CImg.h 2.8 MB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793117941179511796117971179811799118001180111802118031180411805118061180711808118091181011811118121181311814118151181611817118181181911820118211182211823118241182511826118271182811829118301183111832118331183411835118361183711838118391184011841118421184311844118451184611847118481184911850118511185211853118541185511856118571185811859118601186111862118631186411865118661186711868118691187011871118721187311874118751187611877118781187911880118811188211883118841188511886118871188811889118901189111892118931189411895118961189711898118991190011901119021190311904119051190611907119081190911910119111191211913119141191511916119171191811919119201192111922119231192411925119261192711928119291193011931119321193311934119351193611937119381193911940119411194211943119441194511946119471194811949119501195111952119531195411955119561195711958119591196011961119621196311964119651196611967119681196911970119711197211973119741197511976119771197811979119801198111982119831198411985119861198711988119891199011991119921199311994119951199611997119981199912000120011200212003120041200512006120071200812009120101201112012120131201412015120161201712018120191202012021120221202312024120251202612027120281202912030120311203212033120341203512036120371203812039120401204112042120431204412045120461204712048120491205012051120521205312054120551205612057120581205912060120611206212063120641206512066120671206812069120701207112072120731207412075120761207712078120791208012081120821208312084120851208612087120881208912090120911209212093120941209512096120971209812099121001210112102121031210412105121061210712108121091211012111121121211312114121151211612117121181211912120121211212212123121241212512126121271212812129121301213112132121331213412135121361213712138121391214012141121421214312144121451214612147121481214912150121511215212153121541215512156121571215812159121601216112162121631216412165121661216712168121691217012171121721217312174121751217612177121781217912180121811218212183121841218512186121871218812189121901219112192121931219412195121961219712198121991220012201122021220312204122051220612207122081220912210122111221212213122141221512216122171221812219122201222112222122231222412225122261222712228122291223012231122321223312234122351223612237122381223912240122411224212243122441224512246122471224812249122501225112252122531225412255122561225712258122591226012261122621226312264122651226612267122681226912270122711227212273122741227512276122771227812279122801228112282122831228412285122861228712288122891229012291122921229312294122951229612297122981229912300123011230212303123041230512306123071230812309123101231112312123131231412315123161231712318123191232012321123221232312324123251232612327123281232912330123311233212333123341233512336123371233812339123401234112342123431234412345123461234712348123491235012351123521235312354123551235612357123581235912360123611236212363123641236512366123671236812369123701237112372123731237412375123761237712378123791238012381123821238312384123851238612387123881238912390123911239212393123941239512396123971239812399124001240112402124031240412405124061240712408124091241012411124121241312414124151241612417124181241912420124211242212423124241242512426124271242812429124301243112432124331243412435124361243712438124391244012441124421244312444124451244612447124481244912450124511245212453124541245512456124571245812459124601246112462124631246412465124661246712468124691247012471124721247312474124751247612477124781247912480124811248212483124841248512486124871248812489124901249112492124931249412495124961249712498124991250012501125021250312504125051250612507125081250912510125111251212513125141251512516125171251812519125201252112522125231252412525125261252712528125291253012531125321253312534125351253612537125381253912540125411254212543125441254512546125471254812549125501255112552125531255412555125561255712558125591256012561125621256312564125651256612567125681256912570125711257212573125741257512576125771257812579125801258112582125831258412585125861258712588125891259012591125921259312594125951259612597125981259912600126011260212603126041260512606126071260812609126101261112612126131261412615126161261712618126191262012621126221262312624126251262612627126281262912630126311263212633126341263512636126371263812639126401264112642126431264412645126461264712648126491265012651126521265312654126551265612657126581265912660126611266212663126641266512666126671266812669126701267112672126731267412675126761267712678126791268012681126821268312684126851268612687126881268912690126911269212693126941269512696126971269812699127001270112702127031270412705127061270712708127091271012711127121271312714127151271612717127181271912720127211272212723127241272512726127271272812729127301273112732127331273412735127361273712738127391274012741127421274312744127451274612747127481274912750127511275212753127541275512756127571275812759127601276112762127631276412765127661276712768127691277012771127721277312774127751277612777127781277912780127811278212783127841278512786127871278812789127901279112792127931279412795127961279712798127991280012801128021280312804128051280612807128081280912810128111281212813128141281512816128171281812819128201282112822128231282412825128261282712828128291283012831128321283312834128351283612837128381283912840128411284212843128441284512846128471284812849128501285112852128531285412855128561285712858128591286012861128621286312864128651286612867128681286912870128711287212873128741287512876128771287812879128801288112882128831288412885128861288712888128891289012891128921289312894128951289612897128981289912900129011290212903129041290512906129071290812909129101291112912129131291412915129161291712918129191292012921129221292312924129251292612927129281292912930129311293212933129341293512936129371293812939129401294112942129431294412945129461294712948129491295012951129521295312954129551295612957129581295912960129611296212963129641296512966129671296812969129701297112972129731297412975129761297712978129791298012981129821298312984129851298612987129881298912990129911299212993129941299512996129971299812999130001300113002130031300413005130061300713008130091301013011130121301313014130151301613017130181301913020130211302213023130241302513026130271302813029130301303113032130331303413035130361303713038130391304013041130421304313044130451304613047130481304913050130511305213053130541305513056130571305813059130601306113062130631306413065130661306713068130691307013071130721307313074130751307613077130781307913080130811308213083130841308513086130871308813089130901309113092130931309413095130961309713098130991310013101131021310313104131051310613107131081310913110131111311213113131141311513116131171311813119131201312113122131231312413125131261312713128131291313013131131321313313134131351313613137131381313913140131411314213143131441314513146131471314813149131501315113152131531315413155131561315713158131591316013161131621316313164131651316613167131681316913170131711317213173131741317513176131771317813179131801318113182131831318413185131861318713188131891319013191131921319313194131951319613197131981319913200132011320213203132041320513206132071320813209132101321113212132131321413215132161321713218132191322013221132221322313224132251322613227132281322913230132311323213233132341323513236132371323813239132401324113242132431324413245132461324713248132491325013251132521325313254132551325613257132581325913260132611326213263132641326513266132671326813269132701327113272132731327413275132761327713278132791328013281132821328313284132851328613287132881328913290132911329213293132941329513296132971329813299133001330113302133031330413305133061330713308133091331013311133121331313314133151331613317133181331913320133211332213323133241332513326133271332813329133301333113332133331333413335133361333713338133391334013341133421334313344133451334613347133481334913350133511335213353133541335513356133571335813359133601336113362133631336413365133661336713368133691337013371133721337313374133751337613377133781337913380133811338213383133841338513386133871338813389133901339113392133931339413395133961339713398133991340013401134021340313404134051340613407134081340913410134111341213413134141341513416134171341813419134201342113422134231342413425134261342713428134291343013431134321343313434134351343613437134381343913440134411344213443134441344513446134471344813449134501345113452134531345413455134561345713458134591346013461134621346313464134651346613467134681346913470134711347213473134741347513476134771347813479134801348113482134831348413485134861348713488134891349013491134921349313494134951349613497134981349913500135011350213503135041350513506135071350813509135101351113512135131351413515135161351713518135191352013521135221352313524135251352613527135281352913530135311353213533135341353513536135371353813539135401354113542135431354413545135461354713548135491355013551135521355313554135551355613557135581355913560135611356213563135641356513566135671356813569135701357113572135731357413575135761357713578135791358013581135821358313584135851358613587135881358913590135911359213593135941359513596135971359813599136001360113602136031360413605136061360713608136091361013611136121361313614136151361613617136181361913620136211362213623136241362513626136271362813629136301363113632136331363413635136361363713638136391364013641136421364313644136451364613647136481364913650136511365213653136541365513656136571365813659136601366113662136631366413665136661366713668136691367013671136721367313674136751367613677136781367913680136811368213683136841368513686136871368813689136901369113692136931369413695136961369713698136991370013701137021370313704137051370613707137081370913710137111371213713137141371513716137171371813719137201372113722137231372413725137261372713728137291373013731137321373313734137351373613737137381373913740137411374213743137441374513746137471374813749137501375113752137531375413755137561375713758137591376013761137621376313764137651376613767137681376913770137711377213773137741377513776137771377813779137801378113782137831378413785137861378713788137891379013791137921379313794137951379613797137981379913800138011380213803138041380513806138071380813809138101381113812138131381413815138161381713818138191382013821138221382313824138251382613827138281382913830138311383213833138341383513836138371383813839138401384113842138431384413845138461384713848138491385013851138521385313854138551385613857138581385913860138611386213863138641386513866138671386813869138701387113872138731387413875138761387713878138791388013881138821388313884138851388613887138881388913890138911389213893138941389513896138971389813899139001390113902139031390413905139061390713908139091391013911139121391313914139151391613917139181391913920139211392213923139241392513926139271392813929139301393113932139331393413935139361393713938139391394013941139421394313944139451394613947139481394913950139511395213953139541395513956139571395813959139601396113962139631396413965139661396713968139691397013971139721397313974139751397613977139781397913980139811398213983139841398513986139871398813989139901399113992139931399413995139961399713998139991400014001140021400314004140051400614007140081400914010140111401214013140141401514016140171401814019140201402114022140231402414025140261402714028140291403014031140321403314034140351403614037140381403914040140411404214043140441404514046140471404814049140501405114052140531405414055140561405714058140591406014061140621406314064140651406614067140681406914070140711407214073140741407514076140771407814079140801408114082140831408414085140861408714088140891409014091140921409314094140951409614097140981409914100141011410214103141041410514106141071410814109141101411114112141131411414115141161411714118141191412014121141221412314124141251412614127141281412914130141311413214133141341413514136141371413814139141401414114142141431414414145141461414714148141491415014151141521415314154141551415614157141581415914160141611416214163141641416514166141671416814169141701417114172141731417414175141761417714178141791418014181141821418314184141851418614187141881418914190141911419214193141941419514196141971419814199142001420114202142031420414205142061420714208142091421014211142121421314214142151421614217142181421914220142211422214223142241422514226142271422814229142301423114232142331423414235142361423714238142391424014241142421424314244142451424614247142481424914250142511425214253142541425514256142571425814259142601426114262142631426414265142661426714268142691427014271142721427314274142751427614277142781427914280142811428214283142841428514286142871428814289142901429114292142931429414295142961429714298142991430014301143021430314304143051430614307143081430914310143111431214313143141431514316143171431814319143201432114322143231432414325143261432714328143291433014331143321433314334143351433614337143381433914340143411434214343143441434514346143471434814349143501435114352143531435414355143561435714358143591436014361143621436314364143651436614367143681436914370143711437214373143741437514376143771437814379143801438114382143831438414385143861438714388143891439014391143921439314394143951439614397143981439914400144011440214403144041440514406144071440814409144101441114412144131441414415144161441714418144191442014421144221442314424144251442614427144281442914430144311443214433144341443514436144371443814439144401444114442144431444414445144461444714448144491445014451144521445314454144551445614457144581445914460144611446214463144641446514466144671446814469144701447114472144731447414475144761447714478144791448014481144821448314484144851448614487144881448914490144911449214493144941449514496144971449814499145001450114502145031450414505145061450714508145091451014511145121451314514145151451614517145181451914520145211452214523145241452514526145271452814529145301453114532145331453414535145361453714538145391454014541145421454314544145451454614547145481454914550145511455214553145541455514556145571455814559145601456114562145631456414565145661456714568145691457014571145721457314574145751457614577145781457914580145811458214583145841458514586145871458814589145901459114592145931459414595145961459714598145991460014601146021460314604146051460614607146081460914610146111461214613146141461514616146171461814619146201462114622146231462414625146261462714628146291463014631146321463314634146351463614637146381463914640146411464214643146441464514646146471464814649146501465114652146531465414655146561465714658146591466014661146621466314664146651466614667146681466914670146711467214673146741467514676146771467814679146801468114682146831468414685146861468714688146891469014691146921469314694146951469614697146981469914700147011470214703147041470514706147071470814709147101471114712147131471414715147161471714718147191472014721147221472314724147251472614727147281472914730147311473214733147341473514736147371473814739147401474114742147431474414745147461474714748147491475014751147521475314754147551475614757147581475914760147611476214763147641476514766147671476814769147701477114772147731477414775147761477714778147791478014781147821478314784147851478614787147881478914790147911479214793147941479514796147971479814799148001480114802148031480414805148061480714808148091481014811148121481314814148151481614817148181481914820148211482214823148241482514826148271482814829148301483114832148331483414835148361483714838148391484014841148421484314844148451484614847148481484914850148511485214853148541485514856148571485814859148601486114862148631486414865148661486714868148691487014871148721487314874148751487614877148781487914880148811488214883148841488514886148871488814889148901489114892148931489414895148961489714898148991490014901149021490314904149051490614907149081490914910149111491214913149141491514916149171491814919149201492114922149231492414925149261492714928149291493014931149321493314934149351493614937149381493914940149411494214943149441494514946149471494814949149501495114952149531495414955149561495714958149591496014961149621496314964149651496614967149681496914970149711497214973149741497514976149771497814979149801498114982149831498414985149861498714988149891499014991149921499314994149951499614997149981499915000150011500215003150041500515006150071500815009150101501115012150131501415015150161501715018150191502015021150221502315024150251502615027150281502915030150311503215033150341503515036150371503815039150401504115042150431504415045150461504715048150491505015051150521505315054150551505615057150581505915060150611506215063150641506515066150671506815069150701507115072150731507415075150761507715078150791508015081150821508315084150851508615087150881508915090150911509215093150941509515096150971509815099151001510115102151031510415105151061510715108151091511015111151121511315114151151511615117151181511915120151211512215123151241512515126151271512815129151301513115132151331513415135151361513715138151391514015141151421514315144151451514615147151481514915150151511515215153151541515515156151571515815159151601516115162151631516415165151661516715168151691517015171151721517315174151751517615177151781517915180151811518215183151841518515186151871518815189151901519115192151931519415195151961519715198151991520015201152021520315204152051520615207152081520915210152111521215213152141521515216152171521815219152201522115222152231522415225152261522715228152291523015231152321523315234152351523615237152381523915240152411524215243152441524515246152471524815249152501525115252152531525415255152561525715258152591526015261152621526315264152651526615267152681526915270152711527215273152741527515276152771527815279152801528115282152831528415285152861528715288152891529015291152921529315294152951529615297152981529915300153011530215303153041530515306153071530815309153101531115312153131531415315153161531715318153191532015321153221532315324153251532615327153281532915330153311533215333153341533515336153371533815339153401534115342153431534415345153461534715348153491535015351153521535315354153551535615357153581535915360153611536215363153641536515366153671536815369153701537115372153731537415375153761537715378153791538015381153821538315384153851538615387153881538915390153911539215393153941539515396153971539815399154001540115402154031540415405154061540715408154091541015411154121541315414154151541615417154181541915420154211542215423154241542515426154271542815429154301543115432154331543415435154361543715438154391544015441154421544315444154451544615447154481544915450154511545215453154541545515456154571545815459154601546115462154631546415465154661546715468154691547015471154721547315474154751547615477154781547915480154811548215483154841548515486154871548815489154901549115492154931549415495154961549715498154991550015501155021550315504155051550615507155081550915510155111551215513155141551515516155171551815519155201552115522155231552415525155261552715528155291553015531155321553315534155351553615537155381553915540155411554215543155441554515546155471554815549155501555115552155531555415555155561555715558155591556015561155621556315564155651556615567155681556915570155711557215573155741557515576155771557815579155801558115582155831558415585155861558715588155891559015591155921559315594155951559615597155981559915600156011560215603156041560515606156071560815609156101561115612156131561415615156161561715618156191562015621156221562315624156251562615627156281562915630156311563215633156341563515636156371563815639156401564115642156431564415645156461564715648156491565015651156521565315654156551565615657156581565915660156611566215663156641566515666156671566815669156701567115672156731567415675156761567715678156791568015681156821568315684156851568615687156881568915690156911569215693156941569515696156971569815699157001570115702157031570415705157061570715708157091571015711157121571315714157151571615717157181571915720157211572215723157241572515726157271572815729157301573115732157331573415735157361573715738157391574015741157421574315744157451574615747157481574915750157511575215753157541575515756157571575815759157601576115762157631576415765157661576715768157691577015771157721577315774157751577615777157781577915780157811578215783157841578515786157871578815789157901579115792157931579415795157961579715798157991580015801158021580315804158051580615807158081580915810158111581215813158141581515816158171581815819158201582115822158231582415825158261582715828158291583015831158321583315834158351583615837158381583915840158411584215843158441584515846158471584815849158501585115852158531585415855158561585715858158591586015861158621586315864158651586615867158681586915870158711587215873158741587515876158771587815879158801588115882158831588415885158861588715888158891589015891158921589315894158951589615897158981589915900159011590215903159041590515906159071590815909159101591115912159131591415915159161591715918159191592015921159221592315924159251592615927159281592915930159311593215933159341593515936159371593815939159401594115942159431594415945159461594715948159491595015951159521595315954159551595615957159581595915960159611596215963159641596515966159671596815969159701597115972159731597415975159761597715978159791598015981159821598315984159851598615987159881598915990159911599215993159941599515996159971599815999160001600116002160031600416005160061600716008160091601016011160121601316014160151601616017160181601916020160211602216023160241602516026160271602816029160301603116032160331603416035160361603716038160391604016041160421604316044160451604616047160481604916050160511605216053160541605516056160571605816059160601606116062160631606416065160661606716068160691607016071160721607316074160751607616077160781607916080160811608216083160841608516086160871608816089160901609116092160931609416095160961609716098160991610016101161021610316104161051610616107161081610916110161111611216113161141611516116161171611816119161201612116122161231612416125161261612716128161291613016131161321613316134161351613616137161381613916140161411614216143161441614516146161471614816149161501615116152161531615416155161561615716158161591616016161161621616316164161651616616167161681616916170161711617216173161741617516176161771617816179161801618116182161831618416185161861618716188161891619016191161921619316194161951619616197161981619916200162011620216203162041620516206162071620816209162101621116212162131621416215162161621716218162191622016221162221622316224162251622616227162281622916230162311623216233162341623516236162371623816239162401624116242162431624416245162461624716248162491625016251162521625316254162551625616257162581625916260162611626216263162641626516266162671626816269162701627116272162731627416275162761627716278162791628016281162821628316284162851628616287162881628916290162911629216293162941629516296162971629816299163001630116302163031630416305163061630716308163091631016311163121631316314163151631616317163181631916320163211632216323163241632516326163271632816329163301633116332163331633416335163361633716338163391634016341163421634316344163451634616347163481634916350163511635216353163541635516356163571635816359163601636116362163631636416365163661636716368163691637016371163721637316374163751637616377163781637916380163811638216383163841638516386163871638816389163901639116392163931639416395163961639716398163991640016401164021640316404164051640616407164081640916410164111641216413164141641516416164171641816419164201642116422164231642416425164261642716428164291643016431164321643316434164351643616437164381643916440164411644216443164441644516446164471644816449164501645116452164531645416455164561645716458164591646016461164621646316464164651646616467164681646916470164711647216473164741647516476164771647816479164801648116482164831648416485164861648716488164891649016491164921649316494164951649616497164981649916500165011650216503165041650516506165071650816509165101651116512165131651416515165161651716518165191652016521165221652316524165251652616527165281652916530165311653216533165341653516536165371653816539165401654116542165431654416545165461654716548165491655016551165521655316554165551655616557165581655916560165611656216563165641656516566165671656816569165701657116572165731657416575165761657716578165791658016581165821658316584165851658616587165881658916590165911659216593165941659516596165971659816599166001660116602166031660416605166061660716608166091661016611166121661316614166151661616617166181661916620166211662216623166241662516626166271662816629166301663116632166331663416635166361663716638166391664016641166421664316644166451664616647166481664916650166511665216653166541665516656166571665816659166601666116662166631666416665166661666716668166691667016671166721667316674166751667616677166781667916680166811668216683166841668516686166871668816689166901669116692166931669416695166961669716698166991670016701167021670316704167051670616707167081670916710167111671216713167141671516716167171671816719167201672116722167231672416725167261672716728167291673016731167321673316734167351673616737167381673916740167411674216743167441674516746167471674816749167501675116752167531675416755167561675716758167591676016761167621676316764167651676616767167681676916770167711677216773167741677516776167771677816779167801678116782167831678416785167861678716788167891679016791167921679316794167951679616797167981679916800168011680216803168041680516806168071680816809168101681116812168131681416815168161681716818168191682016821168221682316824168251682616827168281682916830168311683216833168341683516836168371683816839168401684116842168431684416845168461684716848168491685016851168521685316854168551685616857168581685916860168611686216863168641686516866168671686816869168701687116872168731687416875168761687716878168791688016881168821688316884168851688616887168881688916890168911689216893168941689516896168971689816899169001690116902169031690416905169061690716908169091691016911169121691316914169151691616917169181691916920169211692216923169241692516926169271692816929169301693116932169331693416935169361693716938169391694016941169421694316944169451694616947169481694916950169511695216953169541695516956169571695816959169601696116962169631696416965169661696716968169691697016971169721697316974169751697616977169781697916980169811698216983169841698516986169871698816989169901699116992169931699416995169961699716998169991700017001170021700317004170051700617007170081700917010170111701217013170141701517016170171701817019170201702117022170231702417025170261702717028170291703017031170321703317034170351703617037170381703917040170411704217043170441704517046170471704817049170501705117052170531705417055170561705717058170591706017061170621706317064170651706617067170681706917070170711707217073170741707517076170771707817079170801708117082170831708417085170861708717088170891709017091170921709317094170951709617097170981709917100171011710217103171041710517106171071710817109171101711117112171131711417115171161711717118171191712017121171221712317124171251712617127171281712917130171311713217133171341713517136171371713817139171401714117142171431714417145171461714717148171491715017151171521715317154171551715617157171581715917160171611716217163171641716517166171671716817169171701717117172171731717417175171761717717178171791718017181171821718317184171851718617187171881718917190171911719217193171941719517196171971719817199172001720117202172031720417205172061720717208172091721017211172121721317214172151721617217172181721917220172211722217223172241722517226172271722817229172301723117232172331723417235172361723717238172391724017241172421724317244172451724617247172481724917250172511725217253172541725517256172571725817259172601726117262172631726417265172661726717268172691727017271172721727317274172751727617277172781727917280172811728217283172841728517286172871728817289172901729117292172931729417295172961729717298172991730017301173021730317304173051730617307173081730917310173111731217313173141731517316173171731817319173201732117322173231732417325173261732717328173291733017331173321733317334173351733617337173381733917340173411734217343173441734517346173471734817349173501735117352173531735417355173561735717358173591736017361173621736317364173651736617367173681736917370173711737217373173741737517376173771737817379173801738117382173831738417385173861738717388173891739017391173921739317394173951739617397173981739917400174011740217403174041740517406174071740817409174101741117412174131741417415174161741717418174191742017421174221742317424174251742617427174281742917430174311743217433174341743517436174371743817439174401744117442174431744417445174461744717448174491745017451174521745317454174551745617457174581745917460174611746217463174641746517466174671746817469174701747117472174731747417475174761747717478174791748017481174821748317484174851748617487174881748917490174911749217493174941749517496174971749817499175001750117502175031750417505175061750717508175091751017511175121751317514175151751617517175181751917520175211752217523175241752517526175271752817529175301753117532175331753417535175361753717538175391754017541175421754317544175451754617547175481754917550175511755217553175541755517556175571755817559175601756117562175631756417565175661756717568175691757017571175721757317574175751757617577175781757917580175811758217583175841758517586175871758817589175901759117592175931759417595175961759717598175991760017601176021760317604176051760617607176081760917610176111761217613176141761517616176171761817619176201762117622176231762417625176261762717628176291763017631176321763317634176351763617637176381763917640176411764217643176441764517646176471764817649176501765117652176531765417655176561765717658176591766017661176621766317664176651766617667176681766917670176711767217673176741767517676176771767817679176801768117682176831768417685176861768717688176891769017691176921769317694176951769617697176981769917700177011770217703177041770517706177071770817709177101771117712177131771417715177161771717718177191772017721177221772317724177251772617727177281772917730177311773217733177341773517736177371773817739177401774117742177431774417745177461774717748177491775017751177521775317754177551775617757177581775917760177611776217763177641776517766177671776817769177701777117772177731777417775177761777717778177791778017781177821778317784177851778617787177881778917790177911779217793177941779517796177971779817799178001780117802178031780417805178061780717808178091781017811178121781317814178151781617817178181781917820178211782217823178241782517826178271782817829178301783117832178331783417835178361783717838178391784017841178421784317844178451784617847178481784917850178511785217853178541785517856178571785817859178601786117862178631786417865178661786717868178691787017871178721787317874178751787617877178781787917880178811788217883178841788517886178871788817889178901789117892178931789417895178961789717898178991790017901179021790317904179051790617907179081790917910179111791217913179141791517916179171791817919179201792117922179231792417925179261792717928179291793017931179321793317934179351793617937179381793917940179411794217943179441794517946179471794817949179501795117952179531795417955179561795717958179591796017961179621796317964179651796617967179681796917970179711797217973179741797517976179771797817979179801798117982179831798417985179861798717988179891799017991179921799317994179951799617997179981799918000180011800218003180041800518006180071800818009180101801118012180131801418015180161801718018180191802018021180221802318024180251802618027180281802918030180311803218033180341803518036180371803818039180401804118042180431804418045180461804718048180491805018051180521805318054180551805618057180581805918060180611806218063180641806518066180671806818069180701807118072180731807418075180761807718078180791808018081180821808318084180851808618087180881808918090180911809218093180941809518096180971809818099181001810118102181031810418105181061810718108181091811018111181121811318114181151811618117181181811918120181211812218123181241812518126181271812818129181301813118132181331813418135181361813718138181391814018141181421814318144181451814618147181481814918150181511815218153181541815518156181571815818159181601816118162181631816418165181661816718168181691817018171181721817318174181751817618177181781817918180181811818218183181841818518186181871818818189181901819118192181931819418195181961819718198181991820018201182021820318204182051820618207182081820918210182111821218213182141821518216182171821818219182201822118222182231822418225182261822718228182291823018231182321823318234182351823618237182381823918240182411824218243182441824518246182471824818249182501825118252182531825418255182561825718258182591826018261182621826318264182651826618267182681826918270182711827218273182741827518276182771827818279182801828118282182831828418285182861828718288182891829018291182921829318294182951829618297182981829918300183011830218303183041830518306183071830818309183101831118312183131831418315183161831718318183191832018321183221832318324183251832618327183281832918330183311833218333183341833518336183371833818339183401834118342183431834418345183461834718348183491835018351183521835318354183551835618357183581835918360183611836218363183641836518366183671836818369183701837118372183731837418375183761837718378183791838018381183821838318384183851838618387183881838918390183911839218393183941839518396183971839818399184001840118402184031840418405184061840718408184091841018411184121841318414184151841618417184181841918420184211842218423184241842518426184271842818429184301843118432184331843418435184361843718438184391844018441184421844318444184451844618447184481844918450184511845218453184541845518456184571845818459184601846118462184631846418465184661846718468184691847018471184721847318474184751847618477184781847918480184811848218483184841848518486184871848818489184901849118492184931849418495184961849718498184991850018501185021850318504185051850618507185081850918510185111851218513185141851518516185171851818519185201852118522185231852418525185261852718528185291853018531185321853318534185351853618537185381853918540185411854218543185441854518546185471854818549185501855118552185531855418555185561855718558185591856018561185621856318564185651856618567185681856918570185711857218573185741857518576185771857818579185801858118582185831858418585185861858718588185891859018591185921859318594185951859618597185981859918600186011860218603186041860518606186071860818609186101861118612186131861418615186161861718618186191862018621186221862318624186251862618627186281862918630186311863218633186341863518636186371863818639186401864118642186431864418645186461864718648186491865018651186521865318654186551865618657186581865918660186611866218663186641866518666186671866818669186701867118672186731867418675186761867718678186791868018681186821868318684186851868618687186881868918690186911869218693186941869518696186971869818699187001870118702187031870418705187061870718708187091871018711187121871318714187151871618717187181871918720187211872218723187241872518726187271872818729187301873118732187331873418735187361873718738187391874018741187421874318744187451874618747187481874918750187511875218753187541875518756187571875818759187601876118762187631876418765187661876718768187691877018771187721877318774187751877618777187781877918780187811878218783187841878518786187871878818789187901879118792187931879418795187961879718798187991880018801188021880318804188051880618807188081880918810188111881218813188141881518816188171881818819188201882118822188231882418825188261882718828188291883018831188321883318834188351883618837188381883918840188411884218843188441884518846188471884818849188501885118852188531885418855188561885718858188591886018861188621886318864188651886618867188681886918870188711887218873188741887518876188771887818879188801888118882188831888418885188861888718888188891889018891188921889318894188951889618897188981889918900189011890218903189041890518906189071890818909189101891118912189131891418915189161891718918189191892018921189221892318924189251892618927189281892918930189311893218933189341893518936189371893818939189401894118942189431894418945189461894718948189491895018951189521895318954189551895618957189581895918960189611896218963189641896518966189671896818969189701897118972189731897418975189761897718978189791898018981189821898318984189851898618987189881898918990189911899218993189941899518996189971899818999190001900119002190031900419005190061900719008190091901019011190121901319014190151901619017190181901919020190211902219023190241902519026190271902819029190301903119032190331903419035190361903719038190391904019041190421904319044190451904619047190481904919050190511905219053190541905519056190571905819059190601906119062190631906419065190661906719068190691907019071190721907319074190751907619077190781907919080190811908219083190841908519086190871908819089190901909119092190931909419095190961909719098190991910019101191021910319104191051910619107191081910919110191111911219113191141911519116191171911819119191201912119122191231912419125191261912719128191291913019131191321913319134191351913619137191381913919140191411914219143191441914519146191471914819149191501915119152191531915419155191561915719158191591916019161191621916319164191651916619167191681916919170191711917219173191741917519176191771917819179191801918119182191831918419185191861918719188191891919019191191921919319194191951919619197191981919919200192011920219203192041920519206192071920819209192101921119212192131921419215192161921719218192191922019221192221922319224192251922619227192281922919230192311923219233192341923519236192371923819239192401924119242192431924419245192461924719248192491925019251192521925319254192551925619257192581925919260192611926219263192641926519266192671926819269192701927119272192731927419275192761927719278192791928019281192821928319284192851928619287192881928919290192911929219293192941929519296192971929819299193001930119302193031930419305193061930719308193091931019311193121931319314193151931619317193181931919320193211932219323193241932519326193271932819329193301933119332193331933419335193361933719338193391934019341193421934319344193451934619347193481934919350193511935219353193541935519356193571935819359193601936119362193631936419365193661936719368193691937019371193721937319374193751937619377193781937919380193811938219383193841938519386193871938819389193901939119392193931939419395193961939719398193991940019401194021940319404194051940619407194081940919410194111941219413194141941519416194171941819419194201942119422194231942419425194261942719428194291943019431194321943319434194351943619437194381943919440194411944219443194441944519446194471944819449194501945119452194531945419455194561945719458194591946019461194621946319464194651946619467194681946919470194711947219473194741947519476194771947819479194801948119482194831948419485194861948719488194891949019491194921949319494194951949619497194981949919500195011950219503195041950519506195071950819509195101951119512195131951419515195161951719518195191952019521195221952319524195251952619527195281952919530195311953219533195341953519536195371953819539195401954119542195431954419545195461954719548195491955019551195521955319554195551955619557195581955919560195611956219563195641956519566195671956819569195701957119572195731957419575195761957719578195791958019581195821958319584195851958619587195881958919590195911959219593195941959519596195971959819599196001960119602196031960419605196061960719608196091961019611196121961319614196151961619617196181961919620196211962219623196241962519626196271962819629196301963119632196331963419635196361963719638196391964019641196421964319644196451964619647196481964919650196511965219653196541965519656196571965819659196601966119662196631966419665196661966719668196691967019671196721967319674196751967619677196781967919680196811968219683196841968519686196871968819689196901969119692196931969419695196961969719698196991970019701197021970319704197051970619707197081970919710197111971219713197141971519716197171971819719197201972119722197231972419725197261972719728197291973019731197321973319734197351973619737197381973919740197411974219743197441974519746197471974819749197501975119752197531975419755197561975719758197591976019761197621976319764197651976619767197681976919770197711977219773197741977519776197771977819779197801978119782197831978419785197861978719788197891979019791197921979319794197951979619797197981979919800198011980219803198041980519806198071980819809198101981119812198131981419815198161981719818198191982019821198221982319824198251982619827198281982919830198311983219833198341983519836198371983819839198401984119842198431984419845198461984719848198491985019851198521985319854198551985619857198581985919860198611986219863198641986519866198671986819869198701987119872198731987419875198761987719878198791988019881198821988319884198851988619887198881988919890198911989219893198941989519896198971989819899199001990119902199031990419905199061990719908199091991019911199121991319914199151991619917199181991919920199211992219923199241992519926199271992819929199301993119932199331993419935199361993719938199391994019941199421994319944199451994619947199481994919950199511995219953199541995519956199571995819959199601996119962199631996419965199661996719968199691997019971199721997319974199751997619977199781997919980199811998219983199841998519986199871998819989199901999119992199931999419995199961999719998199992000020001200022000320004200052000620007200082000920010200112001220013200142001520016200172001820019200202002120022200232002420025200262002720028200292003020031200322003320034200352003620037200382003920040200412004220043200442004520046200472004820049200502005120052200532005420055200562005720058200592006020061200622006320064200652006620067200682006920070200712007220073200742007520076200772007820079200802008120082200832008420085200862008720088200892009020091200922009320094200952009620097200982009920100201012010220103201042010520106201072010820109201102011120112201132011420115201162011720118201192012020121201222012320124201252012620127201282012920130201312013220133201342013520136201372013820139201402014120142201432014420145201462014720148201492015020151201522015320154201552015620157201582015920160201612016220163201642016520166201672016820169201702017120172201732017420175201762017720178201792018020181201822018320184201852018620187201882018920190201912019220193201942019520196201972019820199202002020120202202032020420205202062020720208202092021020211202122021320214202152021620217202182021920220202212022220223202242022520226202272022820229202302023120232202332023420235202362023720238202392024020241202422024320244202452024620247202482024920250202512025220253202542025520256202572025820259202602026120262202632026420265202662026720268202692027020271202722027320274202752027620277202782027920280202812028220283202842028520286202872028820289202902029120292202932029420295202962029720298202992030020301203022030320304203052030620307203082030920310203112031220313203142031520316203172031820319203202032120322203232032420325203262032720328203292033020331203322033320334203352033620337203382033920340203412034220343203442034520346203472034820349203502035120352203532035420355203562035720358203592036020361203622036320364203652036620367203682036920370203712037220373203742037520376203772037820379203802038120382203832038420385203862038720388203892039020391203922039320394203952039620397203982039920400204012040220403204042040520406204072040820409204102041120412204132041420415204162041720418204192042020421204222042320424204252042620427204282042920430204312043220433204342043520436204372043820439204402044120442204432044420445204462044720448204492045020451204522045320454204552045620457204582045920460204612046220463204642046520466204672046820469204702047120472204732047420475204762047720478204792048020481204822048320484204852048620487204882048920490204912049220493204942049520496204972049820499205002050120502205032050420505205062050720508205092051020511205122051320514205152051620517205182051920520205212052220523205242052520526205272052820529205302053120532205332053420535205362053720538205392054020541205422054320544205452054620547205482054920550205512055220553205542055520556205572055820559205602056120562205632056420565205662056720568205692057020571205722057320574205752057620577205782057920580205812058220583205842058520586205872058820589205902059120592205932059420595205962059720598205992060020601206022060320604206052060620607206082060920610206112061220613206142061520616206172061820619206202062120622206232062420625206262062720628206292063020631206322063320634206352063620637206382063920640206412064220643206442064520646206472064820649206502065120652206532065420655206562065720658206592066020661206622066320664206652066620667206682066920670206712067220673206742067520676206772067820679206802068120682206832068420685206862068720688206892069020691206922069320694206952069620697206982069920700207012070220703207042070520706207072070820709207102071120712207132071420715207162071720718207192072020721207222072320724207252072620727207282072920730207312073220733207342073520736207372073820739207402074120742207432074420745207462074720748207492075020751207522075320754207552075620757207582075920760207612076220763207642076520766207672076820769207702077120772207732077420775207762077720778207792078020781207822078320784207852078620787207882078920790207912079220793207942079520796207972079820799208002080120802208032080420805208062080720808208092081020811208122081320814208152081620817208182081920820208212082220823208242082520826208272082820829208302083120832208332083420835208362083720838208392084020841208422084320844208452084620847208482084920850208512085220853208542085520856208572085820859208602086120862208632086420865208662086720868208692087020871208722087320874208752087620877208782087920880208812088220883208842088520886208872088820889208902089120892208932089420895208962089720898208992090020901209022090320904209052090620907209082090920910209112091220913209142091520916209172091820919209202092120922209232092420925209262092720928209292093020931209322093320934209352093620937209382093920940209412094220943209442094520946209472094820949209502095120952209532095420955209562095720958209592096020961209622096320964209652096620967209682096920970209712097220973209742097520976209772097820979209802098120982209832098420985209862098720988209892099020991209922099320994209952099620997209982099921000210012100221003210042100521006210072100821009210102101121012210132101421015210162101721018210192102021021210222102321024210252102621027210282102921030210312103221033210342103521036210372103821039210402104121042210432104421045210462104721048210492105021051210522105321054210552105621057210582105921060210612106221063210642106521066210672106821069210702107121072210732107421075210762107721078210792108021081210822108321084210852108621087210882108921090210912109221093210942109521096210972109821099211002110121102211032110421105211062110721108211092111021111211122111321114211152111621117211182111921120211212112221123211242112521126211272112821129211302113121132211332113421135211362113721138211392114021141211422114321144211452114621147211482114921150211512115221153211542115521156211572115821159211602116121162211632116421165211662116721168211692117021171211722117321174211752117621177211782117921180211812118221183211842118521186211872118821189211902119121192211932119421195211962119721198211992120021201212022120321204212052120621207212082120921210212112121221213212142121521216212172121821219212202122121222212232122421225212262122721228212292123021231212322123321234212352123621237212382123921240212412124221243212442124521246212472124821249212502125121252212532125421255212562125721258212592126021261212622126321264212652126621267212682126921270212712127221273212742127521276212772127821279212802128121282212832128421285212862128721288212892129021291212922129321294212952129621297212982129921300213012130221303213042130521306213072130821309213102131121312213132131421315213162131721318213192132021321213222132321324213252132621327213282132921330213312133221333213342133521336213372133821339213402134121342213432134421345213462134721348213492135021351213522135321354213552135621357213582135921360213612136221363213642136521366213672136821369213702137121372213732137421375213762137721378213792138021381213822138321384213852138621387213882138921390213912139221393213942139521396213972139821399214002140121402214032140421405214062140721408214092141021411214122141321414214152141621417214182141921420214212142221423214242142521426214272142821429214302143121432214332143421435214362143721438214392144021441214422144321444214452144621447214482144921450214512145221453214542145521456214572145821459214602146121462214632146421465214662146721468214692147021471214722147321474214752147621477214782147921480214812148221483214842148521486214872148821489214902149121492214932149421495214962149721498214992150021501215022150321504215052150621507215082150921510215112151221513215142151521516215172151821519215202152121522215232152421525215262152721528215292153021531215322153321534215352153621537215382153921540215412154221543215442154521546215472154821549215502155121552215532155421555215562155721558215592156021561215622156321564215652156621567215682156921570215712157221573215742157521576215772157821579215802158121582215832158421585215862158721588215892159021591215922159321594215952159621597215982159921600216012160221603216042160521606216072160821609216102161121612216132161421615216162161721618216192162021621216222162321624216252162621627216282162921630216312163221633216342163521636216372163821639216402164121642216432164421645216462164721648216492165021651216522165321654216552165621657216582165921660216612166221663216642166521666216672166821669216702167121672216732167421675216762167721678216792168021681216822168321684216852168621687216882168921690216912169221693216942169521696216972169821699217002170121702217032170421705217062170721708217092171021711217122171321714217152171621717217182171921720217212172221723217242172521726217272172821729217302173121732217332173421735217362173721738217392174021741217422174321744217452174621747217482174921750217512175221753217542175521756217572175821759217602176121762217632176421765217662176721768217692177021771217722177321774217752177621777217782177921780217812178221783217842178521786217872178821789217902179121792217932179421795217962179721798217992180021801218022180321804218052180621807218082180921810218112181221813218142181521816218172181821819218202182121822218232182421825218262182721828218292183021831218322183321834218352183621837218382183921840218412184221843218442184521846218472184821849218502185121852218532185421855218562185721858218592186021861218622186321864218652186621867218682186921870218712187221873218742187521876218772187821879218802188121882218832188421885218862188721888218892189021891218922189321894218952189621897218982189921900219012190221903219042190521906219072190821909219102191121912219132191421915219162191721918219192192021921219222192321924219252192621927219282192921930219312193221933219342193521936219372193821939219402194121942219432194421945219462194721948219492195021951219522195321954219552195621957219582195921960219612196221963219642196521966219672196821969219702197121972219732197421975219762197721978219792198021981219822198321984219852198621987219882198921990219912199221993219942199521996219972199821999220002200122002220032200422005220062200722008220092201022011220122201322014220152201622017220182201922020220212202222023220242202522026220272202822029220302203122032220332203422035220362203722038220392204022041220422204322044220452204622047220482204922050220512205222053220542205522056220572205822059220602206122062220632206422065220662206722068220692207022071220722207322074220752207622077220782207922080220812208222083220842208522086220872208822089220902209122092220932209422095220962209722098220992210022101221022210322104221052210622107221082210922110221112211222113221142211522116221172211822119221202212122122221232212422125221262212722128221292213022131221322213322134221352213622137221382213922140221412214222143221442214522146221472214822149221502215122152221532215422155221562215722158221592216022161221622216322164221652216622167221682216922170221712217222173221742217522176221772217822179221802218122182221832218422185221862218722188221892219022191221922219322194221952219622197221982219922200222012220222203222042220522206222072220822209222102221122212222132221422215222162221722218222192222022221222222222322224222252222622227222282222922230222312223222233222342223522236222372223822239222402224122242222432224422245222462224722248222492225022251222522225322254222552225622257222582225922260222612226222263222642226522266222672226822269222702227122272222732227422275222762227722278222792228022281222822228322284222852228622287222882228922290222912229222293222942229522296222972229822299223002230122302223032230422305223062230722308223092231022311223122231322314223152231622317223182231922320223212232222323223242232522326223272232822329223302233122332223332233422335223362233722338223392234022341223422234322344223452234622347223482234922350223512235222353223542235522356223572235822359223602236122362223632236422365223662236722368223692237022371223722237322374223752237622377223782237922380223812238222383223842238522386223872238822389223902239122392223932239422395223962239722398223992240022401224022240322404224052240622407224082240922410224112241222413224142241522416224172241822419224202242122422224232242422425224262242722428224292243022431224322243322434224352243622437224382243922440224412244222443224442244522446224472244822449224502245122452224532245422455224562245722458224592246022461224622246322464224652246622467224682246922470224712247222473224742247522476224772247822479224802248122482224832248422485224862248722488224892249022491224922249322494224952249622497224982249922500225012250222503225042250522506225072250822509225102251122512225132251422515225162251722518225192252022521225222252322524225252252622527225282252922530225312253222533225342253522536225372253822539225402254122542225432254422545225462254722548225492255022551225522255322554225552255622557225582255922560225612256222563225642256522566225672256822569225702257122572225732257422575225762257722578225792258022581225822258322584225852258622587225882258922590225912259222593225942259522596225972259822599226002260122602226032260422605226062260722608226092261022611226122261322614226152261622617226182261922620226212262222623226242262522626226272262822629226302263122632226332263422635226362263722638226392264022641226422264322644226452264622647226482264922650226512265222653226542265522656226572265822659226602266122662226632266422665226662266722668226692267022671226722267322674226752267622677226782267922680226812268222683226842268522686226872268822689226902269122692226932269422695226962269722698226992270022701227022270322704227052270622707227082270922710227112271222713227142271522716227172271822719227202272122722227232272422725227262272722728227292273022731227322273322734227352273622737227382273922740227412274222743227442274522746227472274822749227502275122752227532275422755227562275722758227592276022761227622276322764227652276622767227682276922770227712277222773227742277522776227772277822779227802278122782227832278422785227862278722788227892279022791227922279322794227952279622797227982279922800228012280222803228042280522806228072280822809228102281122812228132281422815228162281722818228192282022821228222282322824228252282622827228282282922830228312283222833228342283522836228372283822839228402284122842228432284422845228462284722848228492285022851228522285322854228552285622857228582285922860228612286222863228642286522866228672286822869228702287122872228732287422875228762287722878228792288022881228822288322884228852288622887228882288922890228912289222893228942289522896228972289822899229002290122902229032290422905229062290722908229092291022911229122291322914229152291622917229182291922920229212292222923229242292522926229272292822929229302293122932229332293422935229362293722938229392294022941229422294322944229452294622947229482294922950229512295222953229542295522956229572295822959229602296122962229632296422965229662296722968229692297022971229722297322974229752297622977229782297922980229812298222983229842298522986229872298822989229902299122992229932299422995229962299722998229992300023001230022300323004230052300623007230082300923010230112301223013230142301523016230172301823019230202302123022230232302423025230262302723028230292303023031230322303323034230352303623037230382303923040230412304223043230442304523046230472304823049230502305123052230532305423055230562305723058230592306023061230622306323064230652306623067230682306923070230712307223073230742307523076230772307823079230802308123082230832308423085230862308723088230892309023091230922309323094230952309623097230982309923100231012310223103231042310523106231072310823109231102311123112231132311423115231162311723118231192312023121231222312323124231252312623127231282312923130231312313223133231342313523136231372313823139231402314123142231432314423145231462314723148231492315023151231522315323154231552315623157231582315923160231612316223163231642316523166231672316823169231702317123172231732317423175231762317723178231792318023181231822318323184231852318623187231882318923190231912319223193231942319523196231972319823199232002320123202232032320423205232062320723208232092321023211232122321323214232152321623217232182321923220232212322223223232242322523226232272322823229232302323123232232332323423235232362323723238232392324023241232422324323244232452324623247232482324923250232512325223253232542325523256232572325823259232602326123262232632326423265232662326723268232692327023271232722327323274232752327623277232782327923280232812328223283232842328523286232872328823289232902329123292232932329423295232962329723298232992330023301233022330323304233052330623307233082330923310233112331223313233142331523316233172331823319233202332123322233232332423325233262332723328233292333023331233322333323334233352333623337233382333923340233412334223343233442334523346233472334823349233502335123352233532335423355233562335723358233592336023361233622336323364233652336623367233682336923370233712337223373233742337523376233772337823379233802338123382233832338423385233862338723388233892339023391233922339323394233952339623397233982339923400234012340223403234042340523406234072340823409234102341123412234132341423415234162341723418234192342023421234222342323424234252342623427234282342923430234312343223433234342343523436234372343823439234402344123442234432344423445234462344723448234492345023451234522345323454234552345623457234582345923460234612346223463234642346523466234672346823469234702347123472234732347423475234762347723478234792348023481234822348323484234852348623487234882348923490234912349223493234942349523496234972349823499235002350123502235032350423505235062350723508235092351023511235122351323514235152351623517235182351923520235212352223523235242352523526235272352823529235302353123532235332353423535235362353723538235392354023541235422354323544235452354623547235482354923550235512355223553235542355523556235572355823559235602356123562235632356423565235662356723568235692357023571235722357323574235752357623577235782357923580235812358223583235842358523586235872358823589235902359123592235932359423595235962359723598235992360023601236022360323604236052360623607236082360923610236112361223613236142361523616236172361823619236202362123622236232362423625236262362723628236292363023631236322363323634236352363623637236382363923640236412364223643236442364523646236472364823649236502365123652236532365423655236562365723658236592366023661236622366323664236652366623667236682366923670236712367223673236742367523676236772367823679236802368123682236832368423685236862368723688236892369023691236922369323694236952369623697236982369923700237012370223703237042370523706237072370823709237102371123712237132371423715237162371723718237192372023721237222372323724237252372623727237282372923730237312373223733237342373523736237372373823739237402374123742237432374423745237462374723748237492375023751237522375323754237552375623757237582375923760237612376223763237642376523766237672376823769237702377123772237732377423775237762377723778237792378023781237822378323784237852378623787237882378923790237912379223793237942379523796237972379823799238002380123802238032380423805238062380723808238092381023811238122381323814238152381623817238182381923820238212382223823238242382523826238272382823829238302383123832238332383423835238362383723838238392384023841238422384323844238452384623847238482384923850238512385223853238542385523856238572385823859238602386123862238632386423865238662386723868238692387023871238722387323874238752387623877238782387923880238812388223883238842388523886238872388823889238902389123892238932389423895238962389723898238992390023901239022390323904239052390623907239082390923910239112391223913239142391523916239172391823919239202392123922239232392423925239262392723928239292393023931239322393323934239352393623937239382393923940239412394223943239442394523946239472394823949239502395123952239532395423955239562395723958239592396023961239622396323964239652396623967239682396923970239712397223973239742397523976239772397823979239802398123982239832398423985239862398723988239892399023991239922399323994239952399623997239982399924000240012400224003240042400524006240072400824009240102401124012240132401424015240162401724018240192402024021240222402324024240252402624027240282402924030240312403224033240342403524036240372403824039240402404124042240432404424045240462404724048240492405024051240522405324054240552405624057240582405924060240612406224063240642406524066240672406824069240702407124072240732407424075240762407724078240792408024081240822408324084240852408624087240882408924090240912409224093240942409524096240972409824099241002410124102241032410424105241062410724108241092411024111241122411324114241152411624117241182411924120241212412224123241242412524126241272412824129241302413124132241332413424135241362413724138241392414024141241422414324144241452414624147241482414924150241512415224153241542415524156241572415824159241602416124162241632416424165241662416724168241692417024171241722417324174241752417624177241782417924180241812418224183241842418524186241872418824189241902419124192241932419424195241962419724198241992420024201242022420324204242052420624207242082420924210242112421224213242142421524216242172421824219242202422124222242232422424225242262422724228242292423024231242322423324234242352423624237242382423924240242412424224243242442424524246242472424824249242502425124252242532425424255242562425724258242592426024261242622426324264242652426624267242682426924270242712427224273242742427524276242772427824279242802428124282242832428424285242862428724288242892429024291242922429324294242952429624297242982429924300243012430224303243042430524306243072430824309243102431124312243132431424315243162431724318243192432024321243222432324324243252432624327243282432924330243312433224333243342433524336243372433824339243402434124342243432434424345243462434724348243492435024351243522435324354243552435624357243582435924360243612436224363243642436524366243672436824369243702437124372243732437424375243762437724378243792438024381243822438324384243852438624387243882438924390243912439224393243942439524396243972439824399244002440124402244032440424405244062440724408244092441024411244122441324414244152441624417244182441924420244212442224423244242442524426244272442824429244302443124432244332443424435244362443724438244392444024441244422444324444244452444624447244482444924450244512445224453244542445524456244572445824459244602446124462244632446424465244662446724468244692447024471244722447324474244752447624477244782447924480244812448224483244842448524486244872448824489244902449124492244932449424495244962449724498244992450024501245022450324504245052450624507245082450924510245112451224513245142451524516245172451824519245202452124522245232452424525245262452724528245292453024531245322453324534245352453624537245382453924540245412454224543245442454524546245472454824549245502455124552245532455424555245562455724558245592456024561245622456324564245652456624567245682456924570245712457224573245742457524576245772457824579245802458124582245832458424585245862458724588245892459024591245922459324594245952459624597245982459924600246012460224603246042460524606246072460824609246102461124612246132461424615246162461724618246192462024621246222462324624246252462624627246282462924630246312463224633246342463524636246372463824639246402464124642246432464424645246462464724648246492465024651246522465324654246552465624657246582465924660246612466224663246642466524666246672466824669246702467124672246732467424675246762467724678246792468024681246822468324684246852468624687246882468924690246912469224693246942469524696246972469824699247002470124702247032470424705247062470724708247092471024711247122471324714247152471624717247182471924720247212472224723247242472524726247272472824729247302473124732247332473424735247362473724738247392474024741247422474324744247452474624747247482474924750247512475224753247542475524756247572475824759247602476124762247632476424765247662476724768247692477024771247722477324774247752477624777247782477924780247812478224783247842478524786247872478824789247902479124792247932479424795247962479724798247992480024801248022480324804248052480624807248082480924810248112481224813248142481524816248172481824819248202482124822248232482424825248262482724828248292483024831248322483324834248352483624837248382483924840248412484224843248442484524846248472484824849248502485124852248532485424855248562485724858248592486024861248622486324864248652486624867248682486924870248712487224873248742487524876248772487824879248802488124882248832488424885248862488724888248892489024891248922489324894248952489624897248982489924900249012490224903249042490524906249072490824909249102491124912249132491424915249162491724918249192492024921249222492324924249252492624927249282492924930249312493224933249342493524936249372493824939249402494124942249432494424945249462494724948249492495024951249522495324954249552495624957249582495924960249612496224963249642496524966249672496824969249702497124972249732497424975249762497724978249792498024981249822498324984249852498624987249882498924990249912499224993249942499524996249972499824999250002500125002250032500425005250062500725008250092501025011250122501325014250152501625017250182501925020250212502225023250242502525026250272502825029250302503125032250332503425035250362503725038250392504025041250422504325044250452504625047250482504925050250512505225053250542505525056250572505825059250602506125062250632506425065250662506725068250692507025071250722507325074250752507625077250782507925080250812508225083250842508525086250872508825089250902509125092250932509425095250962509725098250992510025101251022510325104251052510625107251082510925110251112511225113251142511525116251172511825119251202512125122251232512425125251262512725128251292513025131251322513325134251352513625137251382513925140251412514225143251442514525146251472514825149251502515125152251532515425155251562515725158251592516025161251622516325164251652516625167251682516925170251712517225173251742517525176251772517825179251802518125182251832518425185251862518725188251892519025191251922519325194251952519625197251982519925200252012520225203252042520525206252072520825209252102521125212252132521425215252162521725218252192522025221252222522325224252252522625227252282522925230252312523225233252342523525236252372523825239252402524125242252432524425245252462524725248252492525025251252522525325254252552525625257252582525925260252612526225263252642526525266252672526825269252702527125272252732527425275252762527725278252792528025281252822528325284252852528625287252882528925290252912529225293252942529525296252972529825299253002530125302253032530425305253062530725308253092531025311253122531325314253152531625317253182531925320253212532225323253242532525326253272532825329253302533125332253332533425335253362533725338253392534025341253422534325344253452534625347253482534925350253512535225353253542535525356253572535825359253602536125362253632536425365253662536725368253692537025371253722537325374253752537625377253782537925380253812538225383253842538525386253872538825389253902539125392253932539425395253962539725398253992540025401254022540325404254052540625407254082540925410254112541225413254142541525416254172541825419254202542125422254232542425425254262542725428254292543025431254322543325434254352543625437254382543925440254412544225443254442544525446254472544825449254502545125452254532545425455254562545725458254592546025461254622546325464254652546625467254682546925470254712547225473254742547525476254772547825479254802548125482254832548425485254862548725488254892549025491254922549325494254952549625497254982549925500255012550225503255042550525506255072550825509255102551125512255132551425515255162551725518255192552025521255222552325524255252552625527255282552925530255312553225533255342553525536255372553825539255402554125542255432554425545255462554725548255492555025551255522555325554255552555625557255582555925560255612556225563255642556525566255672556825569255702557125572255732557425575255762557725578255792558025581255822558325584255852558625587255882558925590255912559225593255942559525596255972559825599256002560125602256032560425605256062560725608256092561025611256122561325614256152561625617256182561925620256212562225623256242562525626256272562825629256302563125632256332563425635256362563725638256392564025641256422564325644256452564625647256482564925650256512565225653256542565525656256572565825659256602566125662256632566425665256662566725668256692567025671256722567325674256752567625677256782567925680256812568225683256842568525686256872568825689256902569125692256932569425695256962569725698256992570025701257022570325704257052570625707257082570925710257112571225713257142571525716257172571825719257202572125722257232572425725257262572725728257292573025731257322573325734257352573625737257382573925740257412574225743257442574525746257472574825749257502575125752257532575425755257562575725758257592576025761257622576325764257652576625767257682576925770257712577225773257742577525776257772577825779257802578125782257832578425785257862578725788257892579025791257922579325794257952579625797257982579925800258012580225803258042580525806258072580825809258102581125812258132581425815258162581725818258192582025821258222582325824258252582625827258282582925830258312583225833258342583525836258372583825839258402584125842258432584425845258462584725848258492585025851258522585325854258552585625857258582585925860258612586225863258642586525866258672586825869258702587125872258732587425875258762587725878258792588025881258822588325884258852588625887258882588925890258912589225893258942589525896258972589825899259002590125902259032590425905259062590725908259092591025911259122591325914259152591625917259182591925920259212592225923259242592525926259272592825929259302593125932259332593425935259362593725938259392594025941259422594325944259452594625947259482594925950259512595225953259542595525956259572595825959259602596125962259632596425965259662596725968259692597025971259722597325974259752597625977259782597925980259812598225983259842598525986259872598825989259902599125992259932599425995259962599725998259992600026001260022600326004260052600626007260082600926010260112601226013260142601526016260172601826019260202602126022260232602426025260262602726028260292603026031260322603326034260352603626037260382603926040260412604226043260442604526046260472604826049260502605126052260532605426055260562605726058260592606026061260622606326064260652606626067260682606926070260712607226073260742607526076260772607826079260802608126082260832608426085260862608726088260892609026091260922609326094260952609626097260982609926100261012610226103261042610526106261072610826109261102611126112261132611426115261162611726118261192612026121261222612326124261252612626127261282612926130261312613226133261342613526136261372613826139261402614126142261432614426145261462614726148261492615026151261522615326154261552615626157261582615926160261612616226163261642616526166261672616826169261702617126172261732617426175261762617726178261792618026181261822618326184261852618626187261882618926190261912619226193261942619526196261972619826199262002620126202262032620426205262062620726208262092621026211262122621326214262152621626217262182621926220262212622226223262242622526226262272622826229262302623126232262332623426235262362623726238262392624026241262422624326244262452624626247262482624926250262512625226253262542625526256262572625826259262602626126262262632626426265262662626726268262692627026271262722627326274262752627626277262782627926280262812628226283262842628526286262872628826289262902629126292262932629426295262962629726298262992630026301263022630326304263052630626307263082630926310263112631226313263142631526316263172631826319263202632126322263232632426325263262632726328263292633026331263322633326334263352633626337263382633926340263412634226343263442634526346263472634826349263502635126352263532635426355263562635726358263592636026361263622636326364263652636626367263682636926370263712637226373263742637526376263772637826379263802638126382263832638426385263862638726388263892639026391263922639326394263952639626397263982639926400264012640226403264042640526406264072640826409264102641126412264132641426415264162641726418264192642026421264222642326424264252642626427264282642926430264312643226433264342643526436264372643826439264402644126442264432644426445264462644726448264492645026451264522645326454264552645626457264582645926460264612646226463264642646526466264672646826469264702647126472264732647426475264762647726478264792648026481264822648326484264852648626487264882648926490264912649226493264942649526496264972649826499265002650126502265032650426505265062650726508265092651026511265122651326514265152651626517265182651926520265212652226523265242652526526265272652826529265302653126532265332653426535265362653726538265392654026541265422654326544265452654626547265482654926550265512655226553265542655526556265572655826559265602656126562265632656426565265662656726568265692657026571265722657326574265752657626577265782657926580265812658226583265842658526586265872658826589265902659126592265932659426595265962659726598265992660026601266022660326604266052660626607266082660926610266112661226613266142661526616266172661826619266202662126622266232662426625266262662726628266292663026631266322663326634266352663626637266382663926640266412664226643266442664526646266472664826649266502665126652266532665426655266562665726658266592666026661266622666326664266652666626667266682666926670266712667226673266742667526676266772667826679266802668126682266832668426685266862668726688266892669026691266922669326694266952669626697266982669926700267012670226703267042670526706267072670826709267102671126712267132671426715267162671726718267192672026721267222672326724267252672626727267282672926730267312673226733267342673526736267372673826739267402674126742267432674426745267462674726748267492675026751267522675326754267552675626757267582675926760267612676226763267642676526766267672676826769267702677126772267732677426775267762677726778267792678026781267822678326784267852678626787267882678926790267912679226793267942679526796267972679826799268002680126802268032680426805268062680726808268092681026811268122681326814268152681626817268182681926820268212682226823268242682526826268272682826829268302683126832268332683426835268362683726838268392684026841268422684326844268452684626847268482684926850268512685226853268542685526856268572685826859268602686126862268632686426865268662686726868268692687026871268722687326874268752687626877268782687926880268812688226883268842688526886268872688826889268902689126892268932689426895268962689726898268992690026901269022690326904269052690626907269082690926910269112691226913269142691526916269172691826919269202692126922269232692426925269262692726928269292693026931269322693326934269352693626937269382693926940269412694226943269442694526946269472694826949269502695126952269532695426955269562695726958269592696026961269622696326964269652696626967269682696926970269712697226973269742697526976269772697826979269802698126982269832698426985269862698726988269892699026991269922699326994269952699626997269982699927000270012700227003270042700527006270072700827009270102701127012270132701427015270162701727018270192702027021270222702327024270252702627027270282702927030270312703227033270342703527036270372703827039270402704127042270432704427045270462704727048270492705027051270522705327054270552705627057270582705927060270612706227063270642706527066270672706827069270702707127072270732707427075270762707727078270792708027081270822708327084270852708627087270882708927090270912709227093270942709527096270972709827099271002710127102271032710427105271062710727108271092711027111271122711327114271152711627117271182711927120271212712227123271242712527126271272712827129271302713127132271332713427135271362713727138271392714027141271422714327144271452714627147271482714927150271512715227153271542715527156271572715827159271602716127162271632716427165271662716727168271692717027171271722717327174271752717627177271782717927180271812718227183271842718527186271872718827189271902719127192271932719427195271962719727198271992720027201272022720327204272052720627207272082720927210272112721227213272142721527216272172721827219272202722127222272232722427225272262722727228272292723027231272322723327234272352723627237272382723927240272412724227243272442724527246272472724827249272502725127252272532725427255272562725727258272592726027261272622726327264272652726627267272682726927270272712727227273272742727527276272772727827279272802728127282272832728427285272862728727288272892729027291272922729327294272952729627297272982729927300273012730227303273042730527306273072730827309273102731127312273132731427315273162731727318273192732027321273222732327324273252732627327273282732927330273312733227333273342733527336273372733827339273402734127342273432734427345273462734727348273492735027351273522735327354273552735627357273582735927360273612736227363273642736527366273672736827369273702737127372273732737427375273762737727378273792738027381273822738327384273852738627387273882738927390273912739227393273942739527396273972739827399274002740127402274032740427405274062740727408274092741027411274122741327414274152741627417274182741927420274212742227423274242742527426274272742827429274302743127432274332743427435274362743727438274392744027441274422744327444274452744627447274482744927450274512745227453274542745527456274572745827459274602746127462274632746427465274662746727468274692747027471274722747327474274752747627477274782747927480274812748227483274842748527486274872748827489274902749127492274932749427495274962749727498274992750027501275022750327504275052750627507275082750927510275112751227513275142751527516275172751827519275202752127522275232752427525275262752727528275292753027531275322753327534275352753627537275382753927540275412754227543275442754527546275472754827549275502755127552275532755427555275562755727558275592756027561275622756327564275652756627567275682756927570275712757227573275742757527576275772757827579275802758127582275832758427585275862758727588275892759027591275922759327594275952759627597275982759927600276012760227603276042760527606276072760827609276102761127612276132761427615276162761727618276192762027621276222762327624276252762627627276282762927630276312763227633276342763527636276372763827639276402764127642276432764427645276462764727648276492765027651276522765327654276552765627657276582765927660276612766227663276642766527666276672766827669276702767127672276732767427675276762767727678276792768027681276822768327684276852768627687276882768927690276912769227693276942769527696276972769827699277002770127702277032770427705277062770727708277092771027711277122771327714277152771627717277182771927720277212772227723277242772527726277272772827729277302773127732277332773427735277362773727738277392774027741277422774327744277452774627747277482774927750277512775227753277542775527756277572775827759277602776127762277632776427765277662776727768277692777027771277722777327774277752777627777277782777927780277812778227783277842778527786277872778827789277902779127792277932779427795277962779727798277992780027801278022780327804278052780627807278082780927810278112781227813278142781527816278172781827819278202782127822278232782427825278262782727828278292783027831278322783327834278352783627837278382783927840278412784227843278442784527846278472784827849278502785127852278532785427855278562785727858278592786027861278622786327864278652786627867278682786927870278712787227873278742787527876278772787827879278802788127882278832788427885278862788727888278892789027891278922789327894278952789627897278982789927900279012790227903279042790527906279072790827909279102791127912279132791427915279162791727918279192792027921279222792327924279252792627927279282792927930279312793227933279342793527936279372793827939279402794127942279432794427945279462794727948279492795027951279522795327954279552795627957279582795927960279612796227963279642796527966279672796827969279702797127972279732797427975279762797727978279792798027981279822798327984279852798627987279882798927990279912799227993279942799527996279972799827999280002800128002280032800428005280062800728008280092801028011280122801328014280152801628017280182801928020280212802228023280242802528026280272802828029280302803128032280332803428035280362803728038280392804028041280422804328044280452804628047280482804928050280512805228053280542805528056280572805828059280602806128062280632806428065280662806728068280692807028071280722807328074280752807628077280782807928080280812808228083280842808528086280872808828089280902809128092280932809428095280962809728098280992810028101281022810328104281052810628107281082810928110281112811228113281142811528116281172811828119281202812128122281232812428125281262812728128281292813028131281322813328134281352813628137281382813928140281412814228143281442814528146281472814828149281502815128152281532815428155281562815728158281592816028161281622816328164281652816628167281682816928170281712817228173281742817528176281772817828179281802818128182281832818428185281862818728188281892819028191281922819328194281952819628197281982819928200282012820228203282042820528206282072820828209282102821128212282132821428215282162821728218282192822028221282222822328224282252822628227282282822928230282312823228233282342823528236282372823828239282402824128242282432824428245282462824728248282492825028251282522825328254282552825628257282582825928260282612826228263282642826528266282672826828269282702827128272282732827428275282762827728278282792828028281282822828328284282852828628287282882828928290282912829228293282942829528296282972829828299283002830128302283032830428305283062830728308283092831028311283122831328314283152831628317283182831928320283212832228323283242832528326283272832828329283302833128332283332833428335283362833728338283392834028341283422834328344283452834628347283482834928350283512835228353283542835528356283572835828359283602836128362283632836428365283662836728368283692837028371283722837328374283752837628377283782837928380283812838228383283842838528386283872838828389283902839128392283932839428395283962839728398283992840028401284022840328404284052840628407284082840928410284112841228413284142841528416284172841828419284202842128422284232842428425284262842728428284292843028431284322843328434284352843628437284382843928440284412844228443284442844528446284472844828449284502845128452284532845428455284562845728458284592846028461284622846328464284652846628467284682846928470284712847228473284742847528476284772847828479284802848128482284832848428485284862848728488284892849028491284922849328494284952849628497284982849928500285012850228503285042850528506285072850828509285102851128512285132851428515285162851728518285192852028521285222852328524285252852628527285282852928530285312853228533285342853528536285372853828539285402854128542285432854428545285462854728548285492855028551285522855328554285552855628557285582855928560285612856228563285642856528566285672856828569285702857128572285732857428575285762857728578285792858028581285822858328584285852858628587285882858928590285912859228593285942859528596285972859828599286002860128602286032860428605286062860728608286092861028611286122861328614286152861628617286182861928620286212862228623286242862528626286272862828629286302863128632286332863428635286362863728638286392864028641286422864328644286452864628647286482864928650286512865228653286542865528656286572865828659286602866128662286632866428665286662866728668286692867028671286722867328674286752867628677286782867928680286812868228683286842868528686286872868828689286902869128692286932869428695286962869728698286992870028701287022870328704287052870628707287082870928710287112871228713287142871528716287172871828719287202872128722287232872428725287262872728728287292873028731287322873328734287352873628737287382873928740287412874228743287442874528746287472874828749287502875128752287532875428755287562875728758287592876028761287622876328764287652876628767287682876928770287712877228773287742877528776287772877828779287802878128782287832878428785287862878728788287892879028791287922879328794287952879628797287982879928800288012880228803288042880528806288072880828809288102881128812288132881428815288162881728818288192882028821288222882328824288252882628827288282882928830288312883228833288342883528836288372883828839288402884128842288432884428845288462884728848288492885028851288522885328854288552885628857288582885928860288612886228863288642886528866288672886828869288702887128872288732887428875288762887728878288792888028881288822888328884288852888628887288882888928890288912889228893288942889528896288972889828899289002890128902289032890428905289062890728908289092891028911289122891328914289152891628917289182891928920289212892228923289242892528926289272892828929289302893128932289332893428935289362893728938289392894028941289422894328944289452894628947289482894928950289512895228953289542895528956289572895828959289602896128962289632896428965289662896728968289692897028971289722897328974289752897628977289782897928980289812898228983289842898528986289872898828989289902899128992289932899428995289962899728998289992900029001290022900329004290052900629007290082900929010290112901229013290142901529016290172901829019290202902129022290232902429025290262902729028290292903029031290322903329034290352903629037290382903929040290412904229043290442904529046290472904829049290502905129052290532905429055290562905729058290592906029061290622906329064290652906629067290682906929070290712907229073290742907529076290772907829079290802908129082290832908429085290862908729088290892909029091290922909329094290952909629097290982909929100291012910229103291042910529106291072910829109291102911129112291132911429115291162911729118291192912029121291222912329124291252912629127291282912929130291312913229133291342913529136291372913829139291402914129142291432914429145291462914729148291492915029151291522915329154291552915629157291582915929160291612916229163291642916529166291672916829169291702917129172291732917429175291762917729178291792918029181291822918329184291852918629187291882918929190291912919229193291942919529196291972919829199292002920129202292032920429205292062920729208292092921029211292122921329214292152921629217292182921929220292212922229223292242922529226292272922829229292302923129232292332923429235292362923729238292392924029241292422924329244292452924629247292482924929250292512925229253292542925529256292572925829259292602926129262292632926429265292662926729268292692927029271292722927329274292752927629277292782927929280292812928229283292842928529286292872928829289292902929129292292932929429295292962929729298292992930029301293022930329304293052930629307293082930929310293112931229313293142931529316293172931829319293202932129322293232932429325293262932729328293292933029331293322933329334293352933629337293382933929340293412934229343293442934529346293472934829349293502935129352293532935429355293562935729358293592936029361293622936329364293652936629367293682936929370293712937229373293742937529376293772937829379293802938129382293832938429385293862938729388293892939029391293922939329394293952939629397293982939929400294012940229403294042940529406294072940829409294102941129412294132941429415294162941729418294192942029421294222942329424294252942629427294282942929430294312943229433294342943529436294372943829439294402944129442294432944429445294462944729448294492945029451294522945329454294552945629457294582945929460294612946229463294642946529466294672946829469294702947129472294732947429475294762947729478294792948029481294822948329484294852948629487294882948929490294912949229493294942949529496294972949829499295002950129502295032950429505295062950729508295092951029511295122951329514295152951629517295182951929520295212952229523295242952529526295272952829529295302953129532295332953429535295362953729538295392954029541295422954329544295452954629547295482954929550295512955229553295542955529556295572955829559295602956129562295632956429565295662956729568295692957029571295722957329574295752957629577295782957929580295812958229583295842958529586295872958829589295902959129592295932959429595295962959729598295992960029601296022960329604296052960629607296082960929610296112961229613296142961529616296172961829619296202962129622296232962429625296262962729628296292963029631296322963329634296352963629637296382963929640296412964229643296442964529646296472964829649296502965129652296532965429655296562965729658296592966029661296622966329664296652966629667296682966929670296712967229673296742967529676296772967829679296802968129682296832968429685296862968729688296892969029691296922969329694296952969629697296982969929700297012970229703297042970529706297072970829709297102971129712297132971429715297162971729718297192972029721297222972329724297252972629727297282972929730297312973229733297342973529736297372973829739297402974129742297432974429745297462974729748297492975029751297522975329754297552975629757297582975929760297612976229763297642976529766297672976829769297702977129772297732977429775297762977729778297792978029781297822978329784297852978629787297882978929790297912979229793297942979529796297972979829799298002980129802298032980429805298062980729808298092981029811298122981329814298152981629817298182981929820298212982229823298242982529826298272982829829298302983129832298332983429835298362983729838298392984029841298422984329844298452984629847298482984929850298512985229853298542985529856298572985829859298602986129862298632986429865298662986729868298692987029871298722987329874298752987629877298782987929880298812988229883298842988529886298872988829889298902989129892298932989429895298962989729898298992990029901299022990329904299052990629907299082990929910299112991229913299142991529916299172991829919299202992129922299232992429925299262992729928299292993029931299322993329934299352993629937299382993929940299412994229943299442994529946299472994829949299502995129952299532995429955299562995729958299592996029961299622996329964299652996629967299682996929970299712997229973299742997529976299772997829979299802998129982299832998429985299862998729988299892999029991299922999329994299952999629997299982999930000300013000230003300043000530006300073000830009300103001130012300133001430015300163001730018300193002030021300223002330024300253002630027300283002930030300313003230033300343003530036300373003830039300403004130042300433004430045300463004730048300493005030051300523005330054300553005630057300583005930060300613006230063300643006530066300673006830069300703007130072300733007430075300763007730078300793008030081300823008330084300853008630087300883008930090300913009230093300943009530096300973009830099301003010130102301033010430105301063010730108301093011030111301123011330114301153011630117301183011930120301213012230123301243012530126301273012830129301303013130132301333013430135301363013730138301393014030141301423014330144301453014630147301483014930150301513015230153301543015530156301573015830159301603016130162301633016430165301663016730168301693017030171301723017330174301753017630177301783017930180301813018230183301843018530186301873018830189301903019130192301933019430195301963019730198301993020030201302023020330204302053020630207302083020930210302113021230213302143021530216302173021830219302203022130222302233022430225302263022730228302293023030231302323023330234302353023630237302383023930240302413024230243302443024530246302473024830249302503025130252302533025430255302563025730258302593026030261302623026330264302653026630267302683026930270302713027230273302743027530276302773027830279302803028130282302833028430285302863028730288302893029030291302923029330294302953029630297302983029930300303013030230303303043030530306303073030830309303103031130312303133031430315303163031730318303193032030321303223032330324303253032630327303283032930330303313033230333303343033530336303373033830339303403034130342303433034430345303463034730348303493035030351303523035330354303553035630357303583035930360303613036230363303643036530366303673036830369303703037130372303733037430375303763037730378303793038030381303823038330384303853038630387303883038930390303913039230393303943039530396303973039830399304003040130402304033040430405304063040730408304093041030411304123041330414304153041630417304183041930420304213042230423304243042530426304273042830429304303043130432304333043430435304363043730438304393044030441304423044330444304453044630447304483044930450304513045230453304543045530456304573045830459304603046130462304633046430465304663046730468304693047030471304723047330474304753047630477304783047930480304813048230483304843048530486304873048830489304903049130492304933049430495304963049730498304993050030501305023050330504305053050630507305083050930510305113051230513305143051530516305173051830519305203052130522305233052430525305263052730528305293053030531305323053330534305353053630537305383053930540305413054230543305443054530546305473054830549305503055130552305533055430555305563055730558305593056030561305623056330564305653056630567305683056930570305713057230573305743057530576305773057830579305803058130582305833058430585305863058730588305893059030591305923059330594305953059630597305983059930600306013060230603306043060530606306073060830609306103061130612306133061430615306163061730618306193062030621306223062330624306253062630627306283062930630306313063230633306343063530636306373063830639306403064130642306433064430645306463064730648306493065030651306523065330654306553065630657306583065930660306613066230663306643066530666306673066830669306703067130672306733067430675306763067730678306793068030681306823068330684306853068630687306883068930690306913069230693306943069530696306973069830699307003070130702307033070430705307063070730708307093071030711307123071330714307153071630717307183071930720307213072230723307243072530726307273072830729307303073130732307333073430735307363073730738307393074030741307423074330744307453074630747307483074930750307513075230753307543075530756307573075830759307603076130762307633076430765307663076730768307693077030771307723077330774307753077630777307783077930780307813078230783307843078530786307873078830789307903079130792307933079430795307963079730798307993080030801308023080330804308053080630807308083080930810308113081230813308143081530816308173081830819308203082130822308233082430825308263082730828308293083030831308323083330834308353083630837308383083930840308413084230843308443084530846308473084830849308503085130852308533085430855308563085730858308593086030861308623086330864308653086630867308683086930870308713087230873308743087530876308773087830879308803088130882308833088430885308863088730888308893089030891308923089330894308953089630897308983089930900309013090230903309043090530906309073090830909309103091130912309133091430915309163091730918309193092030921309223092330924309253092630927309283092930930309313093230933309343093530936309373093830939309403094130942309433094430945309463094730948309493095030951309523095330954309553095630957309583095930960309613096230963309643096530966309673096830969309703097130972309733097430975309763097730978309793098030981309823098330984309853098630987309883098930990309913099230993309943099530996309973099830999310003100131002310033100431005310063100731008310093101031011310123101331014310153101631017310183101931020310213102231023310243102531026310273102831029310303103131032310333103431035310363103731038310393104031041310423104331044310453104631047310483104931050310513105231053310543105531056310573105831059310603106131062310633106431065310663106731068310693107031071310723107331074310753107631077310783107931080310813108231083310843108531086310873108831089310903109131092310933109431095310963109731098310993110031101311023110331104311053110631107311083110931110311113111231113311143111531116311173111831119311203112131122311233112431125311263112731128311293113031131311323113331134311353113631137311383113931140311413114231143311443114531146311473114831149311503115131152311533115431155311563115731158311593116031161311623116331164311653116631167311683116931170311713117231173311743117531176311773117831179311803118131182311833118431185311863118731188311893119031191311923119331194311953119631197311983119931200312013120231203312043120531206312073120831209312103121131212312133121431215312163121731218312193122031221312223122331224312253122631227312283122931230312313123231233312343123531236312373123831239312403124131242312433124431245312463124731248312493125031251312523125331254312553125631257312583125931260312613126231263312643126531266312673126831269312703127131272312733127431275312763127731278312793128031281312823128331284312853128631287312883128931290312913129231293312943129531296312973129831299313003130131302313033130431305313063130731308313093131031311313123131331314313153131631317313183131931320313213132231323313243132531326313273132831329313303133131332313333133431335313363133731338313393134031341313423134331344313453134631347313483134931350313513135231353313543135531356313573135831359313603136131362313633136431365313663136731368313693137031371313723137331374313753137631377313783137931380313813138231383313843138531386313873138831389313903139131392313933139431395313963139731398313993140031401314023140331404314053140631407314083140931410314113141231413314143141531416314173141831419314203142131422314233142431425314263142731428314293143031431314323143331434314353143631437314383143931440314413144231443314443144531446314473144831449314503145131452314533145431455314563145731458314593146031461314623146331464314653146631467314683146931470314713147231473314743147531476314773147831479314803148131482314833148431485314863148731488314893149031491314923149331494314953149631497314983149931500315013150231503315043150531506315073150831509315103151131512315133151431515315163151731518315193152031521315223152331524315253152631527315283152931530315313153231533315343153531536315373153831539315403154131542315433154431545315463154731548315493155031551315523155331554315553155631557315583155931560315613156231563315643156531566315673156831569315703157131572315733157431575315763157731578315793158031581315823158331584315853158631587315883158931590315913159231593315943159531596315973159831599316003160131602316033160431605316063160731608316093161031611316123161331614316153161631617316183161931620316213162231623316243162531626316273162831629316303163131632316333163431635316363163731638316393164031641316423164331644316453164631647316483164931650316513165231653316543165531656316573165831659316603166131662316633166431665316663166731668316693167031671316723167331674316753167631677316783167931680316813168231683316843168531686316873168831689316903169131692316933169431695316963169731698316993170031701317023170331704317053170631707317083170931710317113171231713317143171531716317173171831719317203172131722317233172431725317263172731728317293173031731317323173331734317353173631737317383173931740317413174231743317443174531746317473174831749317503175131752317533175431755317563175731758317593176031761317623176331764317653176631767317683176931770317713177231773317743177531776317773177831779317803178131782317833178431785317863178731788317893179031791317923179331794317953179631797317983179931800318013180231803318043180531806318073180831809318103181131812318133181431815318163181731818318193182031821318223182331824318253182631827318283182931830318313183231833318343183531836318373183831839318403184131842318433184431845318463184731848318493185031851318523185331854318553185631857318583185931860318613186231863318643186531866318673186831869318703187131872318733187431875318763187731878318793188031881318823188331884318853188631887318883188931890318913189231893318943189531896318973189831899319003190131902319033190431905319063190731908319093191031911319123191331914319153191631917319183191931920319213192231923319243192531926319273192831929319303193131932319333193431935319363193731938319393194031941319423194331944319453194631947319483194931950319513195231953319543195531956319573195831959319603196131962319633196431965319663196731968319693197031971319723197331974319753197631977319783197931980319813198231983319843198531986319873198831989319903199131992319933199431995319963199731998319993200032001320023200332004320053200632007320083200932010320113201232013320143201532016320173201832019320203202132022320233202432025320263202732028320293203032031320323203332034320353203632037320383203932040320413204232043320443204532046320473204832049320503205132052320533205432055320563205732058320593206032061320623206332064320653206632067320683206932070320713207232073320743207532076320773207832079320803208132082320833208432085320863208732088320893209032091320923209332094320953209632097320983209932100321013210232103321043210532106321073210832109321103211132112321133211432115321163211732118321193212032121321223212332124321253212632127321283212932130321313213232133321343213532136321373213832139321403214132142321433214432145321463214732148321493215032151321523215332154321553215632157321583215932160321613216232163321643216532166321673216832169321703217132172321733217432175321763217732178321793218032181321823218332184321853218632187321883218932190321913219232193321943219532196321973219832199322003220132202322033220432205322063220732208322093221032211322123221332214322153221632217322183221932220322213222232223322243222532226322273222832229322303223132232322333223432235322363223732238322393224032241322423224332244322453224632247322483224932250322513225232253322543225532256322573225832259322603226132262322633226432265322663226732268322693227032271322723227332274322753227632277322783227932280322813228232283322843228532286322873228832289322903229132292322933229432295322963229732298322993230032301323023230332304323053230632307323083230932310323113231232313323143231532316323173231832319323203232132322323233232432325323263232732328323293233032331323323233332334323353233632337323383233932340323413234232343323443234532346323473234832349323503235132352323533235432355323563235732358323593236032361323623236332364323653236632367323683236932370323713237232373323743237532376323773237832379323803238132382323833238432385323863238732388323893239032391323923239332394323953239632397323983239932400324013240232403324043240532406324073240832409324103241132412324133241432415324163241732418324193242032421324223242332424324253242632427324283242932430324313243232433324343243532436324373243832439324403244132442324433244432445324463244732448324493245032451324523245332454324553245632457324583245932460324613246232463324643246532466324673246832469324703247132472324733247432475324763247732478324793248032481324823248332484324853248632487324883248932490324913249232493324943249532496324973249832499325003250132502325033250432505325063250732508325093251032511325123251332514325153251632517325183251932520325213252232523325243252532526325273252832529325303253132532325333253432535325363253732538325393254032541325423254332544325453254632547325483254932550325513255232553325543255532556325573255832559325603256132562325633256432565325663256732568325693257032571325723257332574325753257632577325783257932580325813258232583325843258532586325873258832589325903259132592325933259432595325963259732598325993260032601326023260332604326053260632607326083260932610326113261232613326143261532616326173261832619326203262132622326233262432625326263262732628326293263032631326323263332634326353263632637326383263932640326413264232643326443264532646326473264832649326503265132652326533265432655326563265732658326593266032661326623266332664326653266632667326683266932670326713267232673326743267532676326773267832679326803268132682326833268432685326863268732688326893269032691326923269332694326953269632697326983269932700327013270232703327043270532706327073270832709327103271132712327133271432715327163271732718327193272032721327223272332724327253272632727327283272932730327313273232733327343273532736327373273832739327403274132742327433274432745327463274732748327493275032751327523275332754327553275632757327583275932760327613276232763327643276532766327673276832769327703277132772327733277432775327763277732778327793278032781327823278332784327853278632787327883278932790327913279232793327943279532796327973279832799328003280132802328033280432805328063280732808328093281032811328123281332814328153281632817328183281932820328213282232823328243282532826328273282832829328303283132832328333283432835328363283732838328393284032841328423284332844328453284632847328483284932850328513285232853328543285532856328573285832859328603286132862328633286432865328663286732868328693287032871328723287332874328753287632877328783287932880328813288232883328843288532886328873288832889328903289132892328933289432895328963289732898328993290032901329023290332904329053290632907329083290932910329113291232913329143291532916329173291832919329203292132922329233292432925329263292732928329293293032931329323293332934329353293632937329383293932940329413294232943329443294532946329473294832949329503295132952329533295432955329563295732958329593296032961329623296332964329653296632967329683296932970329713297232973329743297532976329773297832979329803298132982329833298432985329863298732988329893299032991329923299332994329953299632997329983299933000330013300233003330043300533006330073300833009330103301133012330133301433015330163301733018330193302033021330223302333024330253302633027330283302933030330313303233033330343303533036330373303833039330403304133042330433304433045330463304733048330493305033051330523305333054330553305633057330583305933060330613306233063330643306533066330673306833069330703307133072330733307433075330763307733078330793308033081330823308333084330853308633087330883308933090330913309233093330943309533096330973309833099331003310133102331033310433105331063310733108331093311033111331123311333114331153311633117331183311933120331213312233123331243312533126331273312833129331303313133132331333313433135331363313733138331393314033141331423314333144331453314633147331483314933150331513315233153331543315533156331573315833159331603316133162331633316433165331663316733168331693317033171331723317333174331753317633177331783317933180331813318233183331843318533186331873318833189331903319133192331933319433195331963319733198331993320033201332023320333204332053320633207332083320933210332113321233213332143321533216332173321833219332203322133222332233322433225332263322733228332293323033231332323323333234332353323633237332383323933240332413324233243332443324533246332473324833249332503325133252332533325433255332563325733258332593326033261332623326333264332653326633267332683326933270332713327233273332743327533276332773327833279332803328133282332833328433285332863328733288332893329033291332923329333294332953329633297332983329933300333013330233303333043330533306333073330833309333103331133312333133331433315333163331733318333193332033321333223332333324333253332633327333283332933330333313333233333333343333533336333373333833339333403334133342333433334433345333463334733348333493335033351333523335333354333553335633357333583335933360333613336233363333643336533366333673336833369333703337133372333733337433375333763337733378333793338033381333823338333384333853338633387333883338933390333913339233393333943339533396333973339833399334003340133402334033340433405334063340733408334093341033411334123341333414334153341633417334183341933420334213342233423334243342533426334273342833429334303343133432334333343433435334363343733438334393344033441334423344333444334453344633447334483344933450334513345233453334543345533456334573345833459334603346133462334633346433465334663346733468334693347033471334723347333474334753347633477334783347933480334813348233483334843348533486334873348833489334903349133492334933349433495334963349733498334993350033501335023350333504335053350633507335083350933510335113351233513335143351533516335173351833519335203352133522335233352433525335263352733528335293353033531335323353333534335353353633537335383353933540335413354233543335443354533546335473354833549335503355133552335533355433555335563355733558335593356033561335623356333564335653356633567335683356933570335713357233573335743357533576335773357833579335803358133582335833358433585335863358733588335893359033591335923359333594335953359633597335983359933600336013360233603336043360533606336073360833609336103361133612336133361433615336163361733618336193362033621336223362333624336253362633627336283362933630336313363233633336343363533636336373363833639336403364133642336433364433645336463364733648336493365033651336523365333654336553365633657336583365933660336613366233663336643366533666336673366833669336703367133672336733367433675336763367733678336793368033681336823368333684336853368633687336883368933690336913369233693336943369533696336973369833699337003370133702337033370433705337063370733708337093371033711337123371333714337153371633717337183371933720337213372233723337243372533726337273372833729337303373133732337333373433735337363373733738337393374033741337423374333744337453374633747337483374933750337513375233753337543375533756337573375833759337603376133762337633376433765337663376733768337693377033771337723377333774337753377633777337783377933780337813378233783337843378533786337873378833789337903379133792337933379433795337963379733798337993380033801338023380333804338053380633807338083380933810338113381233813338143381533816338173381833819338203382133822338233382433825338263382733828338293383033831338323383333834338353383633837338383383933840338413384233843338443384533846338473384833849338503385133852338533385433855338563385733858338593386033861338623386333864338653386633867338683386933870338713387233873338743387533876338773387833879338803388133882338833388433885338863388733888338893389033891338923389333894338953389633897338983389933900339013390233903339043390533906339073390833909339103391133912339133391433915339163391733918339193392033921339223392333924339253392633927339283392933930339313393233933339343393533936339373393833939339403394133942339433394433945339463394733948339493395033951339523395333954339553395633957339583395933960339613396233963339643396533966339673396833969339703397133972339733397433975339763397733978339793398033981339823398333984339853398633987339883398933990339913399233993339943399533996339973399833999340003400134002340033400434005340063400734008340093401034011340123401334014340153401634017340183401934020340213402234023340243402534026340273402834029340303403134032340333403434035340363403734038340393404034041340423404334044340453404634047340483404934050340513405234053340543405534056340573405834059340603406134062340633406434065340663406734068340693407034071340723407334074340753407634077340783407934080340813408234083340843408534086340873408834089340903409134092340933409434095340963409734098340993410034101341023410334104341053410634107341083410934110341113411234113341143411534116341173411834119341203412134122341233412434125341263412734128341293413034131341323413334134341353413634137341383413934140341413414234143341443414534146341473414834149341503415134152341533415434155341563415734158341593416034161341623416334164341653416634167341683416934170341713417234173341743417534176341773417834179341803418134182341833418434185341863418734188341893419034191341923419334194341953419634197341983419934200342013420234203342043420534206342073420834209342103421134212342133421434215342163421734218342193422034221342223422334224342253422634227342283422934230342313423234233342343423534236342373423834239342403424134242342433424434245342463424734248342493425034251342523425334254342553425634257342583425934260342613426234263342643426534266342673426834269342703427134272342733427434275342763427734278342793428034281342823428334284342853428634287342883428934290342913429234293342943429534296342973429834299343003430134302343033430434305343063430734308343093431034311343123431334314343153431634317343183431934320343213432234323343243432534326343273432834329343303433134332343333433434335343363433734338343393434034341343423434334344343453434634347343483434934350343513435234353343543435534356343573435834359343603436134362343633436434365343663436734368343693437034371343723437334374343753437634377343783437934380343813438234383343843438534386343873438834389343903439134392343933439434395343963439734398343993440034401344023440334404344053440634407344083440934410344113441234413344143441534416344173441834419344203442134422344233442434425344263442734428344293443034431344323443334434344353443634437344383443934440344413444234443344443444534446344473444834449344503445134452344533445434455344563445734458344593446034461344623446334464344653446634467344683446934470344713447234473344743447534476344773447834479344803448134482344833448434485344863448734488344893449034491344923449334494344953449634497344983449934500345013450234503345043450534506345073450834509345103451134512345133451434515345163451734518345193452034521345223452334524345253452634527345283452934530345313453234533345343453534536345373453834539345403454134542345433454434545345463454734548345493455034551345523455334554345553455634557345583455934560345613456234563345643456534566345673456834569345703457134572345733457434575345763457734578345793458034581345823458334584345853458634587345883458934590345913459234593345943459534596345973459834599346003460134602346033460434605346063460734608346093461034611346123461334614346153461634617346183461934620346213462234623346243462534626346273462834629346303463134632346333463434635346363463734638346393464034641346423464334644346453464634647346483464934650346513465234653346543465534656346573465834659346603466134662346633466434665346663466734668346693467034671346723467334674346753467634677346783467934680346813468234683346843468534686346873468834689346903469134692346933469434695346963469734698346993470034701347023470334704347053470634707347083470934710347113471234713347143471534716347173471834719347203472134722347233472434725347263472734728347293473034731347323473334734347353473634737347383473934740347413474234743347443474534746347473474834749347503475134752347533475434755347563475734758347593476034761347623476334764347653476634767347683476934770347713477234773347743477534776347773477834779347803478134782347833478434785347863478734788347893479034791347923479334794347953479634797347983479934800348013480234803348043480534806348073480834809348103481134812348133481434815348163481734818348193482034821348223482334824348253482634827348283482934830348313483234833348343483534836348373483834839348403484134842348433484434845348463484734848348493485034851348523485334854348553485634857348583485934860348613486234863348643486534866348673486834869348703487134872348733487434875348763487734878348793488034881348823488334884348853488634887348883488934890348913489234893348943489534896348973489834899349003490134902349033490434905349063490734908349093491034911349123491334914349153491634917349183491934920349213492234923349243492534926349273492834929349303493134932349333493434935349363493734938349393494034941349423494334944349453494634947349483494934950349513495234953349543495534956349573495834959349603496134962349633496434965349663496734968349693497034971349723497334974349753497634977349783497934980349813498234983349843498534986349873498834989349903499134992349933499434995349963499734998349993500035001350023500335004350053500635007350083500935010350113501235013350143501535016350173501835019350203502135022350233502435025350263502735028350293503035031350323503335034350353503635037350383503935040350413504235043350443504535046350473504835049350503505135052350533505435055350563505735058350593506035061350623506335064350653506635067350683506935070350713507235073350743507535076350773507835079350803508135082350833508435085350863508735088350893509035091350923509335094350953509635097350983509935100351013510235103351043510535106351073510835109351103511135112351133511435115351163511735118351193512035121351223512335124351253512635127351283512935130351313513235133351343513535136351373513835139351403514135142351433514435145351463514735148351493515035151351523515335154351553515635157351583515935160351613516235163351643516535166351673516835169351703517135172351733517435175351763517735178351793518035181351823518335184351853518635187351883518935190351913519235193351943519535196351973519835199352003520135202352033520435205352063520735208352093521035211352123521335214352153521635217352183521935220352213522235223352243522535226352273522835229352303523135232352333523435235352363523735238352393524035241352423524335244352453524635247352483524935250352513525235253352543525535256352573525835259352603526135262352633526435265352663526735268352693527035271352723527335274352753527635277352783527935280352813528235283352843528535286352873528835289352903529135292352933529435295352963529735298352993530035301353023530335304353053530635307353083530935310353113531235313353143531535316353173531835319353203532135322353233532435325353263532735328353293533035331353323533335334353353533635337353383533935340353413534235343353443534535346353473534835349353503535135352353533535435355353563535735358353593536035361353623536335364353653536635367353683536935370353713537235373353743537535376353773537835379353803538135382353833538435385353863538735388353893539035391353923539335394353953539635397353983539935400354013540235403354043540535406354073540835409354103541135412354133541435415354163541735418354193542035421354223542335424354253542635427354283542935430354313543235433354343543535436354373543835439354403544135442354433544435445354463544735448354493545035451354523545335454354553545635457354583545935460354613546235463354643546535466354673546835469354703547135472354733547435475354763547735478354793548035481354823548335484354853548635487354883548935490354913549235493354943549535496354973549835499355003550135502355033550435505355063550735508355093551035511355123551335514355153551635517355183551935520355213552235523355243552535526355273552835529355303553135532355333553435535355363553735538355393554035541355423554335544355453554635547355483554935550355513555235553355543555535556355573555835559355603556135562355633556435565355663556735568355693557035571355723557335574355753557635577355783557935580355813558235583355843558535586355873558835589355903559135592355933559435595355963559735598355993560035601356023560335604356053560635607356083560935610356113561235613356143561535616356173561835619356203562135622356233562435625356263562735628356293563035631356323563335634356353563635637356383563935640356413564235643356443564535646356473564835649356503565135652356533565435655356563565735658356593566035661356623566335664356653566635667356683566935670356713567235673356743567535676356773567835679356803568135682356833568435685356863568735688356893569035691356923569335694356953569635697356983569935700357013570235703357043570535706357073570835709357103571135712357133571435715357163571735718357193572035721357223572335724357253572635727357283572935730357313573235733357343573535736357373573835739357403574135742357433574435745357463574735748357493575035751357523575335754357553575635757357583575935760357613576235763357643576535766357673576835769357703577135772357733577435775357763577735778357793578035781357823578335784357853578635787357883578935790357913579235793357943579535796357973579835799358003580135802358033580435805358063580735808358093581035811358123581335814358153581635817358183581935820358213582235823358243582535826358273582835829358303583135832358333583435835358363583735838358393584035841358423584335844358453584635847358483584935850358513585235853358543585535856358573585835859358603586135862358633586435865358663586735868358693587035871358723587335874358753587635877358783587935880358813588235883358843588535886358873588835889358903589135892358933589435895358963589735898358993590035901359023590335904359053590635907359083590935910359113591235913359143591535916359173591835919359203592135922359233592435925359263592735928359293593035931359323593335934359353593635937359383593935940359413594235943359443594535946359473594835949359503595135952359533595435955359563595735958359593596035961359623596335964359653596635967359683596935970359713597235973359743597535976359773597835979359803598135982359833598435985359863598735988359893599035991359923599335994359953599635997359983599936000360013600236003360043600536006360073600836009360103601136012360133601436015360163601736018360193602036021360223602336024360253602636027360283602936030360313603236033360343603536036360373603836039360403604136042360433604436045360463604736048360493605036051360523605336054360553605636057360583605936060360613606236063360643606536066360673606836069360703607136072360733607436075360763607736078360793608036081360823608336084360853608636087360883608936090360913609236093360943609536096360973609836099361003610136102361033610436105361063610736108361093611036111361123611336114361153611636117361183611936120361213612236123361243612536126361273612836129361303613136132361333613436135361363613736138361393614036141361423614336144361453614636147361483614936150361513615236153361543615536156361573615836159361603616136162361633616436165361663616736168361693617036171361723617336174361753617636177361783617936180361813618236183361843618536186361873618836189361903619136192361933619436195361963619736198361993620036201362023620336204362053620636207362083620936210362113621236213362143621536216362173621836219362203622136222362233622436225362263622736228362293623036231362323623336234362353623636237362383623936240362413624236243362443624536246362473624836249362503625136252362533625436255362563625736258362593626036261362623626336264362653626636267362683626936270362713627236273362743627536276362773627836279362803628136282362833628436285362863628736288362893629036291362923629336294362953629636297362983629936300363013630236303363043630536306363073630836309363103631136312363133631436315363163631736318363193632036321363223632336324363253632636327363283632936330363313633236333363343633536336363373633836339363403634136342363433634436345363463634736348363493635036351363523635336354363553635636357363583635936360363613636236363363643636536366363673636836369363703637136372363733637436375363763637736378363793638036381363823638336384363853638636387363883638936390363913639236393363943639536396363973639836399364003640136402364033640436405364063640736408364093641036411364123641336414364153641636417364183641936420364213642236423364243642536426364273642836429364303643136432364333643436435364363643736438364393644036441364423644336444364453644636447364483644936450364513645236453364543645536456364573645836459364603646136462364633646436465364663646736468364693647036471364723647336474364753647636477364783647936480364813648236483364843648536486364873648836489364903649136492364933649436495364963649736498364993650036501365023650336504365053650636507365083650936510365113651236513365143651536516365173651836519365203652136522365233652436525365263652736528365293653036531365323653336534365353653636537365383653936540365413654236543365443654536546365473654836549365503655136552365533655436555365563655736558365593656036561365623656336564365653656636567365683656936570365713657236573365743657536576365773657836579365803658136582365833658436585365863658736588365893659036591365923659336594365953659636597365983659936600366013660236603366043660536606366073660836609366103661136612366133661436615366163661736618366193662036621366223662336624366253662636627366283662936630366313663236633366343663536636366373663836639366403664136642366433664436645366463664736648366493665036651366523665336654366553665636657366583665936660366613666236663366643666536666366673666836669366703667136672366733667436675366763667736678366793668036681366823668336684366853668636687366883668936690366913669236693366943669536696366973669836699367003670136702367033670436705367063670736708367093671036711367123671336714367153671636717367183671936720367213672236723367243672536726367273672836729367303673136732367333673436735367363673736738367393674036741367423674336744367453674636747367483674936750367513675236753367543675536756367573675836759367603676136762367633676436765367663676736768367693677036771367723677336774367753677636777367783677936780367813678236783367843678536786367873678836789367903679136792367933679436795367963679736798367993680036801368023680336804368053680636807368083680936810368113681236813368143681536816368173681836819368203682136822368233682436825368263682736828368293683036831368323683336834368353683636837368383683936840368413684236843368443684536846368473684836849368503685136852368533685436855368563685736858368593686036861368623686336864368653686636867368683686936870368713687236873368743687536876368773687836879368803688136882368833688436885368863688736888368893689036891368923689336894368953689636897368983689936900369013690236903369043690536906369073690836909369103691136912369133691436915369163691736918369193692036921369223692336924369253692636927369283692936930369313693236933369343693536936369373693836939369403694136942369433694436945369463694736948369493695036951369523695336954369553695636957369583695936960369613696236963369643696536966369673696836969369703697136972369733697436975369763697736978369793698036981369823698336984369853698636987369883698936990369913699236993369943699536996369973699836999370003700137002370033700437005370063700737008370093701037011370123701337014370153701637017370183701937020370213702237023370243702537026370273702837029370303703137032370333703437035370363703737038370393704037041370423704337044370453704637047370483704937050370513705237053370543705537056370573705837059370603706137062370633706437065370663706737068370693707037071370723707337074370753707637077370783707937080370813708237083370843708537086370873708837089370903709137092370933709437095370963709737098370993710037101371023710337104371053710637107371083710937110371113711237113371143711537116371173711837119371203712137122371233712437125371263712737128371293713037131371323713337134371353713637137371383713937140371413714237143371443714537146371473714837149371503715137152371533715437155371563715737158371593716037161371623716337164371653716637167371683716937170371713717237173371743717537176371773717837179371803718137182371833718437185371863718737188371893719037191371923719337194371953719637197371983719937200372013720237203372043720537206372073720837209372103721137212372133721437215372163721737218372193722037221372223722337224372253722637227372283722937230372313723237233372343723537236372373723837239372403724137242372433724437245372463724737248372493725037251372523725337254372553725637257372583725937260372613726237263372643726537266372673726837269372703727137272372733727437275372763727737278372793728037281372823728337284372853728637287372883728937290372913729237293372943729537296372973729837299373003730137302373033730437305373063730737308373093731037311373123731337314373153731637317373183731937320373213732237323373243732537326373273732837329373303733137332373333733437335373363733737338373393734037341373423734337344373453734637347373483734937350373513735237353373543735537356373573735837359373603736137362373633736437365373663736737368373693737037371373723737337374373753737637377373783737937380373813738237383373843738537386373873738837389373903739137392373933739437395373963739737398373993740037401374023740337404374053740637407374083740937410374113741237413374143741537416374173741837419374203742137422374233742437425374263742737428374293743037431374323743337434374353743637437374383743937440374413744237443374443744537446374473744837449374503745137452374533745437455374563745737458374593746037461374623746337464374653746637467374683746937470374713747237473374743747537476374773747837479374803748137482374833748437485374863748737488374893749037491374923749337494374953749637497374983749937500375013750237503375043750537506375073750837509375103751137512375133751437515375163751737518375193752037521375223752337524375253752637527375283752937530375313753237533375343753537536375373753837539375403754137542375433754437545375463754737548375493755037551375523755337554375553755637557375583755937560375613756237563375643756537566375673756837569375703757137572375733757437575375763757737578375793758037581375823758337584375853758637587375883758937590375913759237593375943759537596375973759837599376003760137602376033760437605376063760737608376093761037611376123761337614376153761637617376183761937620376213762237623376243762537626376273762837629376303763137632376333763437635376363763737638376393764037641376423764337644376453764637647376483764937650376513765237653376543765537656376573765837659376603766137662376633766437665376663766737668376693767037671376723767337674376753767637677376783767937680376813768237683376843768537686376873768837689376903769137692376933769437695376963769737698376993770037701377023770337704377053770637707377083770937710377113771237713377143771537716377173771837719377203772137722377233772437725377263772737728377293773037731377323773337734377353773637737377383773937740377413774237743377443774537746377473774837749377503775137752377533775437755377563775737758377593776037761377623776337764377653776637767377683776937770377713777237773377743777537776377773777837779377803778137782377833778437785377863778737788377893779037791377923779337794377953779637797377983779937800378013780237803378043780537806378073780837809378103781137812378133781437815378163781737818378193782037821378223782337824378253782637827378283782937830378313783237833378343783537836378373783837839378403784137842378433784437845378463784737848378493785037851378523785337854378553785637857378583785937860378613786237863378643786537866378673786837869378703787137872378733787437875378763787737878378793788037881378823788337884378853788637887378883788937890378913789237893378943789537896378973789837899379003790137902379033790437905379063790737908379093791037911379123791337914379153791637917379183791937920379213792237923379243792537926379273792837929379303793137932379333793437935379363793737938379393794037941379423794337944379453794637947379483794937950379513795237953379543795537956379573795837959379603796137962379633796437965379663796737968379693797037971379723797337974379753797637977379783797937980379813798237983379843798537986379873798837989379903799137992379933799437995379963799737998379993800038001380023800338004380053800638007380083800938010380113801238013380143801538016380173801838019380203802138022380233802438025380263802738028380293803038031380323803338034380353803638037380383803938040380413804238043380443804538046380473804838049380503805138052380533805438055380563805738058380593806038061380623806338064380653806638067380683806938070380713807238073380743807538076380773807838079380803808138082380833808438085380863808738088380893809038091380923809338094380953809638097380983809938100381013810238103381043810538106381073810838109381103811138112381133811438115381163811738118381193812038121381223812338124381253812638127381283812938130381313813238133381343813538136381373813838139381403814138142381433814438145381463814738148381493815038151381523815338154381553815638157381583815938160381613816238163381643816538166381673816838169381703817138172381733817438175381763817738178381793818038181381823818338184381853818638187381883818938190381913819238193381943819538196381973819838199382003820138202382033820438205382063820738208382093821038211382123821338214382153821638217382183821938220382213822238223382243822538226382273822838229382303823138232382333823438235382363823738238382393824038241382423824338244382453824638247382483824938250382513825238253382543825538256382573825838259382603826138262382633826438265382663826738268382693827038271382723827338274382753827638277382783827938280382813828238283382843828538286382873828838289382903829138292382933829438295382963829738298382993830038301383023830338304383053830638307383083830938310383113831238313383143831538316383173831838319383203832138322383233832438325383263832738328383293833038331383323833338334383353833638337383383833938340383413834238343383443834538346383473834838349383503835138352383533835438355383563835738358383593836038361383623836338364383653836638367383683836938370383713837238373383743837538376383773837838379383803838138382383833838438385383863838738388383893839038391383923839338394383953839638397383983839938400384013840238403384043840538406384073840838409384103841138412384133841438415384163841738418384193842038421384223842338424384253842638427384283842938430384313843238433384343843538436384373843838439384403844138442384433844438445384463844738448384493845038451384523845338454384553845638457384583845938460384613846238463384643846538466384673846838469384703847138472384733847438475384763847738478384793848038481384823848338484384853848638487384883848938490384913849238493384943849538496384973849838499385003850138502385033850438505385063850738508385093851038511385123851338514385153851638517385183851938520385213852238523385243852538526385273852838529385303853138532385333853438535385363853738538385393854038541385423854338544385453854638547385483854938550385513855238553385543855538556385573855838559385603856138562385633856438565385663856738568385693857038571385723857338574385753857638577385783857938580385813858238583385843858538586385873858838589385903859138592385933859438595385963859738598385993860038601386023860338604386053860638607386083860938610386113861238613386143861538616386173861838619386203862138622386233862438625386263862738628386293863038631386323863338634386353863638637386383863938640386413864238643386443864538646386473864838649386503865138652386533865438655386563865738658386593866038661386623866338664386653866638667386683866938670386713867238673386743867538676386773867838679386803868138682386833868438685386863868738688386893869038691386923869338694386953869638697386983869938700387013870238703387043870538706387073870838709387103871138712387133871438715387163871738718387193872038721387223872338724387253872638727387283872938730387313873238733387343873538736387373873838739387403874138742387433874438745387463874738748387493875038751387523875338754387553875638757387583875938760387613876238763387643876538766387673876838769387703877138772387733877438775387763877738778387793878038781387823878338784387853878638787387883878938790387913879238793387943879538796387973879838799388003880138802388033880438805388063880738808388093881038811388123881338814388153881638817388183881938820388213882238823388243882538826388273882838829388303883138832388333883438835388363883738838388393884038841388423884338844388453884638847388483884938850388513885238853388543885538856388573885838859388603886138862388633886438865388663886738868388693887038871388723887338874388753887638877388783887938880388813888238883388843888538886388873888838889388903889138892388933889438895388963889738898388993890038901389023890338904389053890638907389083890938910389113891238913389143891538916389173891838919389203892138922389233892438925389263892738928389293893038931389323893338934389353893638937389383893938940389413894238943389443894538946389473894838949389503895138952389533895438955389563895738958389593896038961389623896338964389653896638967389683896938970389713897238973389743897538976389773897838979389803898138982389833898438985389863898738988389893899038991389923899338994389953899638997389983899939000390013900239003390043900539006390073900839009390103901139012390133901439015390163901739018390193902039021390223902339024390253902639027390283902939030390313903239033390343903539036390373903839039390403904139042390433904439045390463904739048390493905039051390523905339054390553905639057390583905939060390613906239063390643906539066390673906839069390703907139072390733907439075390763907739078390793908039081390823908339084390853908639087390883908939090390913909239093390943909539096390973909839099391003910139102391033910439105391063910739108391093911039111391123911339114391153911639117391183911939120391213912239123391243912539126391273912839129391303913139132391333913439135391363913739138391393914039141391423914339144391453914639147391483914939150391513915239153391543915539156391573915839159391603916139162391633916439165391663916739168391693917039171391723917339174391753917639177391783917939180391813918239183391843918539186391873918839189391903919139192391933919439195391963919739198391993920039201392023920339204392053920639207392083920939210392113921239213392143921539216392173921839219392203922139222392233922439225392263922739228392293923039231392323923339234392353923639237392383923939240392413924239243392443924539246392473924839249392503925139252392533925439255392563925739258392593926039261392623926339264392653926639267392683926939270392713927239273392743927539276392773927839279392803928139282392833928439285392863928739288392893929039291392923929339294392953929639297392983929939300393013930239303393043930539306393073930839309393103931139312393133931439315393163931739318393193932039321393223932339324393253932639327393283932939330393313933239333393343933539336393373933839339393403934139342393433934439345393463934739348393493935039351393523935339354393553935639357393583935939360393613936239363393643936539366393673936839369393703937139372393733937439375393763937739378393793938039381393823938339384393853938639387393883938939390393913939239393393943939539396393973939839399394003940139402394033940439405394063940739408394093941039411394123941339414394153941639417394183941939420394213942239423394243942539426394273942839429394303943139432394333943439435394363943739438394393944039441394423944339444394453944639447394483944939450394513945239453394543945539456394573945839459394603946139462394633946439465394663946739468394693947039471394723947339474394753947639477394783947939480394813948239483394843948539486394873948839489394903949139492394933949439495394963949739498394993950039501395023950339504395053950639507395083950939510395113951239513395143951539516395173951839519395203952139522395233952439525395263952739528395293953039531395323953339534395353953639537395383953939540395413954239543395443954539546395473954839549395503955139552395533955439555395563955739558395593956039561395623956339564395653956639567395683956939570395713957239573395743957539576395773957839579395803958139582395833958439585395863958739588395893959039591395923959339594395953959639597395983959939600396013960239603396043960539606396073960839609396103961139612396133961439615396163961739618396193962039621396223962339624396253962639627396283962939630396313963239633396343963539636396373963839639396403964139642396433964439645396463964739648396493965039651396523965339654396553965639657396583965939660396613966239663396643966539666396673966839669396703967139672396733967439675396763967739678396793968039681396823968339684396853968639687396883968939690396913969239693396943969539696396973969839699397003970139702397033970439705397063970739708397093971039711397123971339714397153971639717397183971939720397213972239723397243972539726397273972839729397303973139732397333973439735397363973739738397393974039741397423974339744397453974639747397483974939750397513975239753397543975539756397573975839759397603976139762397633976439765397663976739768397693977039771397723977339774397753977639777397783977939780397813978239783397843978539786397873978839789397903979139792397933979439795397963979739798397993980039801398023980339804398053980639807398083980939810398113981239813398143981539816398173981839819398203982139822398233982439825398263982739828398293983039831398323983339834398353983639837398383983939840398413984239843398443984539846398473984839849398503985139852398533985439855398563985739858398593986039861398623986339864398653986639867398683986939870398713987239873398743987539876398773987839879398803988139882398833988439885398863988739888398893989039891398923989339894398953989639897398983989939900399013990239903399043990539906399073990839909399103991139912399133991439915399163991739918399193992039921399223992339924399253992639927399283992939930399313993239933399343993539936399373993839939399403994139942399433994439945399463994739948399493995039951399523995339954399553995639957399583995939960399613996239963399643996539966399673996839969399703997139972399733997439975399763997739978399793998039981399823998339984399853998639987399883998939990399913999239993399943999539996399973999839999400004000140002400034000440005400064000740008400094001040011400124001340014400154001640017400184001940020400214002240023400244002540026400274002840029400304003140032400334003440035400364003740038400394004040041400424004340044400454004640047400484004940050400514005240053400544005540056400574005840059400604006140062400634006440065400664006740068400694007040071400724007340074400754007640077400784007940080400814008240083400844008540086400874008840089400904009140092400934009440095400964009740098400994010040101401024010340104401054010640107401084010940110401114011240113401144011540116401174011840119401204012140122401234012440125401264012740128401294013040131401324013340134401354013640137401384013940140401414014240143401444014540146401474014840149401504015140152401534015440155401564015740158401594016040161401624016340164401654016640167401684016940170401714017240173401744017540176401774017840179401804018140182401834018440185401864018740188401894019040191401924019340194401954019640197401984019940200402014020240203402044020540206402074020840209402104021140212402134021440215402164021740218402194022040221402224022340224402254022640227402284022940230402314023240233402344023540236402374023840239402404024140242402434024440245402464024740248402494025040251402524025340254402554025640257402584025940260402614026240263402644026540266402674026840269402704027140272402734027440275402764027740278402794028040281402824028340284402854028640287402884028940290402914029240293402944029540296402974029840299403004030140302403034030440305403064030740308403094031040311403124031340314403154031640317403184031940320403214032240323403244032540326403274032840329403304033140332403334033440335403364033740338403394034040341403424034340344403454034640347403484034940350403514035240353403544035540356403574035840359403604036140362403634036440365403664036740368403694037040371403724037340374403754037640377403784037940380403814038240383403844038540386403874038840389403904039140392403934039440395403964039740398403994040040401404024040340404404054040640407404084040940410404114041240413404144041540416404174041840419404204042140422404234042440425404264042740428404294043040431404324043340434404354043640437404384043940440404414044240443404444044540446404474044840449404504045140452404534045440455404564045740458404594046040461404624046340464404654046640467404684046940470404714047240473404744047540476404774047840479404804048140482404834048440485404864048740488404894049040491404924049340494404954049640497404984049940500405014050240503405044050540506405074050840509405104051140512405134051440515405164051740518405194052040521405224052340524405254052640527405284052940530405314053240533405344053540536405374053840539405404054140542405434054440545405464054740548405494055040551405524055340554405554055640557405584055940560405614056240563405644056540566405674056840569405704057140572405734057440575405764057740578405794058040581405824058340584405854058640587405884058940590405914059240593405944059540596405974059840599406004060140602406034060440605406064060740608406094061040611406124061340614406154061640617406184061940620406214062240623406244062540626406274062840629406304063140632406334063440635406364063740638406394064040641406424064340644406454064640647406484064940650406514065240653406544065540656406574065840659406604066140662406634066440665406664066740668406694067040671406724067340674406754067640677406784067940680406814068240683406844068540686406874068840689406904069140692406934069440695406964069740698406994070040701407024070340704407054070640707407084070940710407114071240713407144071540716407174071840719407204072140722407234072440725407264072740728407294073040731407324073340734407354073640737407384073940740407414074240743407444074540746407474074840749407504075140752407534075440755407564075740758407594076040761407624076340764407654076640767407684076940770407714077240773407744077540776407774077840779407804078140782407834078440785407864078740788407894079040791407924079340794407954079640797407984079940800408014080240803408044080540806408074080840809408104081140812408134081440815408164081740818408194082040821408224082340824408254082640827408284082940830408314083240833408344083540836408374083840839408404084140842408434084440845408464084740848408494085040851408524085340854408554085640857408584085940860408614086240863408644086540866408674086840869408704087140872408734087440875408764087740878408794088040881408824088340884408854088640887408884088940890408914089240893408944089540896408974089840899409004090140902409034090440905409064090740908409094091040911409124091340914409154091640917409184091940920409214092240923409244092540926409274092840929409304093140932409334093440935409364093740938409394094040941409424094340944409454094640947409484094940950409514095240953409544095540956409574095840959409604096140962409634096440965409664096740968409694097040971409724097340974409754097640977409784097940980409814098240983409844098540986409874098840989409904099140992409934099440995409964099740998409994100041001410024100341004410054100641007410084100941010410114101241013410144101541016410174101841019410204102141022410234102441025410264102741028410294103041031410324103341034410354103641037410384103941040410414104241043410444104541046410474104841049410504105141052410534105441055410564105741058410594106041061410624106341064410654106641067410684106941070410714107241073410744107541076410774107841079410804108141082410834108441085410864108741088410894109041091410924109341094410954109641097410984109941100411014110241103411044110541106411074110841109411104111141112411134111441115411164111741118411194112041121411224112341124411254112641127411284112941130411314113241133411344113541136411374113841139411404114141142411434114441145411464114741148411494115041151411524115341154411554115641157411584115941160411614116241163411644116541166411674116841169411704117141172411734117441175411764117741178411794118041181411824118341184411854118641187411884118941190411914119241193411944119541196411974119841199412004120141202412034120441205412064120741208412094121041211412124121341214412154121641217412184121941220412214122241223412244122541226412274122841229412304123141232412334123441235412364123741238412394124041241412424124341244412454124641247412484124941250412514125241253412544125541256412574125841259412604126141262412634126441265412664126741268412694127041271412724127341274412754127641277412784127941280412814128241283412844128541286412874128841289412904129141292412934129441295412964129741298412994130041301413024130341304413054130641307413084130941310413114131241313413144131541316413174131841319413204132141322413234132441325413264132741328413294133041331413324133341334413354133641337413384133941340413414134241343413444134541346413474134841349413504135141352413534135441355413564135741358413594136041361413624136341364413654136641367413684136941370413714137241373413744137541376413774137841379413804138141382413834138441385413864138741388413894139041391413924139341394413954139641397413984139941400414014140241403414044140541406414074140841409414104141141412414134141441415414164141741418414194142041421414224142341424414254142641427414284142941430414314143241433414344143541436414374143841439414404144141442414434144441445414464144741448414494145041451414524145341454414554145641457414584145941460414614146241463414644146541466414674146841469414704147141472414734147441475414764147741478414794148041481414824148341484414854148641487414884148941490414914149241493414944149541496414974149841499415004150141502415034150441505415064150741508415094151041511415124151341514415154151641517415184151941520415214152241523415244152541526415274152841529415304153141532415334153441535415364153741538415394154041541415424154341544415454154641547415484154941550415514155241553415544155541556415574155841559415604156141562415634156441565415664156741568415694157041571415724157341574415754157641577415784157941580415814158241583415844158541586415874158841589415904159141592415934159441595415964159741598415994160041601416024160341604416054160641607416084160941610416114161241613416144161541616416174161841619416204162141622416234162441625416264162741628416294163041631416324163341634416354163641637416384163941640416414164241643416444164541646416474164841649416504165141652416534165441655416564165741658416594166041661416624166341664416654166641667416684166941670416714167241673416744167541676416774167841679416804168141682416834168441685416864168741688416894169041691416924169341694416954169641697416984169941700417014170241703417044170541706417074170841709417104171141712417134171441715417164171741718417194172041721417224172341724417254172641727417284172941730417314173241733417344173541736417374173841739417404174141742417434174441745417464174741748417494175041751417524175341754417554175641757417584175941760417614176241763417644176541766417674176841769417704177141772417734177441775417764177741778417794178041781417824178341784417854178641787417884178941790417914179241793417944179541796417974179841799418004180141802418034180441805418064180741808418094181041811418124181341814418154181641817418184181941820418214182241823418244182541826418274182841829418304183141832418334183441835418364183741838418394184041841418424184341844418454184641847418484184941850418514185241853418544185541856418574185841859418604186141862418634186441865418664186741868418694187041871418724187341874418754187641877418784187941880418814188241883418844188541886418874188841889418904189141892418934189441895418964189741898418994190041901419024190341904419054190641907419084190941910419114191241913419144191541916419174191841919419204192141922419234192441925419264192741928419294193041931419324193341934419354193641937419384193941940419414194241943419444194541946419474194841949419504195141952419534195441955419564195741958419594196041961419624196341964419654196641967419684196941970419714197241973419744197541976419774197841979419804198141982419834198441985419864198741988419894199041991419924199341994419954199641997419984199942000420014200242003420044200542006420074200842009420104201142012420134201442015420164201742018420194202042021420224202342024420254202642027420284202942030420314203242033420344203542036420374203842039420404204142042420434204442045420464204742048420494205042051420524205342054420554205642057420584205942060420614206242063420644206542066420674206842069420704207142072420734207442075420764207742078420794208042081420824208342084420854208642087420884208942090420914209242093420944209542096420974209842099421004210142102421034210442105421064210742108421094211042111421124211342114421154211642117421184211942120421214212242123421244212542126421274212842129421304213142132421334213442135421364213742138421394214042141421424214342144421454214642147421484214942150421514215242153421544215542156421574215842159421604216142162421634216442165421664216742168421694217042171421724217342174421754217642177421784217942180421814218242183421844218542186421874218842189421904219142192421934219442195421964219742198421994220042201422024220342204422054220642207422084220942210422114221242213422144221542216422174221842219422204222142222422234222442225422264222742228422294223042231422324223342234422354223642237422384223942240422414224242243422444224542246422474224842249422504225142252422534225442255422564225742258422594226042261422624226342264422654226642267422684226942270422714227242273422744227542276422774227842279422804228142282422834228442285422864228742288422894229042291422924229342294422954229642297422984229942300423014230242303423044230542306423074230842309423104231142312423134231442315423164231742318423194232042321423224232342324423254232642327423284232942330423314233242333423344233542336423374233842339423404234142342423434234442345423464234742348423494235042351423524235342354423554235642357423584235942360423614236242363423644236542366423674236842369423704237142372423734237442375423764237742378423794238042381423824238342384423854238642387423884238942390423914239242393423944239542396423974239842399424004240142402424034240442405424064240742408424094241042411424124241342414424154241642417424184241942420424214242242423424244242542426424274242842429424304243142432424334243442435424364243742438424394244042441424424244342444424454244642447424484244942450424514245242453424544245542456424574245842459424604246142462424634246442465424664246742468424694247042471424724247342474424754247642477424784247942480424814248242483424844248542486424874248842489424904249142492424934249442495424964249742498424994250042501425024250342504425054250642507425084250942510425114251242513425144251542516425174251842519425204252142522425234252442525425264252742528425294253042531425324253342534425354253642537425384253942540425414254242543425444254542546425474254842549425504255142552425534255442555425564255742558425594256042561425624256342564425654256642567425684256942570425714257242573425744257542576425774257842579425804258142582425834258442585425864258742588425894259042591425924259342594425954259642597425984259942600426014260242603426044260542606426074260842609426104261142612426134261442615426164261742618426194262042621426224262342624426254262642627426284262942630426314263242633426344263542636426374263842639426404264142642426434264442645426464264742648426494265042651426524265342654426554265642657426584265942660426614266242663426644266542666426674266842669426704267142672426734267442675426764267742678426794268042681426824268342684426854268642687426884268942690426914269242693426944269542696426974269842699427004270142702427034270442705427064270742708427094271042711427124271342714427154271642717427184271942720427214272242723427244272542726427274272842729427304273142732427334273442735427364273742738427394274042741427424274342744427454274642747427484274942750427514275242753427544275542756427574275842759427604276142762427634276442765427664276742768427694277042771427724277342774427754277642777427784277942780427814278242783427844278542786427874278842789427904279142792427934279442795427964279742798427994280042801428024280342804428054280642807428084280942810428114281242813428144281542816428174281842819428204282142822428234282442825428264282742828428294283042831428324283342834428354283642837428384283942840428414284242843428444284542846428474284842849428504285142852428534285442855428564285742858428594286042861428624286342864428654286642867428684286942870428714287242873428744287542876428774287842879428804288142882428834288442885428864288742888428894289042891428924289342894428954289642897428984289942900429014290242903429044290542906429074290842909429104291142912429134291442915429164291742918429194292042921429224292342924429254292642927429284292942930429314293242933429344293542936429374293842939429404294142942429434294442945429464294742948429494295042951429524295342954429554295642957429584295942960429614296242963429644296542966429674296842969429704297142972429734297442975429764297742978429794298042981429824298342984429854298642987429884298942990429914299242993429944299542996429974299842999430004300143002430034300443005430064300743008430094301043011430124301343014430154301643017430184301943020430214302243023430244302543026430274302843029430304303143032430334303443035430364303743038430394304043041430424304343044430454304643047430484304943050430514305243053430544305543056430574305843059430604306143062430634306443065430664306743068430694307043071430724307343074430754307643077430784307943080430814308243083430844308543086430874308843089430904309143092430934309443095430964309743098430994310043101431024310343104431054310643107431084310943110431114311243113431144311543116431174311843119431204312143122431234312443125431264312743128431294313043131431324313343134431354313643137431384313943140431414314243143431444314543146431474314843149431504315143152431534315443155431564315743158431594316043161431624316343164431654316643167431684316943170431714317243173431744317543176431774317843179431804318143182431834318443185431864318743188431894319043191431924319343194431954319643197431984319943200432014320243203432044320543206432074320843209432104321143212432134321443215432164321743218432194322043221432224322343224432254322643227432284322943230432314323243233432344323543236432374323843239432404324143242432434324443245432464324743248432494325043251432524325343254432554325643257432584325943260432614326243263432644326543266432674326843269432704327143272432734327443275432764327743278432794328043281432824328343284432854328643287432884328943290432914329243293432944329543296432974329843299433004330143302433034330443305433064330743308433094331043311433124331343314433154331643317433184331943320433214332243323433244332543326433274332843329433304333143332433334333443335433364333743338433394334043341433424334343344433454334643347433484334943350433514335243353433544335543356433574335843359433604336143362433634336443365433664336743368433694337043371433724337343374433754337643377433784337943380433814338243383433844338543386433874338843389433904339143392433934339443395433964339743398433994340043401434024340343404434054340643407434084340943410434114341243413434144341543416434174341843419434204342143422434234342443425434264342743428434294343043431434324343343434434354343643437434384343943440434414344243443434444344543446434474344843449434504345143452434534345443455434564345743458434594346043461434624346343464434654346643467434684346943470434714347243473434744347543476434774347843479434804348143482434834348443485434864348743488434894349043491434924349343494434954349643497434984349943500435014350243503435044350543506435074350843509435104351143512435134351443515435164351743518435194352043521435224352343524435254352643527435284352943530435314353243533435344353543536435374353843539435404354143542435434354443545435464354743548435494355043551435524355343554435554355643557435584355943560435614356243563435644356543566435674356843569435704357143572435734357443575435764357743578435794358043581435824358343584435854358643587435884358943590435914359243593435944359543596435974359843599436004360143602436034360443605436064360743608436094361043611436124361343614436154361643617436184361943620436214362243623436244362543626436274362843629436304363143632436334363443635436364363743638436394364043641436424364343644436454364643647436484364943650436514365243653436544365543656436574365843659436604366143662436634366443665436664366743668436694367043671436724367343674436754367643677436784367943680436814368243683436844368543686436874368843689436904369143692436934369443695436964369743698436994370043701437024370343704437054370643707437084370943710437114371243713437144371543716437174371843719437204372143722437234372443725437264372743728437294373043731437324373343734437354373643737437384373943740437414374243743437444374543746437474374843749437504375143752437534375443755437564375743758437594376043761437624376343764437654376643767437684376943770437714377243773437744377543776437774377843779437804378143782437834378443785437864378743788437894379043791437924379343794437954379643797437984379943800438014380243803438044380543806438074380843809438104381143812438134381443815438164381743818438194382043821438224382343824438254382643827438284382943830438314383243833438344383543836438374383843839438404384143842438434384443845438464384743848438494385043851438524385343854438554385643857438584385943860438614386243863438644386543866438674386843869438704387143872438734387443875438764387743878438794388043881438824388343884438854388643887438884388943890438914389243893438944389543896438974389843899439004390143902439034390443905439064390743908439094391043911439124391343914439154391643917439184391943920439214392243923439244392543926439274392843929439304393143932439334393443935439364393743938439394394043941439424394343944439454394643947439484394943950439514395243953439544395543956439574395843959439604396143962439634396443965439664396743968439694397043971439724397343974439754397643977439784397943980439814398243983439844398543986439874398843989439904399143992439934399443995439964399743998439994400044001440024400344004440054400644007440084400944010440114401244013440144401544016440174401844019440204402144022440234402444025440264402744028440294403044031440324403344034440354403644037440384403944040440414404244043440444404544046440474404844049440504405144052440534405444055440564405744058440594406044061440624406344064440654406644067440684406944070440714407244073440744407544076440774407844079440804408144082440834408444085440864408744088440894409044091440924409344094440954409644097440984409944100441014410244103441044410544106441074410844109441104411144112441134411444115441164411744118441194412044121441224412344124441254412644127441284412944130441314413244133441344413544136441374413844139441404414144142441434414444145441464414744148441494415044151441524415344154441554415644157441584415944160441614416244163441644416544166441674416844169441704417144172441734417444175441764417744178441794418044181441824418344184441854418644187441884418944190441914419244193441944419544196441974419844199442004420144202442034420444205442064420744208442094421044211442124421344214442154421644217442184421944220442214422244223442244422544226442274422844229442304423144232442334423444235442364423744238442394424044241442424424344244442454424644247442484424944250442514425244253442544425544256442574425844259442604426144262442634426444265442664426744268442694427044271442724427344274442754427644277442784427944280442814428244283442844428544286442874428844289442904429144292442934429444295442964429744298442994430044301443024430344304443054430644307443084430944310443114431244313443144431544316443174431844319443204432144322443234432444325443264432744328443294433044331443324433344334443354433644337443384433944340443414434244343443444434544346443474434844349443504435144352443534435444355443564435744358443594436044361443624436344364443654436644367443684436944370443714437244373443744437544376443774437844379443804438144382443834438444385443864438744388443894439044391443924439344394443954439644397443984439944400444014440244403444044440544406444074440844409444104441144412444134441444415444164441744418444194442044421444224442344424444254442644427444284442944430444314443244433444344443544436444374443844439444404444144442444434444444445444464444744448444494445044451444524445344454444554445644457444584445944460444614446244463444644446544466444674446844469444704447144472444734447444475444764447744478444794448044481444824448344484444854448644487444884448944490444914449244493444944449544496444974449844499445004450144502445034450444505445064450744508445094451044511445124451344514445154451644517445184451944520445214452244523445244452544526445274452844529445304453144532445334453444535445364453744538445394454044541445424454344544445454454644547445484454944550445514455244553445544455544556445574455844559445604456144562445634456444565445664456744568445694457044571445724457344574445754457644577445784457944580445814458244583445844458544586445874458844589445904459144592445934459444595445964459744598445994460044601446024460344604446054460644607446084460944610446114461244613446144461544616446174461844619446204462144622446234462444625446264462744628446294463044631446324463344634446354463644637446384463944640446414464244643446444464544646446474464844649446504465144652446534465444655446564465744658446594466044661446624466344664446654466644667446684466944670446714467244673446744467544676446774467844679446804468144682446834468444685446864468744688446894469044691446924469344694446954469644697446984469944700447014470244703447044470544706447074470844709447104471144712447134471444715447164471744718447194472044721447224472344724447254472644727447284472944730447314473244733447344473544736447374473844739447404474144742447434474444745447464474744748447494475044751447524475344754447554475644757447584475944760447614476244763447644476544766447674476844769447704477144772447734477444775447764477744778447794478044781447824478344784447854478644787447884478944790447914479244793447944479544796447974479844799448004480144802448034480444805448064480744808448094481044811448124481344814448154481644817448184481944820448214482244823448244482544826448274482844829448304483144832448334483444835448364483744838448394484044841448424484344844448454484644847448484484944850448514485244853448544485544856448574485844859448604486144862448634486444865448664486744868448694487044871448724487344874448754487644877448784487944880448814488244883448844488544886448874488844889448904489144892448934489444895448964489744898448994490044901449024490344904449054490644907449084490944910449114491244913449144491544916449174491844919449204492144922449234492444925449264492744928449294493044931449324493344934449354493644937449384493944940449414494244943449444494544946449474494844949449504495144952449534495444955449564495744958449594496044961449624496344964449654496644967449684496944970449714497244973449744497544976449774497844979449804498144982449834498444985449864498744988449894499044991449924499344994449954499644997449984499945000450014500245003450044500545006450074500845009450104501145012450134501445015450164501745018450194502045021450224502345024450254502645027450284502945030450314503245033450344503545036450374503845039450404504145042450434504445045450464504745048450494505045051450524505345054450554505645057450584505945060450614506245063450644506545066450674506845069450704507145072450734507445075450764507745078450794508045081450824508345084450854508645087450884508945090450914509245093450944509545096450974509845099451004510145102451034510445105451064510745108451094511045111451124511345114451154511645117451184511945120451214512245123451244512545126451274512845129451304513145132451334513445135451364513745138451394514045141451424514345144451454514645147451484514945150451514515245153451544515545156451574515845159451604516145162451634516445165451664516745168451694517045171451724517345174451754517645177451784517945180451814518245183451844518545186451874518845189451904519145192451934519445195451964519745198451994520045201452024520345204452054520645207452084520945210452114521245213452144521545216452174521845219452204522145222452234522445225452264522745228452294523045231452324523345234452354523645237452384523945240452414524245243452444524545246452474524845249452504525145252452534525445255452564525745258452594526045261452624526345264452654526645267452684526945270452714527245273452744527545276452774527845279452804528145282452834528445285452864528745288452894529045291452924529345294452954529645297452984529945300453014530245303453044530545306453074530845309453104531145312453134531445315453164531745318453194532045321453224532345324453254532645327453284532945330453314533245333453344533545336453374533845339453404534145342453434534445345453464534745348453494535045351453524535345354453554535645357453584535945360453614536245363453644536545366453674536845369453704537145372453734537445375453764537745378453794538045381453824538345384453854538645387453884538945390453914539245393453944539545396453974539845399454004540145402454034540445405454064540745408454094541045411454124541345414454154541645417454184541945420454214542245423454244542545426454274542845429454304543145432454334543445435454364543745438454394544045441454424544345444454454544645447454484544945450454514545245453454544545545456454574545845459454604546145462454634546445465454664546745468454694547045471454724547345474454754547645477454784547945480454814548245483454844548545486454874548845489454904549145492454934549445495454964549745498454994550045501455024550345504455054550645507455084550945510455114551245513455144551545516455174551845519455204552145522455234552445525455264552745528455294553045531455324553345534455354553645537455384553945540455414554245543455444554545546455474554845549455504555145552455534555445555455564555745558455594556045561455624556345564455654556645567455684556945570455714557245573455744557545576455774557845579455804558145582455834558445585455864558745588455894559045591455924559345594455954559645597455984559945600456014560245603456044560545606456074560845609456104561145612456134561445615456164561745618456194562045621456224562345624456254562645627456284562945630456314563245633456344563545636456374563845639456404564145642456434564445645456464564745648456494565045651456524565345654456554565645657456584565945660456614566245663456644566545666456674566845669456704567145672456734567445675456764567745678456794568045681456824568345684456854568645687456884568945690456914569245693456944569545696456974569845699457004570145702457034570445705457064570745708457094571045711457124571345714457154571645717457184571945720457214572245723457244572545726457274572845729457304573145732457334573445735457364573745738457394574045741457424574345744457454574645747457484574945750457514575245753457544575545756457574575845759457604576145762457634576445765457664576745768457694577045771457724577345774457754577645777457784577945780457814578245783457844578545786457874578845789457904579145792457934579445795457964579745798457994580045801458024580345804458054580645807458084580945810458114581245813458144581545816458174581845819458204582145822458234582445825458264582745828458294583045831458324583345834458354583645837458384583945840458414584245843458444584545846458474584845849458504585145852458534585445855458564585745858458594586045861458624586345864458654586645867458684586945870458714587245873458744587545876458774587845879458804588145882458834588445885458864588745888458894589045891458924589345894458954589645897458984589945900459014590245903459044590545906459074590845909459104591145912459134591445915459164591745918459194592045921459224592345924459254592645927459284592945930459314593245933459344593545936459374593845939459404594145942459434594445945459464594745948459494595045951459524595345954459554595645957459584595945960459614596245963459644596545966459674596845969459704597145972459734597445975459764597745978459794598045981459824598345984459854598645987459884598945990459914599245993459944599545996459974599845999460004600146002460034600446005460064600746008460094601046011460124601346014460154601646017460184601946020460214602246023460244602546026460274602846029460304603146032460334603446035460364603746038460394604046041460424604346044460454604646047460484604946050460514605246053460544605546056460574605846059460604606146062460634606446065460664606746068460694607046071460724607346074460754607646077460784607946080460814608246083460844608546086460874608846089460904609146092460934609446095460964609746098460994610046101461024610346104461054610646107461084610946110461114611246113461144611546116461174611846119461204612146122461234612446125461264612746128461294613046131461324613346134461354613646137461384613946140461414614246143461444614546146461474614846149461504615146152461534615446155461564615746158461594616046161461624616346164461654616646167461684616946170461714617246173461744617546176461774617846179461804618146182461834618446185461864618746188461894619046191461924619346194461954619646197461984619946200462014620246203462044620546206462074620846209462104621146212462134621446215462164621746218462194622046221462224622346224462254622646227462284622946230462314623246233462344623546236462374623846239462404624146242462434624446245462464624746248462494625046251462524625346254462554625646257462584625946260462614626246263462644626546266462674626846269462704627146272462734627446275462764627746278462794628046281462824628346284462854628646287462884628946290462914629246293462944629546296462974629846299463004630146302463034630446305463064630746308463094631046311463124631346314463154631646317463184631946320463214632246323463244632546326463274632846329463304633146332463334633446335463364633746338463394634046341463424634346344463454634646347463484634946350463514635246353463544635546356463574635846359463604636146362463634636446365463664636746368463694637046371463724637346374463754637646377463784637946380463814638246383463844638546386463874638846389463904639146392463934639446395463964639746398463994640046401464024640346404464054640646407464084640946410464114641246413464144641546416464174641846419464204642146422464234642446425464264642746428464294643046431464324643346434464354643646437464384643946440464414644246443464444644546446464474644846449464504645146452464534645446455464564645746458464594646046461464624646346464464654646646467464684646946470464714647246473464744647546476464774647846479464804648146482464834648446485464864648746488464894649046491464924649346494464954649646497464984649946500465014650246503465044650546506465074650846509465104651146512465134651446515465164651746518465194652046521465224652346524465254652646527465284652946530465314653246533465344653546536465374653846539465404654146542465434654446545465464654746548465494655046551465524655346554465554655646557465584655946560465614656246563465644656546566465674656846569465704657146572465734657446575465764657746578465794658046581465824658346584465854658646587465884658946590465914659246593465944659546596465974659846599466004660146602466034660446605466064660746608466094661046611466124661346614466154661646617466184661946620466214662246623466244662546626466274662846629466304663146632466334663446635466364663746638466394664046641466424664346644466454664646647466484664946650466514665246653466544665546656466574665846659466604666146662466634666446665466664666746668466694667046671466724667346674466754667646677466784667946680466814668246683466844668546686466874668846689466904669146692466934669446695466964669746698466994670046701467024670346704467054670646707467084670946710467114671246713467144671546716467174671846719467204672146722467234672446725467264672746728467294673046731467324673346734467354673646737467384673946740467414674246743467444674546746467474674846749467504675146752467534675446755467564675746758467594676046761467624676346764467654676646767467684676946770467714677246773467744677546776467774677846779467804678146782467834678446785467864678746788467894679046791467924679346794467954679646797467984679946800468014680246803468044680546806468074680846809468104681146812468134681446815468164681746818468194682046821468224682346824468254682646827468284682946830468314683246833468344683546836468374683846839468404684146842468434684446845468464684746848468494685046851468524685346854468554685646857468584685946860468614686246863468644686546866468674686846869468704687146872468734687446875468764687746878468794688046881468824688346884468854688646887468884688946890468914689246893468944689546896468974689846899469004690146902469034690446905469064690746908469094691046911469124691346914469154691646917469184691946920469214692246923469244692546926469274692846929469304693146932469334693446935469364693746938469394694046941469424694346944469454694646947469484694946950469514695246953469544695546956469574695846959469604696146962469634696446965469664696746968469694697046971469724697346974469754697646977469784697946980469814698246983469844698546986469874698846989469904699146992469934699446995469964699746998469994700047001470024700347004470054700647007470084700947010470114701247013470144701547016470174701847019470204702147022470234702447025470264702747028470294703047031470324703347034470354703647037470384703947040470414704247043470444704547046470474704847049470504705147052470534705447055470564705747058470594706047061470624706347064470654706647067470684706947070470714707247073470744707547076470774707847079470804708147082470834708447085470864708747088470894709047091470924709347094470954709647097470984709947100471014710247103471044710547106471074710847109471104711147112471134711447115471164711747118471194712047121471224712347124471254712647127471284712947130471314713247133471344713547136471374713847139471404714147142471434714447145471464714747148471494715047151471524715347154471554715647157471584715947160471614716247163471644716547166471674716847169471704717147172471734717447175471764717747178471794718047181471824718347184471854718647187471884718947190471914719247193471944719547196471974719847199472004720147202472034720447205472064720747208472094721047211472124721347214472154721647217472184721947220472214722247223472244722547226472274722847229472304723147232472334723447235472364723747238472394724047241472424724347244472454724647247472484724947250472514725247253472544725547256472574725847259472604726147262472634726447265472664726747268472694727047271472724727347274472754727647277472784727947280472814728247283472844728547286472874728847289472904729147292472934729447295472964729747298472994730047301473024730347304473054730647307473084730947310473114731247313473144731547316473174731847319473204732147322473234732447325473264732747328473294733047331473324733347334473354733647337473384733947340473414734247343473444734547346473474734847349473504735147352473534735447355473564735747358473594736047361473624736347364473654736647367473684736947370473714737247373473744737547376473774737847379473804738147382473834738447385473864738747388473894739047391473924739347394473954739647397473984739947400474014740247403474044740547406474074740847409474104741147412474134741447415474164741747418474194742047421474224742347424474254742647427474284742947430474314743247433474344743547436474374743847439474404744147442474434744447445474464744747448474494745047451474524745347454474554745647457474584745947460474614746247463474644746547466474674746847469474704747147472474734747447475474764747747478474794748047481474824748347484474854748647487474884748947490474914749247493474944749547496474974749847499475004750147502475034750447505475064750747508475094751047511475124751347514475154751647517475184751947520475214752247523475244752547526475274752847529475304753147532475334753447535475364753747538475394754047541475424754347544475454754647547475484754947550475514755247553475544755547556475574755847559475604756147562475634756447565475664756747568475694757047571475724757347574475754757647577475784757947580475814758247583475844758547586475874758847589475904759147592475934759447595475964759747598475994760047601476024760347604476054760647607476084760947610476114761247613476144761547616476174761847619476204762147622476234762447625476264762747628476294763047631476324763347634476354763647637476384763947640476414764247643476444764547646476474764847649476504765147652476534765447655476564765747658476594766047661476624766347664476654766647667476684766947670476714767247673476744767547676476774767847679476804768147682476834768447685476864768747688476894769047691476924769347694476954769647697476984769947700477014770247703477044770547706477074770847709477104771147712477134771447715477164771747718477194772047721477224772347724477254772647727477284772947730477314773247733477344773547736477374773847739477404774147742477434774447745477464774747748477494775047751477524775347754477554775647757477584775947760477614776247763477644776547766477674776847769477704777147772477734777447775477764777747778477794778047781477824778347784477854778647787477884778947790477914779247793477944779547796477974779847799478004780147802478034780447805478064780747808478094781047811478124781347814478154781647817478184781947820478214782247823478244782547826478274782847829478304783147832478334783447835478364783747838478394784047841478424784347844478454784647847478484784947850478514785247853478544785547856478574785847859478604786147862478634786447865478664786747868478694787047871478724787347874478754787647877478784787947880478814788247883478844788547886478874788847889478904789147892478934789447895478964789747898478994790047901479024790347904479054790647907479084790947910479114791247913479144791547916479174791847919479204792147922479234792447925479264792747928479294793047931479324793347934479354793647937479384793947940479414794247943479444794547946479474794847949479504795147952479534795447955479564795747958479594796047961479624796347964479654796647967479684796947970479714797247973479744797547976479774797847979479804798147982479834798447985479864798747988479894799047991479924799347994479954799647997479984799948000480014800248003480044800548006480074800848009480104801148012480134801448015480164801748018480194802048021480224802348024480254802648027480284802948030480314803248033480344803548036480374803848039480404804148042480434804448045480464804748048480494805048051480524805348054480554805648057480584805948060480614806248063480644806548066480674806848069480704807148072480734807448075480764807748078480794808048081480824808348084480854808648087480884808948090480914809248093480944809548096480974809848099481004810148102481034810448105481064810748108481094811048111481124811348114481154811648117481184811948120481214812248123481244812548126481274812848129481304813148132481334813448135481364813748138481394814048141481424814348144481454814648147481484814948150481514815248153481544815548156481574815848159481604816148162481634816448165481664816748168481694817048171481724817348174481754817648177481784817948180481814818248183481844818548186481874818848189481904819148192481934819448195481964819748198481994820048201482024820348204482054820648207482084820948210482114821248213482144821548216482174821848219482204822148222482234822448225482264822748228482294823048231482324823348234482354823648237482384823948240482414824248243482444824548246482474824848249482504825148252482534825448255482564825748258482594826048261482624826348264482654826648267482684826948270482714827248273482744827548276482774827848279482804828148282482834828448285482864828748288482894829048291482924829348294482954829648297482984829948300483014830248303483044830548306483074830848309483104831148312483134831448315483164831748318483194832048321483224832348324483254832648327483284832948330483314833248333483344833548336483374833848339483404834148342483434834448345483464834748348483494835048351483524835348354483554835648357483584835948360483614836248363483644836548366483674836848369483704837148372483734837448375483764837748378483794838048381483824838348384483854838648387483884838948390483914839248393483944839548396483974839848399484004840148402484034840448405484064840748408484094841048411484124841348414484154841648417484184841948420484214842248423484244842548426484274842848429484304843148432484334843448435484364843748438484394844048441484424844348444484454844648447484484844948450484514845248453484544845548456484574845848459484604846148462484634846448465484664846748468484694847048471484724847348474484754847648477484784847948480484814848248483484844848548486484874848848489484904849148492484934849448495484964849748498484994850048501485024850348504485054850648507485084850948510485114851248513485144851548516485174851848519485204852148522485234852448525485264852748528485294853048531485324853348534485354853648537485384853948540485414854248543485444854548546485474854848549485504855148552485534855448555485564855748558485594856048561485624856348564485654856648567485684856948570485714857248573485744857548576485774857848579485804858148582485834858448585485864858748588485894859048591485924859348594485954859648597485984859948600486014860248603486044860548606486074860848609486104861148612486134861448615486164861748618486194862048621486224862348624486254862648627486284862948630486314863248633486344863548636486374863848639486404864148642486434864448645486464864748648486494865048651486524865348654486554865648657486584865948660486614866248663486644866548666486674866848669486704867148672486734867448675486764867748678486794868048681486824868348684486854868648687486884868948690486914869248693486944869548696486974869848699487004870148702487034870448705487064870748708487094871048711487124871348714487154871648717487184871948720487214872248723487244872548726487274872848729487304873148732487334873448735487364873748738487394874048741487424874348744487454874648747487484874948750487514875248753487544875548756487574875848759487604876148762487634876448765487664876748768487694877048771487724877348774487754877648777487784877948780487814878248783487844878548786487874878848789487904879148792487934879448795487964879748798487994880048801488024880348804488054880648807488084880948810488114881248813488144881548816488174881848819488204882148822488234882448825488264882748828488294883048831488324883348834488354883648837488384883948840488414884248843488444884548846488474884848849488504885148852488534885448855488564885748858488594886048861488624886348864488654886648867488684886948870488714887248873488744887548876488774887848879488804888148882488834888448885488864888748888488894889048891488924889348894488954889648897488984889948900489014890248903489044890548906489074890848909489104891148912489134891448915489164891748918489194892048921489224892348924489254892648927489284892948930489314893248933489344893548936489374893848939489404894148942489434894448945489464894748948489494895048951489524895348954489554895648957489584895948960489614896248963489644896548966489674896848969489704897148972489734897448975489764897748978489794898048981489824898348984489854898648987489884898948990489914899248993489944899548996489974899848999490004900149002490034900449005490064900749008490094901049011490124901349014490154901649017490184901949020490214902249023490244902549026490274902849029490304903149032490334903449035490364903749038490394904049041490424904349044490454904649047490484904949050490514905249053490544905549056490574905849059490604906149062490634906449065490664906749068490694907049071490724907349074490754907649077490784907949080490814908249083490844908549086490874908849089490904909149092490934909449095490964909749098490994910049101491024910349104491054910649107491084910949110491114911249113491144911549116491174911849119491204912149122491234912449125491264912749128491294913049131491324913349134491354913649137491384913949140491414914249143491444914549146491474914849149491504915149152491534915449155491564915749158491594916049161491624916349164491654916649167491684916949170491714917249173491744917549176491774917849179491804918149182491834918449185491864918749188491894919049191491924919349194491954919649197491984919949200492014920249203492044920549206492074920849209492104921149212492134921449215492164921749218492194922049221492224922349224492254922649227492284922949230492314923249233492344923549236492374923849239492404924149242492434924449245492464924749248492494925049251492524925349254492554925649257492584925949260492614926249263492644926549266492674926849269492704927149272492734927449275492764927749278492794928049281492824928349284492854928649287492884928949290492914929249293492944929549296492974929849299493004930149302493034930449305493064930749308493094931049311493124931349314493154931649317493184931949320493214932249323493244932549326493274932849329493304933149332493334933449335493364933749338493394934049341493424934349344493454934649347493484934949350493514935249353493544935549356493574935849359493604936149362493634936449365493664936749368493694937049371493724937349374493754937649377493784937949380493814938249383493844938549386493874938849389493904939149392493934939449395493964939749398493994940049401494024940349404494054940649407494084940949410494114941249413494144941549416494174941849419494204942149422494234942449425494264942749428494294943049431494324943349434494354943649437494384943949440494414944249443494444944549446494474944849449494504945149452494534945449455494564945749458494594946049461494624946349464494654946649467494684946949470494714947249473494744947549476494774947849479494804948149482494834948449485494864948749488494894949049491494924949349494494954949649497494984949949500495014950249503495044950549506495074950849509495104951149512495134951449515495164951749518495194952049521495224952349524495254952649527495284952949530495314953249533495344953549536495374953849539495404954149542495434954449545495464954749548495494955049551495524955349554495554955649557495584955949560495614956249563495644956549566495674956849569495704957149572495734957449575495764957749578495794958049581495824958349584495854958649587495884958949590495914959249593495944959549596495974959849599496004960149602496034960449605496064960749608496094961049611496124961349614496154961649617496184961949620496214962249623496244962549626496274962849629496304963149632496334963449635496364963749638496394964049641496424964349644496454964649647496484964949650496514965249653496544965549656496574965849659496604966149662496634966449665496664966749668496694967049671496724967349674496754967649677496784967949680496814968249683496844968549686496874968849689496904969149692496934969449695496964969749698496994970049701497024970349704497054970649707497084970949710497114971249713497144971549716497174971849719497204972149722497234972449725497264972749728497294973049731497324973349734497354973649737497384973949740497414974249743497444974549746497474974849749497504975149752497534975449755497564975749758497594976049761497624976349764497654976649767497684976949770497714977249773497744977549776497774977849779497804978149782497834978449785497864978749788497894979049791497924979349794497954979649797497984979949800498014980249803498044980549806498074980849809498104981149812498134981449815498164981749818498194982049821498224982349824498254982649827498284982949830498314983249833498344983549836498374983849839498404984149842498434984449845498464984749848498494985049851498524985349854498554985649857498584985949860498614986249863498644986549866498674986849869498704987149872498734987449875498764987749878498794988049881498824988349884498854988649887498884988949890498914989249893498944989549896498974989849899499004990149902499034990449905499064990749908499094991049911499124991349914499154991649917499184991949920499214992249923499244992549926499274992849929499304993149932499334993449935499364993749938499394994049941499424994349944499454994649947499484994949950499514995249953499544995549956499574995849959499604996149962499634996449965499664996749968499694997049971499724997349974499754997649977499784997949980499814998249983499844998549986499874998849989499904999149992499934999449995499964999749998499995000050001500025000350004500055000650007500085000950010500115001250013500145001550016500175001850019500205002150022500235002450025500265002750028500295003050031500325003350034500355003650037500385003950040500415004250043500445004550046500475004850049500505005150052500535005450055500565005750058500595006050061500625006350064500655006650067500685006950070500715007250073500745007550076500775007850079500805008150082500835008450085500865008750088500895009050091500925009350094500955009650097500985009950100501015010250103501045010550106501075010850109501105011150112501135011450115501165011750118501195012050121501225012350124501255012650127501285012950130501315013250133501345013550136501375013850139501405014150142501435014450145501465014750148501495015050151501525015350154501555015650157501585015950160501615016250163501645016550166501675016850169501705017150172501735017450175501765017750178501795018050181501825018350184501855018650187501885018950190501915019250193501945019550196501975019850199502005020150202502035020450205502065020750208502095021050211502125021350214502155021650217502185021950220502215022250223502245022550226502275022850229502305023150232502335023450235502365023750238502395024050241502425024350244502455024650247502485024950250502515025250253502545025550256502575025850259502605026150262502635026450265502665026750268502695027050271502725027350274502755027650277502785027950280502815028250283502845028550286502875028850289502905029150292502935029450295502965029750298502995030050301503025030350304503055030650307503085030950310503115031250313503145031550316503175031850319503205032150322503235032450325503265032750328503295033050331503325033350334503355033650337503385033950340503415034250343503445034550346503475034850349503505035150352503535035450355503565035750358503595036050361503625036350364503655036650367503685036950370503715037250373503745037550376503775037850379503805038150382503835038450385503865038750388503895039050391503925039350394503955039650397503985039950400504015040250403504045040550406504075040850409504105041150412504135041450415504165041750418504195042050421504225042350424504255042650427504285042950430504315043250433504345043550436504375043850439504405044150442504435044450445504465044750448504495045050451504525045350454504555045650457504585045950460504615046250463504645046550466504675046850469504705047150472504735047450475504765047750478504795048050481504825048350484504855048650487504885048950490504915049250493504945049550496504975049850499505005050150502505035050450505505065050750508505095051050511505125051350514505155051650517505185051950520505215052250523505245052550526505275052850529505305053150532505335053450535505365053750538505395054050541505425054350544505455054650547505485054950550505515055250553505545055550556505575055850559505605056150562505635056450565505665056750568505695057050571505725057350574505755057650577505785057950580505815058250583505845058550586505875058850589505905059150592505935059450595505965059750598505995060050601506025060350604506055060650607506085060950610506115061250613506145061550616506175061850619506205062150622506235062450625506265062750628506295063050631506325063350634506355063650637506385063950640506415064250643506445064550646506475064850649506505065150652506535065450655506565065750658506595066050661506625066350664506655066650667506685066950670506715067250673506745067550676506775067850679506805068150682506835068450685506865068750688506895069050691506925069350694506955069650697506985069950700507015070250703507045070550706507075070850709507105071150712507135071450715507165071750718507195072050721507225072350724507255072650727507285072950730507315073250733507345073550736507375073850739507405074150742507435074450745507465074750748507495075050751507525075350754507555075650757507585075950760507615076250763507645076550766507675076850769507705077150772507735077450775507765077750778507795078050781507825078350784507855078650787507885078950790507915079250793507945079550796507975079850799508005080150802508035080450805508065080750808508095081050811508125081350814508155081650817508185081950820508215082250823508245082550826508275082850829508305083150832508335083450835508365083750838508395084050841508425084350844508455084650847508485084950850508515085250853508545085550856508575085850859508605086150862508635086450865508665086750868508695087050871508725087350874508755087650877508785087950880508815088250883508845088550886508875088850889508905089150892508935089450895508965089750898508995090050901509025090350904509055090650907509085090950910509115091250913509145091550916509175091850919509205092150922509235092450925509265092750928509295093050931509325093350934509355093650937509385093950940509415094250943509445094550946509475094850949509505095150952509535095450955509565095750958509595096050961509625096350964509655096650967509685096950970509715097250973509745097550976509775097850979509805098150982509835098450985509865098750988509895099050991509925099350994509955099650997509985099951000510015100251003510045100551006510075100851009510105101151012510135101451015510165101751018510195102051021510225102351024510255102651027510285102951030510315103251033510345103551036510375103851039510405104151042510435104451045510465104751048510495105051051510525105351054510555105651057510585105951060510615106251063510645106551066510675106851069510705107151072510735107451075510765107751078510795108051081510825108351084510855108651087510885108951090510915109251093510945109551096510975109851099511005110151102511035110451105511065110751108511095111051111511125111351114511155111651117511185111951120511215112251123511245112551126511275112851129511305113151132511335113451135511365113751138511395114051141511425114351144511455114651147511485114951150511515115251153511545115551156511575115851159511605116151162511635116451165511665116751168511695117051171511725117351174511755117651177511785117951180511815118251183511845118551186511875118851189511905119151192511935119451195511965119751198511995120051201512025120351204512055120651207512085120951210512115121251213512145121551216512175121851219512205122151222512235122451225512265122751228512295123051231512325123351234512355123651237512385123951240512415124251243512445124551246512475124851249512505125151252512535125451255512565125751258512595126051261512625126351264512655126651267512685126951270512715127251273512745127551276512775127851279512805128151282512835128451285512865128751288512895129051291512925129351294512955129651297512985129951300513015130251303513045130551306513075130851309513105131151312513135131451315513165131751318513195132051321513225132351324513255132651327513285132951330513315133251333513345133551336513375133851339513405134151342513435134451345513465134751348513495135051351513525135351354513555135651357513585135951360513615136251363513645136551366513675136851369513705137151372513735137451375513765137751378513795138051381513825138351384513855138651387513885138951390513915139251393513945139551396513975139851399514005140151402514035140451405514065140751408514095141051411514125141351414514155141651417514185141951420514215142251423514245142551426514275142851429514305143151432514335143451435514365143751438514395144051441514425144351444514455144651447514485144951450514515145251453514545145551456514575145851459514605146151462514635146451465514665146751468514695147051471514725147351474514755147651477514785147951480514815148251483514845148551486514875148851489514905149151492514935149451495514965149751498514995150051501515025150351504515055150651507515085150951510515115151251513515145151551516515175151851519515205152151522515235152451525515265152751528515295153051531515325153351534515355153651537515385153951540515415154251543515445154551546515475154851549515505155151552515535155451555515565155751558515595156051561515625156351564515655156651567515685156951570515715157251573515745157551576515775157851579515805158151582515835158451585515865158751588515895159051591515925159351594515955159651597515985159951600516015160251603516045160551606516075160851609516105161151612516135161451615516165161751618516195162051621516225162351624516255162651627516285162951630516315163251633516345163551636516375163851639516405164151642516435164451645516465164751648516495165051651516525165351654516555165651657516585165951660516615166251663516645166551666516675166851669516705167151672516735167451675516765167751678516795168051681516825168351684516855168651687516885168951690516915169251693516945169551696516975169851699517005170151702517035170451705517065170751708517095171051711517125171351714517155171651717517185171951720517215172251723517245172551726517275172851729517305173151732517335173451735517365173751738517395174051741517425174351744517455174651747517485174951750517515175251753517545175551756517575175851759517605176151762517635176451765517665176751768517695177051771517725177351774517755177651777517785177951780517815178251783517845178551786517875178851789517905179151792517935179451795517965179751798517995180051801518025180351804518055180651807518085180951810518115181251813518145181551816518175181851819518205182151822518235182451825518265182751828518295183051831518325183351834518355183651837518385183951840518415184251843518445184551846518475184851849518505185151852518535185451855518565185751858518595186051861518625186351864518655186651867518685186951870518715187251873518745187551876518775187851879518805188151882518835188451885518865188751888518895189051891518925189351894518955189651897518985189951900519015190251903519045190551906519075190851909519105191151912519135191451915519165191751918519195192051921519225192351924519255192651927519285192951930519315193251933519345193551936519375193851939519405194151942519435194451945519465194751948519495195051951519525195351954519555195651957519585195951960519615196251963519645196551966519675196851969519705197151972519735197451975519765197751978519795198051981519825198351984519855198651987519885198951990519915199251993519945199551996519975199851999520005200152002520035200452005520065200752008520095201052011520125201352014520155201652017520185201952020520215202252023520245202552026520275202852029520305203152032520335203452035520365203752038520395204052041520425204352044520455204652047520485204952050520515205252053520545205552056520575205852059520605206152062520635206452065520665206752068520695207052071520725207352074520755207652077520785207952080520815208252083520845208552086520875208852089520905209152092520935209452095520965209752098520995210052101521025210352104521055210652107521085210952110521115211252113521145211552116521175211852119521205212152122521235212452125521265212752128521295213052131521325213352134521355213652137521385213952140521415214252143521445214552146521475214852149521505215152152521535215452155521565215752158521595216052161521625216352164521655216652167521685216952170521715217252173521745217552176521775217852179521805218152182521835218452185521865218752188521895219052191521925219352194521955219652197521985219952200522015220252203522045220552206522075220852209522105221152212522135221452215522165221752218522195222052221522225222352224522255222652227522285222952230522315223252233522345223552236522375223852239522405224152242522435224452245522465224752248522495225052251522525225352254522555225652257522585225952260522615226252263522645226552266522675226852269522705227152272522735227452275522765227752278522795228052281522825228352284522855228652287522885228952290522915229252293522945229552296522975229852299523005230152302523035230452305523065230752308523095231052311523125231352314523155231652317523185231952320523215232252323523245232552326523275232852329523305233152332523335233452335523365233752338523395234052341523425234352344523455234652347523485234952350523515235252353523545235552356523575235852359523605236152362523635236452365523665236752368523695237052371523725237352374523755237652377523785237952380523815238252383523845238552386523875238852389523905239152392523935239452395523965239752398523995240052401524025240352404524055240652407524085240952410524115241252413524145241552416524175241852419524205242152422524235242452425524265242752428524295243052431524325243352434524355243652437524385243952440524415244252443524445244552446524475244852449524505245152452524535245452455524565245752458524595246052461524625246352464524655246652467524685246952470524715247252473524745247552476524775247852479524805248152482524835248452485524865248752488524895249052491524925249352494524955249652497524985249952500525015250252503525045250552506525075250852509525105251152512525135251452515525165251752518525195252052521525225252352524525255252652527525285252952530525315253252533525345253552536525375253852539525405254152542525435254452545525465254752548525495255052551525525255352554525555255652557525585255952560525615256252563525645256552566525675256852569525705257152572525735257452575525765257752578525795258052581525825258352584525855258652587525885258952590525915259252593525945259552596525975259852599526005260152602526035260452605526065260752608526095261052611526125261352614526155261652617526185261952620526215262252623526245262552626526275262852629526305263152632526335263452635526365263752638526395264052641526425264352644526455264652647526485264952650526515265252653526545265552656526575265852659526605266152662526635266452665526665266752668526695267052671526725267352674526755267652677526785267952680526815268252683526845268552686526875268852689526905269152692526935269452695526965269752698526995270052701527025270352704527055270652707527085270952710527115271252713527145271552716527175271852719527205272152722527235272452725527265272752728527295273052731527325273352734527355273652737527385273952740527415274252743527445274552746527475274852749527505275152752527535275452755527565275752758527595276052761527625276352764527655276652767527685276952770527715277252773527745277552776527775277852779527805278152782527835278452785527865278752788527895279052791527925279352794527955279652797527985279952800528015280252803528045280552806528075280852809528105281152812528135281452815528165281752818528195282052821528225282352824528255282652827528285282952830528315283252833528345283552836528375283852839528405284152842528435284452845528465284752848528495285052851528525285352854528555285652857528585285952860528615286252863528645286552866528675286852869528705287152872528735287452875528765287752878528795288052881528825288352884528855288652887528885288952890528915289252893528945289552896528975289852899529005290152902529035290452905529065290752908529095291052911529125291352914529155291652917529185291952920529215292252923529245292552926529275292852929529305293152932529335293452935529365293752938529395294052941529425294352944529455294652947529485294952950529515295252953529545295552956529575295852959529605296152962529635296452965529665296752968529695297052971529725297352974529755297652977529785297952980529815298252983529845298552986529875298852989529905299152992529935299452995529965299752998529995300053001530025300353004530055300653007530085300953010530115301253013530145301553016530175301853019530205302153022530235302453025530265302753028530295303053031530325303353034530355303653037530385303953040530415304253043530445304553046530475304853049530505305153052530535305453055530565305753058530595306053061530625306353064530655306653067530685306953070530715307253073530745307553076530775307853079530805308153082530835308453085530865308753088530895309053091530925309353094530955309653097530985309953100531015310253103531045310553106531075310853109531105311153112531135311453115531165311753118531195312053121531225312353124531255312653127531285312953130531315313253133531345313553136531375313853139531405314153142531435314453145531465314753148531495315053151531525315353154531555315653157531585315953160531615316253163531645316553166531675316853169531705317153172531735317453175531765317753178531795318053181531825318353184531855318653187531885318953190531915319253193531945319553196531975319853199532005320153202532035320453205532065320753208532095321053211532125321353214532155321653217532185321953220532215322253223532245322553226532275322853229532305323153232532335323453235532365323753238532395324053241532425324353244532455324653247532485324953250532515325253253532545325553256532575325853259532605326153262532635326453265532665326753268532695327053271532725327353274532755327653277532785327953280532815328253283532845328553286532875328853289532905329153292532935329453295532965329753298532995330053301533025330353304533055330653307533085330953310533115331253313533145331553316533175331853319533205332153322533235332453325533265332753328533295333053331533325333353334533355333653337533385333953340533415334253343533445334553346533475334853349533505335153352533535335453355533565335753358533595336053361533625336353364533655336653367533685336953370533715337253373533745337553376533775337853379533805338153382533835338453385533865338753388533895339053391533925339353394533955339653397533985339953400534015340253403534045340553406534075340853409534105341153412534135341453415534165341753418534195342053421534225342353424534255342653427534285342953430534315343253433534345343553436534375343853439534405344153442534435344453445534465344753448534495345053451534525345353454534555345653457534585345953460534615346253463534645346553466534675346853469534705347153472534735347453475534765347753478534795348053481534825348353484534855348653487534885348953490534915349253493534945349553496534975349853499535005350153502535035350453505535065350753508535095351053511535125351353514535155351653517535185351953520535215352253523535245352553526535275352853529535305353153532535335353453535535365353753538535395354053541535425354353544535455354653547535485354953550535515355253553535545355553556535575355853559535605356153562535635356453565535665356753568535695357053571535725357353574535755357653577535785357953580535815358253583535845358553586535875358853589535905359153592535935359453595535965359753598535995360053601536025360353604536055360653607536085360953610536115361253613536145361553616536175361853619536205362153622536235362453625536265362753628536295363053631536325363353634536355363653637536385363953640536415364253643536445364553646536475364853649536505365153652536535365453655536565365753658536595366053661536625366353664536655366653667536685366953670536715367253673536745367553676536775367853679536805368153682536835368453685536865368753688536895369053691536925369353694536955369653697536985369953700537015370253703537045370553706537075370853709537105371153712537135371453715537165371753718537195372053721537225372353724537255372653727537285372953730537315373253733537345373553736537375373853739537405374153742537435374453745537465374753748537495375053751537525375353754537555375653757537585375953760537615376253763537645376553766537675376853769537705377153772537735377453775537765377753778537795378053781537825378353784537855378653787537885378953790537915379253793537945379553796537975379853799538005380153802538035380453805538065380753808538095381053811538125381353814538155381653817538185381953820538215382253823538245382553826538275382853829538305383153832538335383453835538365383753838538395384053841538425384353844538455384653847538485384953850538515385253853538545385553856538575385853859538605386153862538635386453865538665386753868538695387053871538725387353874538755387653877538785387953880538815388253883538845388553886538875388853889538905389153892538935389453895538965389753898538995390053901539025390353904539055390653907539085390953910539115391253913539145391553916539175391853919539205392153922539235392453925539265392753928539295393053931539325393353934539355393653937539385393953940539415394253943539445394553946539475394853949539505395153952539535395453955539565395753958539595396053961539625396353964539655396653967539685396953970539715397253973539745397553976539775397853979539805398153982539835398453985539865398753988539895399053991539925399353994539955399653997539985399954000540015400254003540045400554006540075400854009540105401154012540135401454015540165401754018540195402054021540225402354024540255402654027540285402954030540315403254033540345403554036540375403854039540405404154042540435404454045540465404754048540495405054051540525405354054540555405654057540585405954060540615406254063540645406554066540675406854069540705407154072540735407454075540765407754078540795408054081540825408354084540855408654087540885408954090540915409254093540945409554096540975409854099541005410154102541035410454105541065410754108541095411054111541125411354114541155411654117541185411954120541215412254123541245412554126541275412854129541305413154132541335413454135541365413754138541395414054141541425414354144541455414654147541485414954150541515415254153541545415554156541575415854159541605416154162541635416454165541665416754168541695417054171541725417354174541755417654177541785417954180541815418254183541845418554186541875418854189541905419154192541935419454195541965419754198541995420054201542025420354204542055420654207542085420954210542115421254213542145421554216542175421854219542205422154222542235422454225542265422754228542295423054231542325423354234542355423654237542385423954240542415424254243542445424554246542475424854249542505425154252542535425454255542565425754258542595426054261542625426354264542655426654267542685426954270542715427254273542745427554276542775427854279542805428154282542835428454285542865428754288542895429054291542925429354294542955429654297542985429954300543015430254303543045430554306543075430854309543105431154312543135431454315543165431754318543195432054321543225432354324543255432654327543285432954330543315433254333543345433554336543375433854339543405434154342543435434454345543465434754348543495435054351543525435354354543555435654357543585435954360543615436254363543645436554366543675436854369543705437154372543735437454375543765437754378543795438054381543825438354384543855438654387543885438954390543915439254393543945439554396543975439854399544005440154402544035440454405544065440754408544095441054411544125441354414544155441654417544185441954420544215442254423544245442554426544275442854429544305443154432544335443454435544365443754438544395444054441544425444354444544455444654447544485444954450544515445254453544545445554456544575445854459544605446154462544635446454465544665446754468544695447054471544725447354474544755447654477544785447954480544815448254483544845448554486544875448854489544905449154492544935449454495544965449754498544995450054501545025450354504545055450654507545085450954510545115451254513545145451554516545175451854519545205452154522545235452454525545265452754528545295453054531545325453354534545355453654537545385453954540545415454254543545445454554546545475454854549545505455154552545535455454555545565455754558545595456054561545625456354564545655456654567545685456954570545715457254573545745457554576545775457854579545805458154582545835458454585545865458754588545895459054591545925459354594545955459654597545985459954600546015460254603546045460554606546075460854609546105461154612546135461454615546165461754618546195462054621546225462354624546255462654627546285462954630546315463254633546345463554636546375463854639546405464154642546435464454645546465464754648546495465054651546525465354654546555465654657546585465954660546615466254663546645466554666546675466854669546705467154672546735467454675546765467754678546795468054681546825468354684546855468654687546885468954690546915469254693546945469554696546975469854699547005470154702547035470454705547065470754708547095471054711547125471354714547155471654717547185471954720547215472254723547245472554726547275472854729547305473154732547335473454735547365473754738547395474054741547425474354744547455474654747547485474954750547515475254753547545475554756547575475854759547605476154762547635476454765547665476754768547695477054771547725477354774547755477654777547785477954780547815478254783547845478554786547875478854789547905479154792547935479454795547965479754798547995480054801548025480354804548055480654807548085480954810548115481254813548145481554816548175481854819548205482154822548235482454825548265482754828548295483054831548325483354834548355483654837548385483954840548415484254843548445484554846548475484854849548505485154852548535485454855548565485754858548595486054861548625486354864548655486654867548685486954870548715487254873548745487554876548775487854879548805488154882548835488454885548865488754888548895489054891548925489354894548955489654897548985489954900549015490254903549045490554906549075490854909549105491154912549135491454915549165491754918549195492054921549225492354924549255492654927549285492954930549315493254933549345493554936549375493854939549405494154942549435494454945549465494754948549495495054951549525495354954549555495654957549585495954960549615496254963549645496554966549675496854969549705497154972549735497454975549765497754978549795498054981549825498354984549855498654987549885498954990549915499254993549945499554996549975499854999550005500155002550035500455005550065500755008550095501055011550125501355014550155501655017550185501955020550215502255023550245502555026550275502855029550305503155032550335503455035550365503755038550395504055041550425504355044550455504655047550485504955050550515505255053550545505555056550575505855059550605506155062550635506455065550665506755068550695507055071550725507355074550755507655077550785507955080550815508255083550845508555086550875508855089550905509155092550935509455095550965509755098550995510055101551025510355104551055510655107551085510955110551115511255113551145511555116551175511855119551205512155122551235512455125551265512755128551295513055131551325513355134551355513655137551385513955140551415514255143551445514555146551475514855149551505515155152551535515455155551565515755158551595516055161551625516355164551655516655167551685516955170551715517255173551745517555176551775517855179551805518155182551835518455185551865518755188551895519055191551925519355194551955519655197551985519955200552015520255203552045520555206552075520855209552105521155212552135521455215552165521755218552195522055221552225522355224552255522655227552285522955230552315523255233552345523555236552375523855239552405524155242552435524455245552465524755248552495525055251552525525355254552555525655257552585525955260552615526255263552645526555266552675526855269552705527155272552735527455275552765527755278552795528055281552825528355284552855528655287552885528955290552915529255293552945529555296552975529855299553005530155302553035530455305553065530755308553095531055311553125531355314553155531655317553185531955320553215532255323553245532555326553275532855329553305533155332553335533455335553365533755338553395534055341553425534355344553455534655347553485534955350553515535255353553545535555356553575535855359553605536155362553635536455365553665536755368553695537055371553725537355374553755537655377553785537955380553815538255383553845538555386553875538855389553905539155392553935539455395553965539755398553995540055401554025540355404554055540655407554085540955410554115541255413554145541555416554175541855419554205542155422554235542455425554265542755428554295543055431554325543355434554355543655437554385543955440554415544255443554445544555446554475544855449554505545155452554535545455455554565545755458554595546055461554625546355464554655546655467554685546955470554715547255473554745547555476554775547855479554805548155482554835548455485554865548755488554895549055491554925549355494554955549655497554985549955500555015550255503555045550555506555075550855509555105551155512555135551455515555165551755518555195552055521555225552355524555255552655527555285552955530555315553255533555345553555536555375553855539555405554155542555435554455545555465554755548555495555055551555525555355554555555555655557555585555955560555615556255563555645556555566555675556855569555705557155572555735557455575555765557755578555795558055581555825558355584555855558655587555885558955590555915559255593555945559555596555975559855599556005560155602556035560455605556065560755608556095561055611556125561355614556155561655617556185561955620556215562255623556245562555626556275562855629556305563155632556335563455635556365563755638556395564055641556425564355644556455564655647556485564955650556515565255653556545565555656556575565855659556605566155662556635566455665556665566755668556695567055671556725567355674556755567655677556785567955680556815568255683556845568555686556875568855689556905569155692556935569455695556965569755698556995570055701557025570355704557055570655707557085570955710557115571255713557145571555716557175571855719557205572155722557235572455725557265572755728557295573055731557325573355734557355573655737557385573955740557415574255743557445574555746557475574855749557505575155752557535575455755557565575755758557595576055761557625576355764557655576655767557685576955770557715577255773557745577555776557775577855779557805578155782557835578455785557865578755788557895579055791557925579355794557955579655797557985579955800558015580255803558045580555806558075580855809558105581155812558135581455815558165581755818558195582055821558225582355824558255582655827558285582955830558315583255833558345583555836558375583855839558405584155842558435584455845558465584755848558495585055851558525585355854558555585655857558585585955860558615586255863558645586555866558675586855869558705587155872558735587455875558765587755878558795588055881558825588355884558855588655887558885588955890558915589255893558945589555896558975589855899559005590155902559035590455905559065590755908559095591055911559125591355914559155591655917559185591955920559215592255923559245592555926559275592855929559305593155932559335593455935559365593755938559395594055941559425594355944559455594655947559485594955950559515595255953559545595555956559575595855959559605596155962559635596455965559665596755968559695597055971559725597355974559755597655977559785597955980559815598255983559845598555986559875598855989559905599155992559935599455995559965599755998559995600056001560025600356004560055600656007560085600956010560115601256013560145601556016560175601856019560205602156022560235602456025560265602756028560295603056031560325603356034560355603656037560385603956040560415604256043560445604556046560475604856049560505605156052560535605456055560565605756058560595606056061560625606356064560655606656067560685606956070560715607256073560745607556076560775607856079560805608156082560835608456085560865608756088560895609056091560925609356094560955609656097560985609956100561015610256103561045610556106561075610856109561105611156112561135611456115561165611756118561195612056121561225612356124561255612656127561285612956130561315613256133561345613556136561375613856139561405614156142561435614456145561465614756148561495615056151561525615356154561555615656157561585615956160561615616256163561645616556166561675616856169561705617156172561735617456175561765617756178561795618056181561825618356184561855618656187561885618956190561915619256193561945619556196561975619856199562005620156202562035620456205562065620756208562095621056211562125621356214562155621656217562185621956220562215622256223562245622556226562275622856229562305623156232562335623456235562365623756238562395624056241562425624356244562455624656247562485624956250562515625256253562545625556256562575625856259562605626156262562635626456265562665626756268562695627056271562725627356274562755627656277562785627956280562815628256283562845628556286562875628856289562905629156292562935629456295562965629756298562995630056301563025630356304563055630656307563085630956310563115631256313563145631556316563175631856319563205632156322563235632456325563265632756328563295633056331563325633356334563355633656337563385633956340563415634256343563445634556346563475634856349563505635156352563535635456355563565635756358563595636056361563625636356364563655636656367563685636956370563715637256373563745637556376563775637856379563805638156382563835638456385563865638756388563895639056391563925639356394563955639656397563985639956400564015640256403564045640556406564075640856409564105641156412564135641456415564165641756418564195642056421564225642356424564255642656427564285642956430564315643256433564345643556436564375643856439564405644156442564435644456445564465644756448564495645056451564525645356454564555645656457564585645956460564615646256463564645646556466564675646856469564705647156472564735647456475564765647756478564795648056481564825648356484564855648656487564885648956490564915649256493564945649556496564975649856499565005650156502565035650456505565065650756508565095651056511565125651356514565155651656517565185651956520565215652256523565245652556526565275652856529565305653156532565335653456535565365653756538565395654056541565425654356544565455654656547565485654956550565515655256553565545655556556565575655856559565605656156562565635656456565565665656756568565695657056571565725657356574565755657656577565785657956580565815658256583565845658556586565875658856589565905659156592565935659456595565965659756598565995660056601566025660356604566055660656607566085660956610566115661256613566145661556616566175661856619566205662156622566235662456625566265662756628566295663056631566325663356634566355663656637566385663956640566415664256643566445664556646566475664856649566505665156652566535665456655566565665756658566595666056661566625666356664566655666656667566685666956670566715667256673566745667556676566775667856679566805668156682566835668456685566865668756688566895669056691566925669356694566955669656697566985669956700567015670256703567045670556706567075670856709567105671156712567135671456715567165671756718567195672056721567225672356724567255672656727567285672956730567315673256733567345673556736567375673856739567405674156742567435674456745567465674756748567495675056751567525675356754567555675656757567585675956760567615676256763567645676556766567675676856769567705677156772567735677456775567765677756778567795678056781567825678356784567855678656787567885678956790567915679256793567945679556796567975679856799568005680156802568035680456805568065680756808568095681056811568125681356814568155681656817568185681956820568215682256823568245682556826568275682856829568305683156832568335683456835568365683756838568395684056841568425684356844568455684656847568485684956850568515685256853568545685556856568575685856859568605686156862568635686456865568665686756868568695687056871568725687356874568755687656877568785687956880568815688256883568845688556886568875688856889568905689156892568935689456895568965689756898568995690056901569025690356904569055690656907569085690956910569115691256913569145691556916569175691856919569205692156922569235692456925569265692756928569295693056931569325693356934569355693656937569385693956940569415694256943569445694556946569475694856949569505695156952569535695456955569565695756958569595696056961569625696356964569655696656967569685696956970569715697256973569745697556976569775697856979569805698156982569835698456985569865698756988569895699056991569925699356994569955699656997569985699957000570015700257003570045700557006570075700857009570105701157012570135701457015570165701757018570195702057021570225702357024570255702657027570285702957030570315703257033570345703557036570375703857039570405704157042570435704457045570465704757048570495705057051570525705357054570555705657057570585705957060570615706257063570645706557066570675706857069570705707157072570735707457075570765707757078570795708057081570825708357084570855708657087570885708957090570915709257093570945709557096570975709857099571005710157102571035710457105571065710757108571095711057111571125711357114571155711657117571185711957120571215712257123571245712557126571275712857129571305713157132571335713457135571365713757138571395714057141571425714357144571455714657147571485714957150571515715257153571545715557156571575715857159571605716157162571635716457165571665716757168571695717057171571725717357174571755717657177571785717957180571815718257183571845718557186571875718857189571905719157192571935719457195571965719757198571995720057201572025720357204572055720657207572085720957210572115721257213572145721557216572175721857219572205722157222572235722457225572265722757228572295723057231572325723357234572355723657237572385723957240572415724257243572445724557246572475724857249572505725157252572535725457255572565725757258572595726057261572625726357264572655726657267572685726957270572715727257273572745727557276572775727857279572805728157282572835728457285572865728757288572895729057291572925729357294572955729657297572985729957300573015730257303573045730557306573075730857309573105731157312573135731457315573165731757318573195732057321573225732357324573255732657327573285732957330573315733257333573345733557336573375733857339573405734157342573435734457345573465734757348573495735057351573525735357354573555735657357573585735957360573615736257363573645736557366573675736857369573705737157372573735737457375573765737757378573795738057381573825738357384573855738657387573885738957390573915739257393573945739557396573975739857399574005740157402574035740457405574065740757408574095741057411574125741357414574155741657417574185741957420574215742257423574245742557426574275742857429574305743157432574335743457435574365743757438574395744057441574425744357444574455744657447574485744957450574515745257453574545745557456574575745857459574605746157462574635746457465574665746757468574695747057471574725747357474574755747657477574785747957480574815748257483574845748557486574875748857489574905749157492574935749457495574965749757498574995750057501575025750357504575055750657507575085750957510575115751257513575145751557516575175751857519575205752157522575235752457525575265752757528575295753057531575325753357534575355753657537575385753957540575415754257543575445754557546575475754857549575505755157552575535755457555575565755757558575595756057561575625756357564575655756657567575685756957570575715757257573575745757557576575775757857579575805758157582575835758457585575865758757588575895759057591575925759357594575955759657597575985759957600576015760257603576045760557606576075760857609576105761157612576135761457615576165761757618576195762057621576225762357624576255762657627576285762957630576315763257633576345763557636576375763857639576405764157642576435764457645576465764757648576495765057651576525765357654576555765657657576585765957660576615766257663576645766557666576675766857669576705767157672576735767457675576765767757678576795768057681576825768357684576855768657687576885768957690576915769257693576945769557696576975769857699577005770157702577035770457705577065770757708577095771057711577125771357714577155771657717577185771957720577215772257723577245772557726577275772857729577305773157732577335773457735577365773757738577395774057741577425774357744577455774657747577485774957750577515775257753577545775557756577575775857759577605776157762577635776457765577665776757768577695777057771577725777357774577755777657777577785777957780577815778257783577845778557786577875778857789577905779157792577935779457795577965779757798577995780057801578025780357804578055780657807578085780957810578115781257813578145781557816578175781857819578205782157822578235782457825578265782757828578295783057831578325783357834578355783657837578385783957840578415784257843578445784557846578475784857849578505785157852578535785457855578565785757858578595786057861578625786357864578655786657867578685786957870578715787257873578745787557876578775787857879578805788157882578835788457885578865788757888578895789057891578925789357894578955789657897578985789957900579015790257903579045790557906579075790857909579105791157912579135791457915579165791757918579195792057921579225792357924579255792657927579285792957930579315793257933579345793557936579375793857939579405794157942579435794457945579465794757948579495795057951579525795357954579555795657957579585795957960579615796257963579645796557966579675796857969579705797157972579735797457975579765797757978579795798057981579825798357984579855798657987579885798957990579915799257993579945799557996579975799857999580005800158002580035800458005580065800758008580095801058011580125801358014580155801658017580185801958020580215802258023580245802558026580275802858029580305803158032580335803458035580365803758038580395804058041580425804358044580455804658047580485804958050580515805258053580545805558056580575805858059580605806158062580635806458065580665806758068580695807058071580725807358074580755807658077580785807958080580815808258083580845808558086580875808858089580905809158092580935809458095580965809758098580995810058101581025810358104581055810658107581085810958110581115811258113581145811558116581175811858119581205812158122581235812458125581265812758128581295813058131581325813358134581355813658137581385813958140581415814258143581445814558146581475814858149581505815158152581535815458155581565815758158581595816058161581625816358164581655816658167581685816958170581715817258173581745817558176581775817858179581805818158182581835818458185581865818758188581895819058191581925819358194581955819658197581985819958200582015820258203582045820558206582075820858209582105821158212582135821458215582165821758218582195822058221582225822358224582255822658227582285822958230582315823258233582345823558236582375823858239582405824158242582435824458245582465824758248582495825058251582525825358254582555825658257582585825958260582615826258263582645826558266582675826858269582705827158272582735827458275582765827758278582795828058281582825828358284582855828658287582885828958290582915829258293582945829558296582975829858299583005830158302583035830458305583065830758308583095831058311583125831358314583155831658317583185831958320583215832258323583245832558326583275832858329583305833158332583335833458335583365833758338583395834058341583425834358344583455834658347583485834958350583515835258353583545835558356583575835858359583605836158362583635836458365583665836758368583695837058371583725837358374583755837658377583785837958380583815838258383583845838558386583875838858389583905839158392583935839458395583965839758398583995840058401584025840358404584055840658407584085840958410584115841258413584145841558416584175841858419584205842158422584235842458425584265842758428584295843058431584325843358434584355843658437584385843958440584415844258443584445844558446584475844858449584505845158452584535845458455584565845758458584595846058461584625846358464584655846658467584685846958470584715847258473584745847558476584775847858479584805848158482584835848458485584865848758488584895849058491584925849358494584955849658497584985849958500585015850258503585045850558506585075850858509585105851158512585135851458515585165851758518585195852058521585225852358524585255852658527585285852958530585315853258533585345853558536585375853858539585405854158542585435854458545585465854758548585495855058551585525855358554585555855658557585585855958560585615856258563585645856558566585675856858569585705857158572585735857458575585765857758578585795858058581585825858358584585855858658587585885858958590585915859258593585945859558596585975859858599586005860158602586035860458605586065860758608586095861058611586125861358614586155861658617586185861958620586215862258623586245862558626586275862858629586305863158632586335863458635586365863758638586395864058641586425864358644586455864658647586485864958650586515865258653586545865558656586575865858659586605866158662586635866458665586665866758668586695867058671586725867358674586755867658677586785867958680586815868258683586845868558686586875868858689586905869158692586935869458695586965869758698586995870058701587025870358704587055870658707587085870958710587115871258713587145871558716587175871858719587205872158722587235872458725587265872758728587295873058731587325873358734587355873658737587385873958740587415874258743587445874558746587475874858749587505875158752587535875458755587565875758758587595876058761587625876358764587655876658767587685876958770587715877258773587745877558776587775877858779587805878158782587835878458785587865878758788587895879058791587925879358794587955879658797587985879958800588015880258803588045880558806588075880858809588105881158812588135881458815588165881758818588195882058821588225882358824588255882658827588285882958830588315883258833588345883558836588375883858839588405884158842588435884458845588465884758848588495885058851588525885358854588555885658857588585885958860588615886258863588645886558866588675886858869588705887158872588735887458875588765887758878588795888058881588825888358884588855888658887588885888958890588915889258893588945889558896588975889858899589005890158902589035890458905589065890758908589095891058911589125891358914589155891658917589185891958920589215892258923589245892558926589275892858929589305893158932589335893458935589365893758938589395894058941589425894358944589455894658947589485894958950589515895258953589545895558956589575895858959589605896158962589635896458965589665896758968589695897058971589725897358974589755897658977589785897958980589815898258983589845898558986589875898858989589905899158992589935899458995589965899758998589995900059001590025900359004590055900659007590085900959010590115901259013590145901559016590175901859019590205902159022590235902459025590265902759028590295903059031590325903359034590355903659037590385903959040590415904259043590445904559046590475904859049590505905159052590535905459055590565905759058590595906059061590625906359064590655906659067590685906959070590715907259073590745907559076590775907859079590805908159082590835908459085590865908759088590895909059091590925909359094590955909659097590985909959100591015910259103591045910559106591075910859109591105911159112591135911459115591165911759118591195912059121591225912359124591255912659127591285912959130591315913259133591345913559136591375913859139591405914159142591435914459145591465914759148591495915059151591525915359154591555915659157591585915959160591615916259163591645916559166591675916859169591705917159172591735917459175591765917759178591795918059181591825918359184591855918659187591885918959190591915919259193591945919559196591975919859199592005920159202592035920459205592065920759208592095921059211592125921359214592155921659217592185921959220592215922259223592245922559226592275922859229592305923159232592335923459235592365923759238592395924059241592425924359244592455924659247592485924959250592515925259253592545925559256592575925859259592605926159262592635926459265592665926759268592695927059271592725927359274592755927659277592785927959280592815928259283592845928559286592875928859289592905929159292592935929459295592965929759298592995930059301593025930359304593055930659307593085930959310593115931259313593145931559316593175931859319593205932159322593235932459325593265932759328593295933059331593325933359334593355933659337593385933959340593415934259343593445934559346593475934859349593505935159352593535935459355593565935759358593595936059361593625936359364593655936659367593685936959370593715937259373593745937559376593775937859379593805938159382593835938459385593865938759388593895939059391593925939359394593955939659397593985939959400594015940259403594045940559406594075940859409594105941159412594135941459415594165941759418594195942059421594225942359424594255942659427594285942959430594315943259433594345943559436594375943859439594405944159442594435944459445594465944759448594495945059451594525945359454594555945659457594585945959460594615946259463594645946559466594675946859469594705947159472594735947459475594765947759478594795948059481594825948359484594855948659487594885948959490594915949259493594945949559496594975949859499595005950159502595035950459505595065950759508595095951059511595125951359514595155951659517595185951959520595215952259523595245952559526595275952859529595305953159532595335953459535595365953759538595395954059541595425954359544595455954659547595485954959550595515955259553595545955559556595575955859559595605956159562595635956459565595665956759568595695957059571595725957359574595755957659577595785957959580595815958259583595845958559586595875958859589595905959159592595935959459595595965959759598595995960059601596025960359604596055960659607596085960959610596115961259613596145961559616596175961859619596205962159622596235962459625596265962759628596295963059631596325963359634596355963659637596385963959640596415964259643596445964559646596475964859649596505965159652596535965459655596565965759658596595966059661596625966359664596655966659667596685966959670596715967259673596745967559676596775967859679596805968159682596835968459685596865968759688596895969059691596925969359694596955969659697596985969959700597015970259703597045970559706597075970859709597105971159712597135971459715597165971759718597195972059721597225972359724597255972659727597285972959730597315973259733597345973559736597375973859739597405974159742597435974459745597465974759748597495975059751597525975359754597555975659757597585975959760597615976259763597645976559766597675976859769597705977159772597735977459775597765977759778597795978059781597825978359784597855978659787597885978959790597915979259793597945979559796597975979859799598005980159802598035980459805598065980759808598095981059811598125981359814598155981659817598185981959820598215982259823598245982559826598275982859829598305983159832598335983459835598365983759838598395984059841598425984359844598455984659847598485984959850598515985259853598545985559856598575985859859598605986159862598635986459865598665986759868598695987059871598725987359874598755987659877598785987959880598815988259883598845988559886598875988859889598905989159892598935989459895598965989759898598995990059901599025990359904599055990659907599085990959910599115991259913599145991559916599175991859919599205992159922599235992459925599265992759928599295993059931599325993359934599355993659937599385993959940599415994259943599445994559946599475994859949599505995159952599535995459955599565995759958599595996059961599625996359964599655996659967599685996959970599715997259973599745997559976599775997859979599805998159982599835998459985599865998759988599895999059991599925999359994599955999659997599985999960000600016000260003600046000560006600076000860009600106001160012600136001460015600166001760018600196002060021600226002360024600256002660027600286002960030600316003260033600346003560036600376003860039600406004160042600436004460045600466004760048600496005060051600526005360054600556005660057600586005960060600616006260063600646006560066600676006860069600706007160072600736007460075600766007760078600796008060081600826008360084600856008660087600886008960090600916009260093600946009560096600976009860099601006010160102601036010460105601066010760108601096011060111601126011360114601156011660117601186011960120601216012260123601246012560126601276012860129601306013160132601336013460135601366013760138601396014060141601426014360144601456014660147601486014960150601516015260153601546015560156601576015860159601606016160162601636016460165601666016760168601696017060171601726017360174601756017660177601786017960180601816018260183601846018560186601876018860189601906019160192601936019460195601966019760198601996020060201602026020360204602056020660207602086020960210602116021260213602146021560216602176021860219602206022160222602236022460225602266022760228602296023060231602326023360234602356023660237602386023960240602416024260243602446024560246602476024860249602506025160252602536025460255602566025760258602596026060261602626026360264602656026660267602686026960270602716027260273602746027560276602776027860279602806028160282602836028460285602866028760288602896029060291602926029360294602956029660297602986029960300603016030260303603046030560306603076030860309603106031160312603136031460315603166031760318603196032060321603226032360324603256032660327603286032960330603316033260333603346033560336603376033860339603406034160342603436034460345603466034760348603496035060351603526035360354603556035660357603586035960360603616036260363603646036560366603676036860369603706037160372603736037460375603766037760378603796038060381603826038360384603856038660387603886038960390603916039260393603946039560396603976039860399604006040160402604036040460405604066040760408604096041060411604126041360414604156041660417604186041960420604216042260423604246042560426604276042860429604306043160432604336043460435604366043760438604396044060441604426044360444604456044660447604486044960450604516045260453604546045560456604576045860459604606046160462604636046460465604666046760468604696047060471604726047360474604756047660477604786047960480604816048260483604846048560486604876048860489604906049160492604936049460495604966049760498604996050060501605026050360504605056050660507605086050960510605116051260513605146051560516605176051860519605206052160522605236052460525605266052760528605296053060531605326053360534605356053660537605386053960540605416054260543605446054560546605476054860549605506055160552605536055460555605566055760558605596056060561605626056360564605656056660567605686056960570605716057260573605746057560576605776057860579605806058160582605836058460585605866058760588605896059060591605926059360594605956059660597605986059960600606016060260603606046060560606606076060860609606106061160612606136061460615606166061760618606196062060621606226062360624606256062660627606286062960630606316063260633606346063560636606376063860639606406064160642606436064460645606466064760648606496065060651606526065360654606556065660657606586065960660606616066260663606646066560666606676066860669606706067160672606736067460675606766067760678606796068060681606826068360684606856068660687606886068960690606916069260693606946069560696606976069860699607006070160702607036070460705607066070760708607096071060711607126071360714607156071660717607186071960720607216072260723607246072560726607276072860729607306073160732607336073460735607366073760738607396074060741607426074360744607456074660747607486074960750607516075260753607546075560756607576075860759607606076160762607636076460765607666076760768607696077060771607726077360774607756077660777607786077960780607816078260783607846078560786607876078860789607906079160792607936079460795607966079760798607996080060801608026080360804608056080660807608086080960810608116081260813608146081560816608176081860819608206082160822608236082460825608266082760828608296083060831608326083360834608356083660837608386083960840608416084260843608446084560846608476084860849608506085160852608536085460855608566085760858608596086060861608626086360864608656086660867608686086960870608716087260873608746087560876608776087860879608806088160882608836088460885608866088760888608896089060891608926089360894608956089660897608986089960900609016090260903609046090560906609076090860909609106091160912609136091460915609166091760918609196092060921609226092360924609256092660927609286092960930609316093260933609346093560936609376093860939609406094160942609436094460945609466094760948609496095060951609526095360954609556095660957609586095960960609616096260963609646096560966609676096860969609706097160972609736097460975609766097760978609796098060981609826098360984609856098660987609886098960990609916099260993609946099560996609976099860999610006100161002610036100461005610066100761008610096101061011610126101361014610156101661017610186101961020610216102261023610246102561026610276102861029
  1. /*
  2. #
  3. # File : CImg.h
  4. # ( C++ header file )
  5. #
  6. # Description : The 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 Tschumperle.
  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://www.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://www.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://www.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 224
  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. // Detect/configure OS variables.
  81. //
  82. // Define 'cimg_OS' to: '0' for an unknown OS (will try to minize library dependencies).
  83. // '1' for a Unix-like OS (Linux, Solaris, BSD, MacOSX, Irix, ...).
  84. // '2' for Microsoft Windows.
  85. // (auto-detection is performed if 'cimg_OS' is not set by the user).
  86. #ifndef cimg_OS
  87. #if defined(unix) || defined(__unix) || defined(__unix__) \
  88. || defined(linux) || defined(__linux) || defined(__linux__) \
  89. || defined(sun) || defined(__sun) \
  90. || defined(BSD) || defined(__OpenBSD__) || defined(__NetBSD__) \
  91. || defined(__FreeBSD__) || defined (__DragonFly__) \
  92. || defined(sgi) || defined(__sgi) \
  93. || defined(__MACOSX__) || defined(__APPLE__) \
  94. || defined(__CYGWIN__)
  95. #define cimg_OS 1
  96. #elif defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) \
  97. || defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
  98. #define cimg_OS 2
  99. #else
  100. #define cimg_OS 0
  101. #endif
  102. #elif !(cimg_OS==0 || cimg_OS==1 || cimg_OS==2)
  103. #error CImg Library: Invalid configuration variable 'cimg_OS'.
  104. #error (correct values are '0 = unknown OS', '1 = Unix-like OS', '2 = Microsoft Windows').
  105. #endif
  106. #ifndef cimg_date
  107. #define cimg_date __DATE__
  108. #endif
  109. #ifndef cimg_time
  110. #define cimg_time __TIME__
  111. #endif
  112. // Disable silly warnings on some Microsoft VC++ compilers.
  113. #ifdef _MSC_VER
  114. #pragma warning(push)
  115. #pragma warning(disable:4127)
  116. #pragma warning(disable:4244)
  117. #pragma warning(disable:4311)
  118. #pragma warning(disable:4312)
  119. #pragma warning(disable:4319)
  120. #pragma warning(disable:4512)
  121. #pragma warning(disable:4571)
  122. #pragma warning(disable:4640)
  123. #pragma warning(disable:4706)
  124. #pragma warning(disable:4710)
  125. #pragma warning(disable:4800)
  126. #pragma warning(disable:4804)
  127. #pragma warning(disable:4820)
  128. #pragma warning(disable:4996)
  129. #ifndef _CRT_SECURE_NO_DEPRECATE
  130. #define _CRT_SECURE_NO_DEPRECATE 1
  131. #endif
  132. #ifndef _CRT_SECURE_NO_WARNINGS
  133. #define _CRT_SECURE_NO_WARNINGS 1
  134. #endif
  135. #ifndef _CRT_NONSTDC_NO_DEPRECATE
  136. #define _CRT_NONSTDC_NO_DEPRECATE 1
  137. #endif
  138. #endif
  139. // Define correct string functions for each compiler and OS.
  140. #if cimg_OS==2 && defined(_MSC_VER)
  141. #define cimg_sscanf std::sscanf
  142. #define cimg_sprintf std::sprintf
  143. #define cimg_snprintf cimg::_snprintf
  144. #define cimg_vsnprintf cimg::_vsnprintf
  145. #else
  146. #include <stdio.h>
  147. #if defined(__MACOSX__) || defined(__APPLE__)
  148. #define cimg_sscanf cimg::_sscanf
  149. #define cimg_sprintf cimg::_sprintf
  150. #define cimg_snprintf cimg::_snprintf
  151. #define cimg_vsnprintf cimg::_vsnprintf
  152. #else
  153. #define cimg_sscanf std::sscanf
  154. #define cimg_sprintf std::sprintf
  155. #define cimg_snprintf snprintf
  156. #define cimg_vsnprintf vsnprintf
  157. #endif
  158. #endif
  159. // Include OS-specific headers.
  160. #if cimg_OS==1
  161. #include <sys/types.h>
  162. #include <sys/time.h>
  163. #include <sys/stat.h>
  164. #include <unistd.h>
  165. #include <dirent.h>
  166. #include <fnmatch.h>
  167. #elif cimg_OS==2
  168. #ifndef std_fopen
  169. #define std_fopen cimg::win_fopen
  170. #endif
  171. #ifndef NOMINMAX
  172. #define NOMINMAX
  173. #endif
  174. #ifndef WIN32_LEAN_AND_MEAN
  175. #define WIN32_LEAN_AND_MEAN
  176. #endif
  177. #include <windows.h>
  178. #ifndef _WIN32_IE
  179. #define _WIN32_IE 0x0400
  180. #endif
  181. #include <shlobj.h>
  182. #include <process.h>
  183. #include <io.h>
  184. #endif
  185. // Look for C++11 features.
  186. #ifndef cimg_use_cpp11
  187. #if __cplusplus>201100
  188. #define cimg_use_cpp11 1
  189. #else
  190. #define cimg_use_cpp11 0
  191. #endif
  192. #endif
  193. #if cimg_use_cpp11==1
  194. #include <initializer_list>
  195. #include <utility>
  196. #endif
  197. // Convenient macro to define pragma
  198. #ifdef _MSC_VER
  199. #define cimg_pragma(x) __pragma(x)
  200. #else
  201. #define cimg_pragma(x) _Pragma(#x)
  202. #endif
  203. // Define own types 'cimg_long/ulong' and 'cimg_int64/uint64' to ensure portability.
  204. // ( constrained to 'sizeof(cimg_ulong/cimg_long) = sizeof(void*)' and 'sizeof(cimg_int64/cimg_uint64)=8' ).
  205. #if cimg_OS==2
  206. #define cimg_uint64 unsigned __int64
  207. #define cimg_int64 __int64
  208. #define cimg_ulong UINT_PTR
  209. #define cimg_long INT_PTR
  210. #ifdef _MSC_VER
  211. #define cimg_fuint64 "%I64u"
  212. #define cimg_fint64 "%I64d"
  213. #else
  214. #define cimg_fuint64 "%llu"
  215. #define cimg_fint64 "%lld"
  216. #endif
  217. #else
  218. #if UINTPTR_MAX==0xffffffff || defined(__arm__) || defined(_M_ARM) || ((ULONG_MAX)==(UINT_MAX))
  219. #define cimg_uint64 unsigned long long
  220. #define cimg_int64 long long
  221. #define cimg_fuint64 "%llu"
  222. #define cimg_fint64 "%lld"
  223. #else
  224. #define cimg_uint64 unsigned long
  225. #define cimg_int64 long
  226. #define cimg_fuint64 "%lu"
  227. #define cimg_fint64 "%ld"
  228. #endif
  229. #if defined(__arm__) || defined(_M_ARM)
  230. #define cimg_ulong unsigned long long
  231. #define cimg_long long long
  232. #else
  233. #define cimg_ulong unsigned long
  234. #define cimg_long long
  235. #endif
  236. #endif
  237. // Configure filename separator.
  238. //
  239. // Filename separator is set by default to '/', except for Windows where it is '\'.
  240. #ifndef cimg_file_separator
  241. #if cimg_OS==2
  242. #define cimg_file_separator '\\'
  243. #else
  244. #define cimg_file_separator '/'
  245. #endif
  246. #endif
  247. // Configure verbosity of output messages.
  248. //
  249. // Define 'cimg_verbosity' to: '0' to hide library messages (quiet mode).
  250. // '1' to output library messages on the console.
  251. // '2' to output library messages on a basic dialog window (default behavior).
  252. // '3' to do as '1' + add extra warnings (may slow down the code!).
  253. // '4' to do as '2' + add extra warnings (may slow down the code!).
  254. //
  255. // Define 'cimg_strict_warnings' to replace warning messages by exception throwns.
  256. //
  257. // Define 'cimg_use_vt100' to allow output of color messages on VT100-compatible terminals.
  258. #ifndef cimg_verbosity
  259. #if cimg_OS==2
  260. #define cimg_verbosity 2
  261. #else
  262. #define cimg_verbosity 1
  263. #endif
  264. #elif !(cimg_verbosity==0 || cimg_verbosity==1 || cimg_verbosity==2 || cimg_verbosity==3 || cimg_verbosity==4)
  265. #error CImg Library: Configuration variable 'cimg_verbosity' is badly defined.
  266. #error (should be { 0=quiet | 1=console | 2=dialog | 3=console+warnings | 4=dialog+warnings }).
  267. #endif
  268. // Configure display framework.
  269. //
  270. // Define 'cimg_display' to: '0' to disable display capabilities.
  271. // '1' to use the X-Window framework (X11).
  272. // '2' to use the Microsoft GDI32 framework.
  273. #ifndef cimg_display
  274. #if cimg_OS==0
  275. #define cimg_display 0
  276. #elif cimg_OS==1
  277. #define cimg_display 1
  278. #elif cimg_OS==2
  279. #define cimg_display 2
  280. #endif
  281. #elif !(cimg_display==0 || cimg_display==1 || cimg_display==2)
  282. #error CImg Library: Configuration variable 'cimg_display' is badly defined.
  283. #error (should be { 0=none | 1=X-Window (X11) | 2=Microsoft GDI32 }).
  284. #endif
  285. // Configure the 'abort' signal handler (does nothing by default).
  286. // A typical signal handler can be defined in your own source like this:
  287. // #define cimg_abort_test if (is_abort) throw CImgAbortException("")
  288. //
  289. // where 'is_abort' is a boolean variable defined somewhere in your code and reachable in the method.
  290. // 'cimg_abort_test2' does the same but is called more often (in inner loops).
  291. #if defined(cimg_abort_test) && defined(cimg_use_openmp)
  292. // Define abort macros to be used with OpenMP.
  293. #ifndef _cimg_abort_init_omp
  294. #define _cimg_abort_init_omp bool _cimg_abort_go_omp = true; cimg::unused(_cimg_abort_go_omp)
  295. #endif
  296. #ifndef _cimg_abort_try_omp
  297. #define _cimg_abort_try_omp if (_cimg_abort_go_omp) try
  298. #endif
  299. #ifndef _cimg_abort_catch_omp
  300. #define _cimg_abort_catch_omp catch (CImgAbortException&) { cimg_pragma(omp atomic) _cimg_abort_go_omp&=false; }
  301. #endif
  302. #ifdef cimg_abort_test2
  303. #ifndef _cimg_abort_try_omp2
  304. #define _cimg_abort_try_omp2 _cimg_abort_try_omp
  305. #endif
  306. #ifndef _cimg_abort_catch_omp2
  307. #define _cimg_abort_catch_omp2 _cimg_abort_catch_omp
  308. #endif
  309. #ifndef _cimg_abort_catch_fill_omp
  310. #define _cimg_abort_catch_fill_omp \
  311. catch (CImgException& e) { cimg_pragma(omp critical(abort)) CImg<charT>::string(e._message).move_to(is_error); \
  312. cimg_pragma(omp atomic) _cimg_abort_go_omp&=false; }
  313. #endif
  314. #endif
  315. #endif
  316. #ifndef _cimg_abort_init_omp
  317. #define _cimg_abort_init_omp
  318. #endif
  319. #ifndef _cimg_abort_try_omp
  320. #define _cimg_abort_try_omp
  321. #endif
  322. #ifndef _cimg_abort_catch_omp
  323. #define _cimg_abort_catch_omp
  324. #endif
  325. #ifndef _cimg_abort_try_omp2
  326. #define _cimg_abort_try_omp2
  327. #endif
  328. #ifndef _cimg_abort_catch_omp2
  329. #define _cimg_abort_catch_omp2
  330. #endif
  331. #ifndef _cimg_abort_catch_fill_omp
  332. #define _cimg_abort_catch_fill_omp
  333. #endif
  334. #ifndef cimg_abort_init
  335. #define cimg_abort_init
  336. #endif
  337. #ifndef cimg_abort_test
  338. #define cimg_abort_test
  339. #endif
  340. #ifndef cimg_abort_test2
  341. #define cimg_abort_test2
  342. #endif
  343. #ifndef std_fopen
  344. #define std_fopen std::fopen
  345. #endif
  346. // Include display-specific headers.
  347. #if cimg_display==1
  348. #include <X11/Xlib.h>
  349. #include <X11/Xutil.h>
  350. #include <X11/keysym.h>
  351. #include <pthread.h>
  352. #ifdef cimg_use_xshm
  353. #include <sys/ipc.h>
  354. #include <sys/shm.h>
  355. #include <X11/extensions/XShm.h>
  356. #endif
  357. #ifdef cimg_use_xrandr
  358. #include <X11/extensions/Xrandr.h>
  359. #endif
  360. #endif
  361. #ifndef cimg_appname
  362. #define cimg_appname "CImg"
  363. #endif
  364. // Configure OpenMP support.
  365. // (http://www.openmp.org)
  366. //
  367. // Define 'cimg_use_openmp' to enable OpenMP support.
  368. //
  369. // OpenMP directives may be used in a (very) few CImg functions to get
  370. // advantages of multi-core CPUs.
  371. #ifdef cimg_use_openmp
  372. #include <omp.h>
  373. #define cimg_pragma_openmp(p) cimg_pragma(omp p)
  374. #else
  375. #define cimg_pragma_openmp(p)
  376. #endif
  377. // Configure OpenCV support.
  378. // (http://opencv.willowgarage.com/wiki/)
  379. //
  380. // Define 'cimg_use_opencv' to enable OpenCV support.
  381. //
  382. // OpenCV library may be used to access images from cameras
  383. // (see method 'CImg<T>::load_camera()').
  384. #ifdef cimg_use_opencv
  385. #ifdef True
  386. #undef True
  387. #define _cimg_redefine_True
  388. #endif
  389. #ifdef False
  390. #undef False
  391. #define _cimg_redefine_False
  392. #endif
  393. #include <cstddef>
  394. #include "cv.h"
  395. #include "highgui.h"
  396. #endif
  397. // Configure LibPNG support.
  398. // (http://www.libpng.org)
  399. //
  400. // Define 'cimg_use_png' to enable LibPNG support.
  401. //
  402. // PNG library may be used to get a native support of '.png' files.
  403. // (see methods 'CImg<T>::{load,save}_png()'.
  404. #ifdef cimg_use_png
  405. extern "C" {
  406. #include "png.h"
  407. }
  408. #endif
  409. // Configure LibJPEG support.
  410. // (http://en.wikipedia.org/wiki/Libjpeg)
  411. //
  412. // Define 'cimg_use_jpeg' to enable LibJPEG support.
  413. //
  414. // JPEG library may be used to get a native support of '.jpg' files.
  415. // (see methods 'CImg<T>::{load,save}_jpeg()').
  416. #ifdef cimg_use_jpeg
  417. extern "C" {
  418. #include "jpeglib.h"
  419. #include "setjmp.h"
  420. }
  421. #endif
  422. // Configure LibTIFF support.
  423. // (http://www.libtiff.org)
  424. //
  425. // Define 'cimg_use_tiff' to enable LibTIFF support.
  426. //
  427. // TIFF library may be used to get a native support of '.tif' files.
  428. // (see methods 'CImg[List]<T>::{load,save}_tiff()').
  429. #ifdef cimg_use_tiff
  430. extern "C" {
  431. #define uint64 uint64_hack_
  432. #define int64 int64_hack_
  433. #include "tiffio.h"
  434. #undef uint64
  435. #undef int64
  436. }
  437. #endif
  438. // Configure LibMINC2 support.
  439. // (http://en.wikibooks.org/wiki/MINC/Reference/MINC2.0_File_Format_Reference)
  440. //
  441. // Define 'cimg_use_minc2' to enable LibMINC2 support.
  442. //
  443. // MINC2 library may be used to get a native support of '.mnc' files.
  444. // (see methods 'CImg<T>::{load,save}_minc2()').
  445. #ifdef cimg_use_minc2
  446. #include "minc_io_simple_volume.h"
  447. #include "minc_1_simple.h"
  448. #include "minc_1_simple_rw.h"
  449. #endif
  450. // Configure Zlib support.
  451. // (http://www.zlib.net)
  452. //
  453. // Define 'cimg_use_zlib' to enable Zlib support.
  454. //
  455. // Zlib library may be used to allow compressed data in '.cimgz' files
  456. // (see methods 'CImg[List]<T>::{load,save}_cimg()').
  457. #ifdef cimg_use_zlib
  458. extern "C" {
  459. #include "zlib.h"
  460. }
  461. #endif
  462. // Configure libcurl support.
  463. // (http://curl.haxx.se/libcurl/)
  464. //
  465. // Define 'cimg_use_curl' to enable libcurl support.
  466. //
  467. // Libcurl may be used to get a native support of file downloading from the network.
  468. // (see method 'cimg::load_network()'.)
  469. #ifdef cimg_use_curl
  470. #include "curl/curl.h"
  471. #endif
  472. // Configure Magick++ support.
  473. // (http://www.imagemagick.org/Magick++)
  474. //
  475. // Define 'cimg_use_magick' to enable Magick++ support.
  476. //
  477. // Magick++ library may be used to get a native support of various image file formats.
  478. // (see methods 'CImg<T>::{load,save}()').
  479. #ifdef cimg_use_magick
  480. #include "Magick++.h"
  481. #endif
  482. // Configure FFTW3 support.
  483. // (http://www.fftw.org)
  484. //
  485. // Define 'cimg_use_fftw3' to enable libFFTW3 support.
  486. //
  487. // FFTW3 library may be used to efficiently compute the Fast Fourier Transform
  488. // of image data, without restriction on the image size.
  489. // (see method 'CImg[List]<T>::FFT()').
  490. #ifdef cimg_use_fftw3
  491. extern "C" {
  492. #include "fftw3.h"
  493. }
  494. #endif
  495. // Configure LibBoard support.
  496. // (http://libboard.sourceforge.net/)
  497. //
  498. // Define 'cimg_use_board' to enable Board support.
  499. //
  500. // Board library may be used to draw 3d objects in vector-graphics canvas
  501. // that can be saved as '.ps' or '.svg' files afterwards.
  502. // (see method 'CImg<T>::draw_object3d()').
  503. #ifdef cimg_use_board
  504. #include "Board.h"
  505. #endif
  506. // Configure OpenEXR support.
  507. // (http://www.openexr.com/)
  508. //
  509. // Define 'cimg_use_openexr' to enable OpenEXR support.
  510. //
  511. // OpenEXR library may be used to get a native support of '.exr' files.
  512. // (see methods 'CImg<T>::{load,save}_exr()').
  513. #ifdef cimg_use_openexr
  514. #include "ImfRgbaFile.h"
  515. #include "ImfInputFile.h"
  516. #include "ImfChannelList.h"
  517. #include "ImfMatrixAttribute.h"
  518. #include "ImfArray.h"
  519. #endif
  520. // Configure TinyEXR support.
  521. // (https://github.com/syoyo/tinyexr)
  522. //
  523. // Define 'cimg_use_tinyexr' to enable TinyEXR support.
  524. //
  525. // TinyEXR is a small, single header-only library to load and save OpenEXR(.exr) images.
  526. #ifdef cimg_use_tinyexr
  527. #ifndef TINYEXR_IMPLEMENTATION
  528. #define TINYEXR_IMPLEMENTATION
  529. #endif
  530. #include "tinyexr.h"
  531. #endif
  532. // Lapack configuration.
  533. // (http://www.netlib.org/lapack)
  534. //
  535. // Define 'cimg_use_lapack' to enable LAPACK support.
  536. //
  537. // Lapack library may be used in several CImg methods to speed up
  538. // matrix computations (eigenvalues, inverse, ...).
  539. #ifdef cimg_use_lapack
  540. extern "C" {
  541. extern void sgetrf_(int*, int*, float*, int*, int*, int*);
  542. extern void sgetri_(int*, float*, int*, int*, float*, int*, int*);
  543. extern void sgetrs_(char*, int*, int*, float*, int*, int*, float*, int*, int*);
  544. extern void sgesvd_(char*, char*, int*, int*, float*, int*, float*, float*, int*, float*, int*, float*, int*, int*);
  545. extern void ssyev_(char*, char*, int*, float*, int*, float*, float*, int*, int*);
  546. extern void dgetrf_(int*, int*, double*, int*, int*, int*);
  547. extern void dgetri_(int*, double*, int*, int*, double*, int*, int*);
  548. extern void dgetrs_(char*, int*, int*, double*, int*, int*, double*, int*, int*);
  549. extern void dgesvd_(char*, char*, int*, int*, double*, int*, double*, double*,
  550. int*, double*, int*, double*, int*, int*);
  551. extern void dsyev_(char*, char*, int*, double*, int*, double*, double*, int*, int*);
  552. extern void dgels_(char*, int*,int*,int*,double*,int*,double*,int*,double*,int*,int*);
  553. extern void sgels_(char*, int*,int*,int*,float*,int*,float*,int*,float*,int*,int*);
  554. }
  555. #endif
  556. // Check if min/max/PI macros are defined.
  557. //
  558. // CImg does not compile if macros 'min', 'max' or 'PI' are defined,
  559. // because it redefines functions min(), max() and const variable PI in the cimg:: namespace.
  560. // so it '#undef' these macros if necessary, and restore them to reasonable
  561. // values at the end of this file.
  562. #ifdef min
  563. #undef min
  564. #define _cimg_redefine_min
  565. #endif
  566. #ifdef max
  567. #undef max
  568. #define _cimg_redefine_max
  569. #endif
  570. #ifdef PI
  571. #undef PI
  572. #define _cimg_redefine_PI
  573. #endif
  574. // Define 'cimg_library' namespace suffix.
  575. //
  576. // You may want to add a suffix to the 'cimg_library' namespace, for instance if you need to work
  577. // with several versions of the library at the same time.
  578. #ifdef cimg_namespace_suffix
  579. #define __cimg_library_suffixed(s) cimg_library_##s
  580. #define _cimg_library_suffixed(s) __cimg_library_suffixed(s)
  581. #define cimg_library_suffixed _cimg_library_suffixed(cimg_namespace_suffix)
  582. #else
  583. #define cimg_library_suffixed cimg_library
  584. #endif
  585. /*------------------------------------------------------------------------------
  586. #
  587. # Define user-friendly macros.
  588. #
  589. # These CImg macros are prefixed by 'cimg_' and can be used safely in your own
  590. # code. They are useful to parse command line options, or to write image loops.
  591. #
  592. ------------------------------------------------------------------------------*/
  593. // Macros to define program usage, and retrieve command line arguments.
  594. #define cimg_usage(usage) cimg_library_suffixed::cimg::option((char*)0,argc,argv,(char*)0,usage,false)
  595. #define cimg_help(str) cimg_library_suffixed::cimg::option((char*)0,argc,argv,str,(char*)0)
  596. #define cimg_option(name,defaut,usage) cimg_library_suffixed::cimg::option(name,argc,argv,defaut,usage)
  597. // Macros to define and manipulate local neighborhoods.
  598. #define CImg_2x2(I,T) T I[4]; \
  599. T& I##cc = I[0]; T& I##nc = I[1]; \
  600. T& I##cn = I[2]; T& I##nn = I[3]; \
  601. I##cc = I##nc = \
  602. I##cn = I##nn = 0
  603. #define CImg_3x3(I,T) T I[9]; \
  604. T& I##pp = I[0]; T& I##cp = I[1]; T& I##np = I[2]; \
  605. T& I##pc = I[3]; T& I##cc = I[4]; T& I##nc = I[5]; \
  606. T& I##pn = I[6]; T& I##cn = I[7]; T& I##nn = I[8]; \
  607. I##pp = I##cp = I##np = \
  608. I##pc = I##cc = I##nc = \
  609. I##pn = I##cn = I##nn = 0
  610. #define CImg_4x4(I,T) T I[16]; \
  611. T& I##pp = I[0]; T& I##cp = I[1]; T& I##np = I[2]; T& I##ap = I[3]; \
  612. T& I##pc = I[4]; T& I##cc = I[5]; T& I##nc = I[6]; T& I##ac = I[7]; \
  613. T& I##pn = I[8]; T& I##cn = I[9]; T& I##nn = I[10]; T& I##an = I[11]; \
  614. T& I##pa = I[12]; T& I##ca = I[13]; T& I##na = I[14]; T& I##aa = I[15]; \
  615. I##pp = I##cp = I##np = I##ap = \
  616. I##pc = I##cc = I##nc = I##ac = \
  617. I##pn = I##cn = I##nn = I##an = \
  618. I##pa = I##ca = I##na = I##aa = 0
  619. #define CImg_5x5(I,T) T I[25]; \
  620. 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]; \
  621. 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]; \
  622. 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]; \
  623. 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]; \
  624. 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]; \
  625. I##bb = I##pb = I##cb = I##nb = I##ab = \
  626. I##bp = I##pp = I##cp = I##np = I##ap = \
  627. I##bc = I##pc = I##cc = I##nc = I##ac = \
  628. I##bn = I##pn = I##cn = I##nn = I##an = \
  629. I##ba = I##pa = I##ca = I##na = I##aa = 0
  630. #define CImg_2x2x2(I,T) T I[8]; \
  631. T& I##ccc = I[0]; T& I##ncc = I[1]; \
  632. T& I##cnc = I[2]; T& I##nnc = I[3]; \
  633. T& I##ccn = I[4]; T& I##ncn = I[5]; \
  634. T& I##cnn = I[6]; T& I##nnn = I[7]; \
  635. I##ccc = I##ncc = \
  636. I##cnc = I##nnc = \
  637. I##ccn = I##ncn = \
  638. I##cnn = I##nnn = 0
  639. #define CImg_3x3x3(I,T) T I[27]; \
  640. T& I##ppp = I[0]; T& I##cpp = I[1]; T& I##npp = I[2]; \
  641. T& I##pcp = I[3]; T& I##ccp = I[4]; T& I##ncp = I[5]; \
  642. T& I##pnp = I[6]; T& I##cnp = I[7]; T& I##nnp = I[8]; \
  643. T& I##ppc = I[9]; T& I##cpc = I[10]; T& I##npc = I[11]; \
  644. T& I##pcc = I[12]; T& I##ccc = I[13]; T& I##ncc = I[14]; \
  645. T& I##pnc = I[15]; T& I##cnc = I[16]; T& I##nnc = I[17]; \
  646. T& I##ppn = I[18]; T& I##cpn = I[19]; T& I##npn = I[20]; \
  647. T& I##pcn = I[21]; T& I##ccn = I[22]; T& I##ncn = I[23]; \
  648. T& I##pnn = I[24]; T& I##cnn = I[25]; T& I##nnn = I[26]; \
  649. I##ppp = I##cpp = I##npp = \
  650. I##pcp = I##ccp = I##ncp = \
  651. I##pnp = I##cnp = I##nnp = \
  652. I##ppc = I##cpc = I##npc = \
  653. I##pcc = I##ccc = I##ncc = \
  654. I##pnc = I##cnc = I##nnc = \
  655. I##ppn = I##cpn = I##npn = \
  656. I##pcn = I##ccn = I##ncn = \
  657. I##pnn = I##cnn = I##nnn = 0
  658. #define cimg_get2x2(img,x,y,z,c,I,T) \
  659. 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), \
  660. I[3] = (T)(img)(_n1##x,_n1##y,z,c)
  661. #define cimg_get3x3(img,x,y,z,c,I,T) \
  662. 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), \
  663. 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), \
  664. 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)
  665. #define cimg_get4x4(img,x,y,z,c,I,T) \
  666. 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), \
  667. 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), \
  668. 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), \
  669. 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), \
  670. 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), \
  671. I[15] = (T)(img)(_n2##x,_n2##y,z,c)
  672. #define cimg_get5x5(img,x,y,z,c,I,T) \
  673. 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), \
  674. 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), \
  675. 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), \
  676. 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), \
  677. 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), \
  678. 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), \
  679. 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), \
  680. 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), \
  681. I[24] = (T)(img)(_n2##x,_n2##y,z,c)
  682. #define cimg_get6x6(img,x,y,z,c,I,T) \
  683. 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), \
  684. 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), \
  685. 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), \
  686. 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), \
  687. 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), \
  688. 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), \
  689. 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), \
  690. 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), \
  691. 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), \
  692. 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), \
  693. 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), \
  694. 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)
  695. #define cimg_get7x7(img,x,y,z,c,I,T) \
  696. 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), \
  697. 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), \
  698. 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), \
  699. 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), \
  700. 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), \
  701. 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), \
  702. 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), \
  703. 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), \
  704. 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), \
  705. 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), \
  706. 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), \
  707. 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), \
  708. 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), \
  709. 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), \
  710. 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), \
  711. 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), \
  712. I[48] = (T)(img)(_n3##x,_n3##y,z,c)
  713. #define cimg_get8x8(img,x,y,z,c,I,T) \
  714. 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), \
  715. 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), \
  716. 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), \
  717. 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), \
  718. 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), \
  719. 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), \
  720. 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), \
  721. 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), \
  722. 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), \
  723. 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), \
  724. 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), \
  725. 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), \
  726. 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), \
  727. 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), \
  728. 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), \
  729. 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), \
  730. 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), \
  731. 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), \
  732. 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), \
  733. 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), \
  734. 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), \
  735. I[63] = (T)(img)(_n4##x,_n4##y,z,c);
  736. #define cimg_get9x9(img,x,y,z,c,I,T) \
  737. 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), \
  738. 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), \
  739. 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), \
  740. 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), \
  741. 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), \
  742. 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), \
  743. 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), \
  744. 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), \
  745. 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), \
  746. 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), \
  747. 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), \
  748. 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), \
  749. 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), \
  750. 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), \
  751. 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), \
  752. 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), \
  753. 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), \
  754. 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), \
  755. 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), \
  756. 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), \
  757. 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), \
  758. 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), \
  759. 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), \
  760. 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), \
  761. 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), \
  762. 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), \
  763. 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)
  764. #define cimg_get2x2x2(img,x,y,z,c,I,T) \
  765. 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), \
  766. 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), \
  767. I[6] = (T)(img)(x,_n1##y,_n1##z,c), I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)
  768. #define cimg_get3x3x3(img,x,y,z,c,I,T) \
  769. I[0] = (T)(img)(_p1##x,_p1##y,_p1##z,c), I[1] = (T)(img)(x,_p1##y,_p1##z,c), \
  770. 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), \
  771. 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), \
  772. 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), \
  773. 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), \
  774. 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), \
  775. 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), \
  776. 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), \
  777. 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), \
  778. I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)
  779. // Macros to perform various image loops.
  780. //
  781. // These macros are simpler to use than loops with C++ iterators.
  782. #define cimg_for(img,ptrs,T_ptrs) \
  783. for (T_ptrs *ptrs = (img)._data, *_max##ptrs = (img)._data + (img).size(); ptrs<_max##ptrs; ++ptrs)
  784. #define cimg_rof(img,ptrs,T_ptrs) for (T_ptrs *ptrs = (img)._data + (img).size() - 1; ptrs>=(img)._data; --ptrs)
  785. #define cimg_foroff(img,off) for (cimg_ulong off = 0, _max##off = (img).size(); off<_max##off; ++off)
  786. #define cimg_for1(bound,i) for (int i = 0; i<(int)(bound); ++i)
  787. #define cimg_forX(img,x) cimg_for1((img)._width,x)
  788. #define cimg_forY(img,y) cimg_for1((img)._height,y)
  789. #define cimg_forZ(img,z) cimg_for1((img)._depth,z)
  790. #define cimg_forC(img,c) cimg_for1((img)._spectrum,c)
  791. #define cimg_forXY(img,x,y) cimg_forY(img,y) cimg_forX(img,x)
  792. #define cimg_forXZ(img,x,z) cimg_forZ(img,z) cimg_forX(img,x)
  793. #define cimg_forYZ(img,y,z) cimg_forZ(img,z) cimg_forY(img,y)
  794. #define cimg_forXC(img,x,c) cimg_forC(img,c) cimg_forX(img,x)
  795. #define cimg_forYC(img,y,c) cimg_forC(img,c) cimg_forY(img,y)
  796. #define cimg_forZC(img,z,c) cimg_forC(img,c) cimg_forZ(img,z)
  797. #define cimg_forXYZ(img,x,y,z) cimg_forZ(img,z) cimg_forXY(img,x,y)
  798. #define cimg_forXYC(img,x,y,c) cimg_forC(img,c) cimg_forXY(img,x,y)
  799. #define cimg_forXZC(img,x,z,c) cimg_forC(img,c) cimg_forXZ(img,x,z)
  800. #define cimg_forYZC(img,y,z,c) cimg_forC(img,c) cimg_forYZ(img,y,z)
  801. #define cimg_forXYZC(img,x,y,z,c) cimg_forC(img,c) cimg_forXYZ(img,x,y,z)
  802. #define cimg_rof1(bound,i) for (int i = (int)(bound) - 1; i>=0; --i)
  803. #define cimg_rofX(img,x) cimg_rof1((img)._width,x)
  804. #define cimg_rofY(img,y) cimg_rof1((img)._height,y)
  805. #define cimg_rofZ(img,z) cimg_rof1((img)._depth,z)
  806. #define cimg_rofC(img,c) cimg_rof1((img)._spectrum,c)
  807. #define cimg_rofXY(img,x,y) cimg_rofY(img,y) cimg_rofX(img,x)
  808. #define cimg_rofXZ(img,x,z) cimg_rofZ(img,z) cimg_rofX(img,x)
  809. #define cimg_rofYZ(img,y,z) cimg_rofZ(img,z) cimg_rofY(img,y)
  810. #define cimg_rofXC(img,x,c) cimg_rofC(img,c) cimg_rofX(img,x)
  811. #define cimg_rofYC(img,y,c) cimg_rofC(img,c) cimg_rofY(img,y)
  812. #define cimg_rofZC(img,z,c) cimg_rofC(img,c) cimg_rofZ(img,z)
  813. #define cimg_rofXYZ(img,x,y,z) cimg_rofZ(img,z) cimg_rofXY(img,x,y)
  814. #define cimg_rofXYC(img,x,y,c) cimg_rofC(img,c) cimg_rofXY(img,x,y)
  815. #define cimg_rofXZC(img,x,z,c) cimg_rofC(img,c) cimg_rofXZ(img,x,z)
  816. #define cimg_rofYZC(img,y,z,c) cimg_rofC(img,c) cimg_rofYZ(img,y,z)
  817. #define cimg_rofXYZC(img,x,y,z,c) cimg_rofC(img,c) cimg_rofXYZ(img,x,y,z)
  818. #define cimg_for_in1(bound,i0,i1,i) \
  819. for (int i = (int)(i0)<0?0:(int)(i0), _max##i = (int)(i1)<(int)(bound)?(int)(i1):(int)(bound) - 1; i<=_max##i; ++i)
  820. #define cimg_for_inX(img,x0,x1,x) cimg_for_in1((img)._width,x0,x1,x)
  821. #define cimg_for_inY(img,y0,y1,y) cimg_for_in1((img)._height,y0,y1,y)
  822. #define cimg_for_inZ(img,z0,z1,z) cimg_for_in1((img)._depth,z0,z1,z)
  823. #define cimg_for_inC(img,c0,c1,c) cimg_for_in1((img)._spectrum,c0,c1,c)
  824. #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)
  825. #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)
  826. #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)
  827. #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)
  828. #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)
  829. #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)
  830. #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)
  831. #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)
  832. #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)
  833. #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)
  834. #define cimg_for_inXYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
  835. cimg_for_inC(img,c0,c1,c) cimg_for_inXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
  836. #define cimg_for_insideX(img,x,n) cimg_for_inX(img,n,(img)._width - 1 - (n),x)
  837. #define cimg_for_insideY(img,y,n) cimg_for_inY(img,n,(img)._height - 1 - (n),y)
  838. #define cimg_for_insideZ(img,z,n) cimg_for_inZ(img,n,(img)._depth - 1 - (n),z)
  839. #define cimg_for_insideC(img,c,n) cimg_for_inC(img,n,(img)._spectrum - 1 - (n),c)
  840. #define cimg_for_insideXY(img,x,y,n) cimg_for_inXY(img,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),x,y)
  841. #define cimg_for_insideXYZ(img,x,y,z,n) \
  842. cimg_for_inXYZ(img,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),(img)._depth - 1 - (n),x,y,z)
  843. #define cimg_for_insideXYZC(img,x,y,z,c,n) \
  844. cimg_for_inXYZ(img,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),(img)._depth - 1 - (n),x,y,z)
  845. #define cimg_for_out1(boundi,i0,i1,i) \
  846. for (int i = (int)(i0)>0?0:(int)(i1) + 1; i<(int)(boundi); ++i, i = i==(int)(i0)?(int)(i1) + 1:i)
  847. #define cimg_for_out2(boundi,boundj,i0,j0,i1,j1,i,j) \
  848. for (int j = 0; j<(int)(boundj); ++j) \
  849. for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j?0:(int)(i0)>0?0:(int)(i1) + 1; i<(int)(boundi); \
  850. ++i, i = _n1j?i:(i==(int)(i0)?(int)(i1) + 1:i))
  851. #define cimg_for_out3(boundi,boundj,boundk,i0,j0,k0,i1,j1,k1,i,j,k) \
  852. for (int k = 0; k<(int)(boundk); ++k) \
  853. for (int _n1k = (int)(k<(int)(k0) || k>(int)(k1)), j = 0; j<(int)(boundj); ++j) \
  854. for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j || _n1k?0:(int)(i0)>0?0:(int)(i1) + 1; i<(int)(boundi); \
  855. ++i, i = _n1j || _n1k?i:(i==(int)(i0)?(int)(i1) + 1:i))
  856. #define cimg_for_out4(boundi,boundj,boundk,boundl,i0,j0,k0,l0,i1,j1,k1,l1,i,j,k,l) \
  857. for (int l = 0; l<(int)(boundl); ++l) \
  858. for (int _n1l = (int)(l<(int)(l0) || l>(int)(l1)), k = 0; k<(int)(boundk); ++k) \
  859. for (int _n1k = (int)(k<(int)(k0) || k>(int)(k1)), j = 0; j<(int)(boundj); ++j) \
  860. for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j || _n1k || _n1l?0:(int)(i0)>0?0:(int)(i1) + 1; \
  861. i<(int)(boundi); ++i, i = _n1j || _n1k || _n1l?i:(i==(int)(i0)?(int)(i1) + 1:i))
  862. #define cimg_for_outX(img,x0,x1,x) cimg_for_out1((img)._width,x0,x1,x)
  863. #define cimg_for_outY(img,y0,y1,y) cimg_for_out1((img)._height,y0,y1,y)
  864. #define cimg_for_outZ(img,z0,z1,z) cimg_for_out1((img)._depth,z0,z1,z)
  865. #define cimg_for_outC(img,c0,c1,c) cimg_for_out1((img)._spectrum,c0,c1,c)
  866. #define cimg_for_outXY(img,x0,y0,x1,y1,x,y) cimg_for_out2((img)._width,(img)._height,x0,y0,x1,y1,x,y)
  867. #define cimg_for_outXZ(img,x0,z0,x1,z1,x,z) cimg_for_out2((img)._width,(img)._depth,x0,z0,x1,z1,x,z)
  868. #define cimg_for_outXC(img,x0,c0,x1,c1,x,c) cimg_for_out2((img)._width,(img)._spectrum,x0,c0,x1,c1,x,c)
  869. #define cimg_for_outYZ(img,y0,z0,y1,z1,y,z) cimg_for_out2((img)._height,(img)._depth,y0,z0,y1,z1,y,z)
  870. #define cimg_for_outYC(img,y0,c0,y1,c1,y,c) cimg_for_out2((img)._height,(img)._spectrum,y0,c0,y1,c1,y,c)
  871. #define cimg_for_outZC(img,z0,c0,z1,c1,z,c) cimg_for_out2((img)._depth,(img)._spectrum,z0,c0,z1,c1,z,c)
  872. #define cimg_for_outXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) \
  873. cimg_for_out3((img)._width,(img)._height,(img)._depth,x0,y0,z0,x1,y1,z1,x,y,z)
  874. #define cimg_for_outXYC(img,x0,y0,c0,x1,y1,c1,x,y,c) \
  875. cimg_for_out3((img)._width,(img)._height,(img)._spectrum,x0,y0,c0,x1,y1,c1,x,y,c)
  876. #define cimg_for_outXZC(img,x0,z0,c0,x1,z1,c1,x,z,c) \
  877. cimg_for_out3((img)._width,(img)._depth,(img)._spectrum,x0,z0,c0,x1,z1,c1,x,z,c)
  878. #define cimg_for_outYZC(img,y0,z0,c0,y1,z1,c1,y,z,c) \
  879. cimg_for_out3((img)._height,(img)._depth,(img)._spectrum,y0,z0,c0,y1,z1,c1,y,z,c)
  880. #define cimg_for_outXYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
  881. cimg_for_out4((img)._width,(img)._height,(img)._depth,(img)._spectrum,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c)
  882. #define cimg_for_borderX(img,x,n) cimg_for_outX(img,n,(img)._width - 1 - (n),x)
  883. #define cimg_for_borderY(img,y,n) cimg_for_outY(img,n,(img)._height - 1 - (n),y)
  884. #define cimg_for_borderZ(img,z,n) cimg_for_outZ(img,n,(img)._depth - 1 - (n),z)
  885. #define cimg_for_borderC(img,c,n) cimg_for_outC(img,n,(img)._spectrum - 1 - (n),c)
  886. #define cimg_for_borderXY(img,x,y,n) cimg_for_outXY(img,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),x,y)
  887. #define cimg_for_borderXYZ(img,x,y,z,n) \
  888. cimg_for_outXYZ(img,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),(img)._depth - 1 - (n),x,y,z)
  889. #define cimg_for_borderXYZC(img,x,y,z,c,n) \
  890. cimg_for_outXYZC(img,n,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n), \
  891. (img)._depth - 1 - (n),(img)._spectrum - 1 - (n),x,y,z,c)
  892. #define cimg_for_spiralXY(img,x,y) \
  893. for (int x = 0, y = 0, _n1##x = 1, _n1##y = (img).width()*(img).height(); _n1##y; \
  894. --_n1##y, _n1##x+=(_n1##x>>2) - ((!(_n1##x&3)?--y:((_n1##x&3)==1?(img)._width - 1 - ++x:\
  895. ((_n1##x&3)==2?(img)._height - 1 - ++y:--x))))?0:1)
  896. #define cimg_for_lineXY(x,y,x0,y0,x1,y1) \
  897. for (int x = (int)(x0), y = (int)(y0), _sx = 1, _sy = 1, _steep = 0, \
  898. _dx=(x1)>(x0)?(int)(x1) - (int)(x0):(_sx=-1,(int)(x0) - (int)(x1)), \
  899. _dy=(y1)>(y0)?(int)(y1) - (int)(y0):(_sy=-1,(int)(y0) - (int)(y1)), \
  900. _counter = _dx, \
  901. _err = _dx>_dy?(_dy>>1):((_steep=1),(_counter=_dy),(_dx>>1)); \
  902. _counter>=0; \
  903. --_counter, x+=_steep? \
  904. (y+=_sy,(_err-=_dx)<0?_err+=_dy,_sx:0): \
  905. (y+=(_err-=_dy)<0?_err+=_dx,_sy:0,_sx))
  906. #define cimg_for2(bound,i) \
  907. for (int i = 0, _n1##i = 1>=(bound)?(int)(bound) - 1:1; \
  908. _n1##i<(int)(bound) || i==--_n1##i; \
  909. ++i, ++_n1##i)
  910. #define cimg_for2X(img,x) cimg_for2((img)._width,x)
  911. #define cimg_for2Y(img,y) cimg_for2((img)._height,y)
  912. #define cimg_for2Z(img,z) cimg_for2((img)._depth,z)
  913. #define cimg_for2C(img,c) cimg_for2((img)._spectrum,c)
  914. #define cimg_for2XY(img,x,y) cimg_for2Y(img,y) cimg_for2X(img,x)
  915. #define cimg_for2XZ(img,x,z) cimg_for2Z(img,z) cimg_for2X(img,x)
  916. #define cimg_for2XC(img,x,c) cimg_for2C(img,c) cimg_for2X(img,x)
  917. #define cimg_for2YZ(img,y,z) cimg_for2Z(img,z) cimg_for2Y(img,y)
  918. #define cimg_for2YC(img,y,c) cimg_for2C(img,c) cimg_for2Y(img,y)
  919. #define cimg_for2ZC(img,z,c) cimg_for2C(img,c) cimg_for2Z(img,z)
  920. #define cimg_for2XYZ(img,x,y,z) cimg_for2Z(img,z) cimg_for2XY(img,x,y)
  921. #define cimg_for2XZC(img,x,z,c) cimg_for2C(img,c) cimg_for2XZ(img,x,z)
  922. #define cimg_for2YZC(img,y,z,c) cimg_for2C(img,c) cimg_for2YZ(img,y,z)
  923. #define cimg_for2XYZC(img,x,y,z,c) cimg_for2C(img,c) cimg_for2XYZ(img,x,y,z)
  924. #define cimg_for_in2(bound,i0,i1,i) \
  925. for (int i = (int)(i0)<0?0:(int)(i0), \
  926. _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1; \
  927. i<=(int)(i1) && (_n1##i<(int)(bound) || i==--_n1##i); \
  928. ++i, ++_n1##i)
  929. #define cimg_for_in2X(img,x0,x1,x) cimg_for_in2((img)._width,x0,x1,x)
  930. #define cimg_for_in2Y(img,y0,y1,y) cimg_for_in2((img)._height,y0,y1,y)
  931. #define cimg_for_in2Z(img,z0,z1,z) cimg_for_in2((img)._depth,z0,z1,z)
  932. #define cimg_for_in2C(img,c0,c1,c) cimg_for_in2((img)._spectrum,c0,c1,c)
  933. #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)
  934. #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)
  935. #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)
  936. #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)
  937. #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)
  938. #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)
  939. #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)
  940. #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)
  941. #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)
  942. #define cimg_for_in2XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
  943. cimg_for_in2C(img,c0,c1,c) cimg_for_in2XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
  944. #define cimg_for3(bound,i) \
  945. for (int i = 0, _p1##i = 0, \
  946. _n1##i = 1>=(bound)?(int)(bound) - 1:1; \
  947. _n1##i<(int)(bound) || i==--_n1##i; \
  948. _p1##i = i++, ++_n1##i)
  949. #define cimg_for3X(img,x) cimg_for3((img)._width,x)
  950. #define cimg_for3Y(img,y) cimg_for3((img)._height,y)
  951. #define cimg_for3Z(img,z) cimg_for3((img)._depth,z)
  952. #define cimg_for3C(img,c) cimg_for3((img)._spectrum,c)
  953. #define cimg_for3XY(img,x,y) cimg_for3Y(img,y) cimg_for3X(img,x)
  954. #define cimg_for3XZ(img,x,z) cimg_for3Z(img,z) cimg_for3X(img,x)
  955. #define cimg_for3XC(img,x,c) cimg_for3C(img,c) cimg_for3X(img,x)
  956. #define cimg_for3YZ(img,y,z) cimg_for3Z(img,z) cimg_for3Y(img,y)
  957. #define cimg_for3YC(img,y,c) cimg_for3C(img,c) cimg_for3Y(img,y)
  958. #define cimg_for3ZC(img,z,c) cimg_for3C(img,c) cimg_for3Z(img,z)
  959. #define cimg_for3XYZ(img,x,y,z) cimg_for3Z(img,z) cimg_for3XY(img,x,y)
  960. #define cimg_for3XZC(img,x,z,c) cimg_for3C(img,c) cimg_for3XZ(img,x,z)
  961. #define cimg_for3YZC(img,y,z,c) cimg_for3C(img,c) cimg_for3YZ(img,y,z)
  962. #define cimg_for3XYZC(img,x,y,z,c) cimg_for3C(img,c) cimg_for3XYZ(img,x,y,z)
  963. #define cimg_for_in3(bound,i0,i1,i) \
  964. for (int i = (int)(i0)<0?0:(int)(i0), \
  965. _p1##i = i - 1<0?0:i - 1, \
  966. _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1; \
  967. i<=(int)(i1) && (_n1##i<(int)(bound) || i==--_n1##i); \
  968. _p1##i = i++, ++_n1##i)
  969. #define cimg_for_in3X(img,x0,x1,x) cimg_for_in3((img)._width,x0,x1,x)
  970. #define cimg_for_in3Y(img,y0,y1,y) cimg_for_in3((img)._height,y0,y1,y)
  971. #define cimg_for_in3Z(img,z0,z1,z) cimg_for_in3((img)._depth,z0,z1,z)
  972. #define cimg_for_in3C(img,c0,c1,c) cimg_for_in3((img)._spectrum,c0,c1,c)
  973. #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)
  974. #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)
  975. #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)
  976. #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)
  977. #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)
  978. #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)
  979. #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)
  980. #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)
  981. #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)
  982. #define cimg_for_in3XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
  983. cimg_for_in3C(img,c0,c1,c) cimg_for_in3XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
  984. #define cimg_for4(bound,i) \
  985. for (int i = 0, _p1##i = 0, _n1##i = 1>=(bound)?(int)(bound) - 1:1, \
  986. _n2##i = 2>=(bound)?(int)(bound) - 1:2; \
  987. _n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i); \
  988. _p1##i = i++, ++_n1##i, ++_n2##i)
  989. #define cimg_for4X(img,x) cimg_for4((img)._width,x)
  990. #define cimg_for4Y(img,y) cimg_for4((img)._height,y)
  991. #define cimg_for4Z(img,z) cimg_for4((img)._depth,z)
  992. #define cimg_for4C(img,c) cimg_for4((img)._spectrum,c)
  993. #define cimg_for4XY(img,x,y) cimg_for4Y(img,y) cimg_for4X(img,x)
  994. #define cimg_for4XZ(img,x,z) cimg_for4Z(img,z) cimg_for4X(img,x)
  995. #define cimg_for4XC(img,x,c) cimg_for4C(img,c) cimg_for4X(img,x)
  996. #define cimg_for4YZ(img,y,z) cimg_for4Z(img,z) cimg_for4Y(img,y)
  997. #define cimg_for4YC(img,y,c) cimg_for4C(img,c) cimg_for4Y(img,y)
  998. #define cimg_for4ZC(img,z,c) cimg_for4C(img,c) cimg_for4Z(img,z)
  999. #define cimg_for4XYZ(img,x,y,z) cimg_for4Z(img,z) cimg_for4XY(img,x,y)
  1000. #define cimg_for4XZC(img,x,z,c) cimg_for4C(img,c) cimg_for4XZ(img,x,z)
  1001. #define cimg_for4YZC(img,y,z,c) cimg_for4C(img,c) cimg_for4YZ(img,y,z)
  1002. #define cimg_for4XYZC(img,x,y,z,c) cimg_for4C(img,c) cimg_for4XYZ(img,x,y,z)
  1003. #define cimg_for_in4(bound,i0,i1,i) \
  1004. for (int i = (int)(i0)<0?0:(int)(i0), \
  1005. _p1##i = i - 1<0?0:i - 1, \
  1006. _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \
  1007. _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2; \
  1008. i<=(int)(i1) && (_n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i)); \
  1009. _p1##i = i++, ++_n1##i, ++_n2##i)
  1010. #define cimg_for_in4X(img,x0,x1,x) cimg_for_in4((img)._width,x0,x1,x)
  1011. #define cimg_for_in4Y(img,y0,y1,y) cimg_for_in4((img)._height,y0,y1,y)
  1012. #define cimg_for_in4Z(img,z0,z1,z) cimg_for_in4((img)._depth,z0,z1,z)
  1013. #define cimg_for_in4C(img,c0,c1,c) cimg_for_in4((img)._spectrum,c0,c1,c)
  1014. #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)
  1015. #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)
  1016. #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)
  1017. #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)
  1018. #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)
  1019. #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)
  1020. #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)
  1021. #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)
  1022. #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)
  1023. #define cimg_for_in4XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
  1024. cimg_for_in4C(img,c0,c1,c) cimg_for_in4XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
  1025. #define cimg_for5(bound,i) \
  1026. for (int i = 0, _p2##i = 0, _p1##i = 0, \
  1027. _n1##i = 1>=(bound)?(int)(bound) - 1:1, \
  1028. _n2##i = 2>=(bound)?(int)(bound) - 1:2; \
  1029. _n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i); \
  1030. _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i)
  1031. #define cimg_for5X(img,x) cimg_for5((img)._width,x)
  1032. #define cimg_for5Y(img,y) cimg_for5((img)._height,y)
  1033. #define cimg_for5Z(img,z) cimg_for5((img)._depth,z)
  1034. #define cimg_for5C(img,c) cimg_for5((img)._spectrum,c)
  1035. #define cimg_for5XY(img,x,y) cimg_for5Y(img,y) cimg_for5X(img,x)
  1036. #define cimg_for5XZ(img,x,z) cimg_for5Z(img,z) cimg_for5X(img,x)
  1037. #define cimg_for5XC(img,x,c) cimg_for5C(img,c) cimg_for5X(img,x)
  1038. #define cimg_for5YZ(img,y,z) cimg_for5Z(img,z) cimg_for5Y(img,y)
  1039. #define cimg_for5YC(img,y,c) cimg_for5C(img,c) cimg_for5Y(img,y)
  1040. #define cimg_for5ZC(img,z,c) cimg_for5C(img,c) cimg_for5Z(img,z)
  1041. #define cimg_for5XYZ(img,x,y,z) cimg_for5Z(img,z) cimg_for5XY(img,x,y)
  1042. #define cimg_for5XZC(img,x,z,c) cimg_for5C(img,c) cimg_for5XZ(img,x,z)
  1043. #define cimg_for5YZC(img,y,z,c) cimg_for5C(img,c) cimg_for5YZ(img,y,z)
  1044. #define cimg_for5XYZC(img,x,y,z,c) cimg_for5C(img,c) cimg_for5XYZ(img,x,y,z)
  1045. #define cimg_for_in5(bound,i0,i1,i) \
  1046. for (int i = (int)(i0)<0?0:(int)(i0), \
  1047. _p2##i = i - 2<0?0:i - 2, \
  1048. _p1##i = i - 1<0?0:i - 1, \
  1049. _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \
  1050. _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2; \
  1051. i<=(int)(i1) && (_n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i)); \
  1052. _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i)
  1053. #define cimg_for_in5X(img,x0,x1,x) cimg_for_in5((img)._width,x0,x1,x)
  1054. #define cimg_for_in5Y(img,y0,y1,y) cimg_for_in5((img)._height,y0,y1,y)
  1055. #define cimg_for_in5Z(img,z0,z1,z) cimg_for_in5((img)._depth,z0,z1,z)
  1056. #define cimg_for_in5C(img,c0,c1,c) cimg_for_in5((img)._spectrum,c0,c1,c)
  1057. #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)
  1058. #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)
  1059. #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)
  1060. #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)
  1061. #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)
  1062. #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)
  1063. #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)
  1064. #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)
  1065. #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)
  1066. #define cimg_for_in5XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
  1067. cimg_for_in5C(img,c0,c1,c) cimg_for_in5XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
  1068. #define cimg_for6(bound,i) \
  1069. for (int i = 0, _p2##i = 0, _p1##i = 0, \
  1070. _n1##i = 1>=(bound)?(int)(bound) - 1:1, \
  1071. _n2##i = 2>=(bound)?(int)(bound) - 1:2, \
  1072. _n3##i = 3>=(bound)?(int)(bound) - 1:3; \
  1073. _n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i); \
  1074. _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
  1075. #define cimg_for6X(img,x) cimg_for6((img)._width,x)
  1076. #define cimg_for6Y(img,y) cimg_for6((img)._height,y)
  1077. #define cimg_for6Z(img,z) cimg_for6((img)._depth,z)
  1078. #define cimg_for6C(img,c) cimg_for6((img)._spectrum,c)
  1079. #define cimg_for6XY(img,x,y) cimg_for6Y(img,y) cimg_for6X(img,x)
  1080. #define cimg_for6XZ(img,x,z) cimg_for6Z(img,z) cimg_for6X(img,x)
  1081. #define cimg_for6XC(img,x,c) cimg_for6C(img,c) cimg_for6X(img,x)
  1082. #define cimg_for6YZ(img,y,z) cimg_for6Z(img,z) cimg_for6Y(img,y)
  1083. #define cimg_for6YC(img,y,c) cimg_for6C(img,c) cimg_for6Y(img,y)
  1084. #define cimg_for6ZC(img,z,c) cimg_for6C(img,c) cimg_for6Z(img,z)
  1085. #define cimg_for6XYZ(img,x,y,z) cimg_for6Z(img,z) cimg_for6XY(img,x,y)
  1086. #define cimg_for6XZC(img,x,z,c) cimg_for6C(img,c) cimg_for6XZ(img,x,z)
  1087. #define cimg_for6YZC(img,y,z,c) cimg_for6C(img,c) cimg_for6YZ(img,y,z)
  1088. #define cimg_for6XYZC(img,x,y,z,c) cimg_for6C(img,c) cimg_for6XYZ(img,x,y,z)
  1089. #define cimg_for_in6(bound,i0,i1,i) \
  1090. for (int i = (int)(i0)<0?0:(int)(i0), \
  1091. _p2##i = i - 2<0?0:i - 2, \
  1092. _p1##i = i - 1<0?0:i - 1, \
  1093. _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \
  1094. _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \
  1095. _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3; \
  1096. i<=(int)(i1) && \
  1097. (_n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i)); \
  1098. _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
  1099. #define cimg_for_in6X(img,x0,x1,x) cimg_for_in6((img)._width,x0,x1,x)
  1100. #define cimg_for_in6Y(img,y0,y1,y) cimg_for_in6((img)._height,y0,y1,y)
  1101. #define cimg_for_in6Z(img,z0,z1,z) cimg_for_in6((img)._depth,z0,z1,z)
  1102. #define cimg_for_in6C(img,c0,c1,c) cimg_for_in6((img)._spectrum,c0,c1,c)
  1103. #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)
  1104. #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)
  1105. #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)
  1106. #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)
  1107. #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)
  1108. #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)
  1109. #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)
  1110. #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)
  1111. #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)
  1112. #define cimg_for_in6XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
  1113. cimg_for_in6C(img,c0,c1,c) cimg_for_in6XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
  1114. #define cimg_for7(bound,i) \
  1115. for (int i = 0, _p3##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. _n3##i = 3>=(bound)?(int)(bound) - 1:3; \
  1119. _n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i); \
  1120. _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
  1121. #define cimg_for7X(img,x) cimg_for7((img)._width,x)
  1122. #define cimg_for7Y(img,y) cimg_for7((img)._height,y)
  1123. #define cimg_for7Z(img,z) cimg_for7((img)._depth,z)
  1124. #define cimg_for7C(img,c) cimg_for7((img)._spectrum,c)
  1125. #define cimg_for7XY(img,x,y) cimg_for7Y(img,y) cimg_for7X(img,x)
  1126. #define cimg_for7XZ(img,x,z) cimg_for7Z(img,z) cimg_for7X(img,x)
  1127. #define cimg_for7XC(img,x,c) cimg_for7C(img,c) cimg_for7X(img,x)
  1128. #define cimg_for7YZ(img,y,z) cimg_for7Z(img,z) cimg_for7Y(img,y)
  1129. #define cimg_for7YC(img,y,c) cimg_for7C(img,c) cimg_for7Y(img,y)
  1130. #define cimg_for7ZC(img,z,c) cimg_for7C(img,c) cimg_for7Z(img,z)
  1131. #define cimg_for7XYZ(img,x,y,z) cimg_for7Z(img,z) cimg_for7XY(img,x,y)
  1132. #define cimg_for7XZC(img,x,z,c) cimg_for7C(img,c) cimg_for7XZ(img,x,z)
  1133. #define cimg_for7YZC(img,y,z,c) cimg_for7C(img,c) cimg_for7YZ(img,y,z)
  1134. #define cimg_for7XYZC(img,x,y,z,c) cimg_for7C(img,c) cimg_for7XYZ(img,x,y,z)
  1135. #define cimg_for_in7(bound,i0,i1,i) \
  1136. for (int i = (int)(i0)<0?0:(int)(i0), \
  1137. _p3##i = i - 3<0?0:i - 3, \
  1138. _p2##i = i - 2<0?0:i - 2, \
  1139. _p1##i = i - 1<0?0:i - 1, \
  1140. _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \
  1141. _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \
  1142. _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3; \
  1143. i<=(int)(i1) && \
  1144. (_n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i)); \
  1145. _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
  1146. #define cimg_for_in7X(img,x0,x1,x) cimg_for_in7((img)._width,x0,x1,x)
  1147. #define cimg_for_in7Y(img,y0,y1,y) cimg_for_in7((img)._height,y0,y1,y)
  1148. #define cimg_for_in7Z(img,z0,z1,z) cimg_for_in7((img)._depth,z0,z1,z)
  1149. #define cimg_for_in7C(img,c0,c1,c) cimg_for_in7((img)._spectrum,c0,c1,c)
  1150. #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)
  1151. #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)
  1152. #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)
  1153. #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)
  1154. #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)
  1155. #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)
  1156. #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)
  1157. #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)
  1158. #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)
  1159. #define cimg_for_in7XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
  1160. cimg_for_in7C(img,c0,c1,c) cimg_for_in7XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
  1161. #define cimg_for8(bound,i) \
  1162. for (int i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \
  1163. _n1##i = 1>=(bound)?(int)(bound) - 1:1, \
  1164. _n2##i = 2>=(bound)?(int)(bound) - 1:2, \
  1165. _n3##i = 3>=(bound)?(int)(bound) - 1:3, \
  1166. _n4##i = 4>=(bound)?(int)(bound) - 1:4; \
  1167. _n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
  1168. i==(_n4##i = _n3##i = _n2##i = --_n1##i); \
  1169. _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
  1170. #define cimg_for8X(img,x) cimg_for8((img)._width,x)
  1171. #define cimg_for8Y(img,y) cimg_for8((img)._height,y)
  1172. #define cimg_for8Z(img,z) cimg_for8((img)._depth,z)
  1173. #define cimg_for8C(img,c) cimg_for8((img)._spectrum,c)
  1174. #define cimg_for8XY(img,x,y) cimg_for8Y(img,y) cimg_for8X(img,x)
  1175. #define cimg_for8XZ(img,x,z) cimg_for8Z(img,z) cimg_for8X(img,x)
  1176. #define cimg_for8XC(img,x,c) cimg_for8C(img,c) cimg_for8X(img,x)
  1177. #define cimg_for8YZ(img,y,z) cimg_for8Z(img,z) cimg_for8Y(img,y)
  1178. #define cimg_for8YC(img,y,c) cimg_for8C(img,c) cimg_for8Y(img,y)
  1179. #define cimg_for8ZC(img,z,c) cimg_for8C(img,c) cimg_for8Z(img,z)
  1180. #define cimg_for8XYZ(img,x,y,z) cimg_for8Z(img,z) cimg_for8XY(img,x,y)
  1181. #define cimg_for8XZC(img,x,z,c) cimg_for8C(img,c) cimg_for8XZ(img,x,z)
  1182. #define cimg_for8YZC(img,y,z,c) cimg_for8C(img,c) cimg_for8YZ(img,y,z)
  1183. #define cimg_for8XYZC(img,x,y,z,c) cimg_for8C(img,c) cimg_for8XYZ(img,x,y,z)
  1184. #define cimg_for_in8(bound,i0,i1,i) \
  1185. for (int i = (int)(i0)<0?0:(int)(i0), \
  1186. _p3##i = i - 3<0?0:i - 3, \
  1187. _p2##i = i - 2<0?0:i - 2, \
  1188. _p1##i = i - 1<0?0:i - 1, \
  1189. _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \
  1190. _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \
  1191. _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3, \
  1192. _n4##i = i + 4>=(int)(bound)?(int)(bound) - 1:i + 4; \
  1193. i<=(int)(i1) && (_n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
  1194. i==(_n4##i = _n3##i = _n2##i = --_n1##i)); \
  1195. _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
  1196. #define cimg_for_in8X(img,x0,x1,x) cimg_for_in8((img)._width,x0,x1,x)
  1197. #define cimg_for_in8Y(img,y0,y1,y) cimg_for_in8((img)._height,y0,y1,y)
  1198. #define cimg_for_in8Z(img,z0,z1,z) cimg_for_in8((img)._depth,z0,z1,z)
  1199. #define cimg_for_in8C(img,c0,c1,c) cimg_for_in8((img)._spectrum,c0,c1,c)
  1200. #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)
  1201. #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)
  1202. #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)
  1203. #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)
  1204. #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)
  1205. #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)
  1206. #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)
  1207. #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)
  1208. #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)
  1209. #define cimg_for_in8XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
  1210. cimg_for_in8C(img,c0,c1,c) cimg_for_in8XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
  1211. #define cimg_for9(bound,i) \
  1212. for (int i = 0, _p4##i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \
  1213. _n1##i = 1>=(int)(bound)?(int)(bound) - 1:1, \
  1214. _n2##i = 2>=(int)(bound)?(int)(bound) - 1:2, \
  1215. _n3##i = 3>=(int)(bound)?(int)(bound) - 1:3, \
  1216. _n4##i = 4>=(int)(bound)?(int)(bound) - 1:4; \
  1217. _n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
  1218. i==(_n4##i = _n3##i = _n2##i = --_n1##i); \
  1219. _p4##i = _p3##i, _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
  1220. #define cimg_for9X(img,x) cimg_for9((img)._width,x)
  1221. #define cimg_for9Y(img,y) cimg_for9((img)._height,y)
  1222. #define cimg_for9Z(img,z) cimg_for9((img)._depth,z)
  1223. #define cimg_for9C(img,c) cimg_for9((img)._spectrum,c)
  1224. #define cimg_for9XY(img,x,y) cimg_for9Y(img,y) cimg_for9X(img,x)
  1225. #define cimg_for9XZ(img,x,z) cimg_for9Z(img,z) cimg_for9X(img,x)
  1226. #define cimg_for9XC(img,x,c) cimg_for9C(img,c) cimg_for9X(img,x)
  1227. #define cimg_for9YZ(img,y,z) cimg_for9Z(img,z) cimg_for9Y(img,y)
  1228. #define cimg_for9YC(img,y,c) cimg_for9C(img,c) cimg_for9Y(img,y)
  1229. #define cimg_for9ZC(img,z,c) cimg_for9C(img,c) cimg_for9Z(img,z)
  1230. #define cimg_for9XYZ(img,x,y,z) cimg_for9Z(img,z) cimg_for9XY(img,x,y)
  1231. #define cimg_for9XZC(img,x,z,c) cimg_for9C(img,c) cimg_for9XZ(img,x,z)
  1232. #define cimg_for9YZC(img,y,z,c) cimg_for9C(img,c) cimg_for9YZ(img,y,z)
  1233. #define cimg_for9XYZC(img,x,y,z,c) cimg_for9C(img,c) cimg_for9XYZ(img,x,y,z)
  1234. #define cimg_for_in9(bound,i0,i1,i) \
  1235. for (int i = (int)(i0)<0?0:(int)(i0), \
  1236. _p4##i = i - 4<0?0:i - 4, \
  1237. _p3##i = i - 3<0?0:i - 3, \
  1238. _p2##i = i - 2<0?0:i - 2, \
  1239. _p1##i = i - 1<0?0:i - 1, \
  1240. _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \
  1241. _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \
  1242. _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3, \
  1243. _n4##i = i + 4>=(int)(bound)?(int)(bound) - 1:i + 4; \
  1244. i<=(int)(i1) && (_n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
  1245. i==(_n4##i = _n3##i = _n2##i = --_n1##i)); \
  1246. _p4##i = _p3##i, _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
  1247. #define cimg_for_in9X(img,x0,x1,x) cimg_for_in9((img)._width,x0,x1,x)
  1248. #define cimg_for_in9Y(img,y0,y1,y) cimg_for_in9((img)._height,y0,y1,y)
  1249. #define cimg_for_in9Z(img,z0,z1,z) cimg_for_in9((img)._depth,z0,z1,z)
  1250. #define cimg_for_in9C(img,c0,c1,c) cimg_for_in9((img)._spectrum,c0,c1,c)
  1251. #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)
  1252. #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)
  1253. #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)
  1254. #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)
  1255. #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)
  1256. #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)
  1257. #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)
  1258. #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)
  1259. #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)
  1260. #define cimg_for_in9XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
  1261. cimg_for_in9C(img,c0,c1,c) cimg_for_in9XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
  1262. #define cimg_for2x2(img,x,y,z,c,I,T) \
  1263. cimg_for2((img)._height,y) for (int x = 0, \
  1264. _n1##x = (int)( \
  1265. (I[0] = (T)(img)(0,y,z,c)), \
  1266. (I[2] = (T)(img)(0,_n1##y,z,c)), \
  1267. 1>=(img)._width?(img).width() - 1:1); \
  1268. (_n1##x<(img).width() && ( \
  1269. (I[1] = (T)(img)(_n1##x,y,z,c)), \
  1270. (I[3] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
  1271. x==--_n1##x; \
  1272. I[0] = I[1], \
  1273. I[2] = I[3], \
  1274. ++x, ++_n1##x)
  1275. #define cimg_for_in2x2(img,x0,y0,x1,y1,x,y,z,c,I,T) \
  1276. cimg_for_in2((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
  1277. _n1##x = (int)( \
  1278. (I[0] = (T)(img)(x,y,z,c)), \
  1279. (I[2] = (T)(img)(x,_n1##y,z,c)), \
  1280. x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \
  1281. x<=(int)(x1) && ((_n1##x<(img).width() && ( \
  1282. (I[1] = (T)(img)(_n1##x,y,z,c)), \
  1283. (I[3] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
  1284. x==--_n1##x); \
  1285. I[0] = I[1], \
  1286. I[2] = I[3], \
  1287. ++x, ++_n1##x)
  1288. #define cimg_for3x3(img,x,y,z,c,I,T) \
  1289. cimg_for3((img)._height,y) for (int x = 0, \
  1290. _p1##x = 0, \
  1291. _n1##x = (int)( \
  1292. (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
  1293. (I[3] = I[4] = (T)(img)(0,y,z,c)), \
  1294. (I[6] = I[7] = (T)(img)(0,_n1##y,z,c)), \
  1295. 1>=(img)._width?(img).width() - 1:1); \
  1296. (_n1##x<(img).width() && ( \
  1297. (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
  1298. (I[5] = (T)(img)(_n1##x,y,z,c)), \
  1299. (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
  1300. x==--_n1##x; \
  1301. I[0] = I[1], I[1] = I[2], \
  1302. I[3] = I[4], I[4] = I[5], \
  1303. I[6] = I[7], I[7] = I[8], \
  1304. _p1##x = x++, ++_n1##x)
  1305. #define cimg_for_in3x3(img,x0,y0,x1,y1,x,y,z,c,I,T) \
  1306. cimg_for_in3((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
  1307. _p1##x = x - 1<0?0:x - 1, \
  1308. _n1##x = (int)( \
  1309. (I[0] = (T)(img)(_p1##x,_p1##y,z,c)), \
  1310. (I[3] = (T)(img)(_p1##x,y,z,c)), \
  1311. (I[6] = (T)(img)(_p1##x,_n1##y,z,c)), \
  1312. (I[1] = (T)(img)(x,_p1##y,z,c)), \
  1313. (I[4] = (T)(img)(x,y,z,c)), \
  1314. (I[7] = (T)(img)(x,_n1##y,z,c)), \
  1315. x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \
  1316. x<=(int)(x1) && ((_n1##x<(img).width() && ( \
  1317. (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
  1318. (I[5] = (T)(img)(_n1##x,y,z,c)), \
  1319. (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
  1320. x==--_n1##x); \
  1321. I[0] = I[1], I[1] = I[2], \
  1322. I[3] = I[4], I[4] = I[5], \
  1323. I[6] = I[7], I[7] = I[8], \
  1324. _p1##x = x++, ++_n1##x)
  1325. #define cimg_for4x4(img,x,y,z,c,I,T) \
  1326. cimg_for4((img)._height,y) for (int x = 0, \
  1327. _p1##x = 0, \
  1328. _n1##x = 1>=(img)._width?(img).width() - 1:1, \
  1329. _n2##x = (int)( \
  1330. (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
  1331. (I[4] = I[5] = (T)(img)(0,y,z,c)), \
  1332. (I[8] = I[9] = (T)(img)(0,_n1##y,z,c)), \
  1333. (I[12] = I[13] = (T)(img)(0,_n2##y,z,c)), \
  1334. (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
  1335. (I[6] = (T)(img)(_n1##x,y,z,c)), \
  1336. (I[10] = (T)(img)(_n1##x,_n1##y,z,c)), \
  1337. (I[14] = (T)(img)(_n1##x,_n2##y,z,c)), \
  1338. 2>=(img)._width?(img).width() - 1:2); \
  1339. (_n2##x<(img).width() && ( \
  1340. (I[3] = (T)(img)(_n2##x,_p1##y,z,c)), \
  1341. (I[7] = (T)(img)(_n2##x,y,z,c)), \
  1342. (I[11] = (T)(img)(_n2##x,_n1##y,z,c)), \
  1343. (I[15] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
  1344. _n1##x==--_n2##x || x==(_n2##x = --_n1##x); \
  1345. I[0] = I[1], I[1] = I[2], I[2] = I[3], \
  1346. I[4] = I[5], I[5] = I[6], I[6] = I[7], \
  1347. I[8] = I[9], I[9] = I[10], I[10] = I[11], \
  1348. I[12] = I[13], I[13] = I[14], I[14] = I[15], \
  1349. _p1##x = x++, ++_n1##x, ++_n2##x)
  1350. #define cimg_for_in4x4(img,x0,y0,x1,y1,x,y,z,c,I,T) \
  1351. cimg_for_in4((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
  1352. _p1##x = x - 1<0?0:x - 1, \
  1353. _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \
  1354. _n2##x = (int)( \
  1355. (I[0] = (T)(img)(_p1##x,_p1##y,z,c)), \
  1356. (I[4] = (T)(img)(_p1##x,y,z,c)), \
  1357. (I[8] = (T)(img)(_p1##x,_n1##y,z,c)), \
  1358. (I[12] = (T)(img)(_p1##x,_n2##y,z,c)), \
  1359. (I[1] = (T)(img)(x,_p1##y,z,c)), \
  1360. (I[5] = (T)(img)(x,y,z,c)), \
  1361. (I[9] = (T)(img)(x,_n1##y,z,c)), \
  1362. (I[13] = (T)(img)(x,_n2##y,z,c)), \
  1363. (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
  1364. (I[6] = (T)(img)(_n1##x,y,z,c)), \
  1365. (I[10] = (T)(img)(_n1##x,_n1##y,z,c)), \
  1366. (I[14] = (T)(img)(_n1##x,_n2##y,z,c)), \
  1367. x + 2>=(int)(img)._width?(img).width() - 1:x + 2); \
  1368. x<=(int)(x1) && ((_n2##x<(img).width() && ( \
  1369. (I[3] = (T)(img)(_n2##x,_p1##y,z,c)), \
  1370. (I[7] = (T)(img)(_n2##x,y,z,c)), \
  1371. (I[11] = (T)(img)(_n2##x,_n1##y,z,c)), \
  1372. (I[15] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
  1373. _n1##x==--_n2##x || x==(_n2##x = --_n1##x)); \
  1374. I[0] = I[1], I[1] = I[2], I[2] = I[3], \
  1375. I[4] = I[5], I[5] = I[6], I[6] = I[7], \
  1376. I[8] = I[9], I[9] = I[10], I[10] = I[11], \
  1377. I[12] = I[13], I[13] = I[14], I[14] = I[15], \
  1378. _p1##x = x++, ++_n1##x, ++_n2##x)
  1379. #define cimg_for5x5(img,x,y,z,c,I,T) \
  1380. cimg_for5((img)._height,y) for (int x = 0, \
  1381. _p2##x = 0, _p1##x = 0, \
  1382. _n1##x = 1>=(img)._width?(img).width() - 1:1, \
  1383. _n2##x = (int)( \
  1384. (I[0] = I[1] = I[2] = (T)(img)(_p2##x,_p2##y,z,c)), \
  1385. (I[5] = I[6] = I[7] = (T)(img)(0,_p1##y,z,c)), \
  1386. (I[10] = I[11] = I[12] = (T)(img)(0,y,z,c)), \
  1387. (I[15] = I[16] = I[17] = (T)(img)(0,_n1##y,z,c)), \
  1388. (I[20] = I[21] = I[22] = (T)(img)(0,_n2##y,z,c)), \
  1389. (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
  1390. (I[8] = (T)(img)(_n1##x,_p1##y,z,c)), \
  1391. (I[13] = (T)(img)(_n1##x,y,z,c)), \
  1392. (I[18] = (T)(img)(_n1##x,_n1##y,z,c)), \
  1393. (I[23] = (T)(img)(_n1##x,_n2##y,z,c)), \
  1394. 2>=(img)._width?(img).width() - 1:2); \
  1395. (_n2##x<(img).width() && ( \
  1396. (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
  1397. (I[9] = (T)(img)(_n2##x,_p1##y,z,c)), \
  1398. (I[14] = (T)(img)(_n2##x,y,z,c)), \
  1399. (I[19] = (T)(img)(_n2##x,_n1##y,z,c)), \
  1400. (I[24] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
  1401. _n1##x==--_n2##x || x==(_n2##x = --_n1##x); \
  1402. I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], \
  1403. I[5] = I[6], I[6] = I[7], I[7] = I[8], I[8] = I[9], \
  1404. I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], \
  1405. I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], \
  1406. I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \
  1407. _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x)
  1408. #define cimg_for_in5x5(img,x0,y0,x1,y1,x,y,z,c,I,T) \
  1409. cimg_for_in5((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
  1410. _p2##x = x - 2<0?0:x - 2, \
  1411. _p1##x = x - 1<0?0:x - 1, \
  1412. _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \
  1413. _n2##x = (int)( \
  1414. (I[0] = (T)(img)(_p2##x,_p2##y,z,c)), \
  1415. (I[5] = (T)(img)(_p2##x,_p1##y,z,c)), \
  1416. (I[10] = (T)(img)(_p2##x,y,z,c)), \
  1417. (I[15] = (T)(img)(_p2##x,_n1##y,z,c)), \
  1418. (I[20] = (T)(img)(_p2##x,_n2##y,z,c)), \
  1419. (I[1] = (T)(img)(_p1##x,_p2##y,z,c)), \
  1420. (I[6] = (T)(img)(_p1##x,_p1##y,z,c)), \
  1421. (I[11] = (T)(img)(_p1##x,y,z,c)), \
  1422. (I[16] = (T)(img)(_p1##x,_n1##y,z,c)), \
  1423. (I[21] = (T)(img)(_p1##x,_n2##y,z,c)), \
  1424. (I[2] = (T)(img)(x,_p2##y,z,c)), \
  1425. (I[7] = (T)(img)(x,_p1##y,z,c)), \
  1426. (I[12] = (T)(img)(x,y,z,c)), \
  1427. (I[17] = (T)(img)(x,_n1##y,z,c)), \
  1428. (I[22] = (T)(img)(x,_n2##y,z,c)), \
  1429. (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
  1430. (I[8] = (T)(img)(_n1##x,_p1##y,z,c)), \
  1431. (I[13] = (T)(img)(_n1##x,y,z,c)), \
  1432. (I[18] = (T)(img)(_n1##x,_n1##y,z,c)), \
  1433. (I[23] = (T)(img)(_n1##x,_n2##y,z,c)), \
  1434. x + 2>=(int)(img)._width?(img).width() - 1:x + 2); \
  1435. x<=(int)(x1) && ((_n2##x<(img).width() && ( \
  1436. (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
  1437. (I[9] = (T)(img)(_n2##x,_p1##y,z,c)), \
  1438. (I[14] = (T)(img)(_n2##x,y,z,c)), \
  1439. (I[19] = (T)(img)(_n2##x,_n1##y,z,c)), \
  1440. (I[24] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
  1441. _n1##x==--_n2##x || x==(_n2##x = --_n1##x)); \
  1442. I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], \
  1443. I[5] = I[6], I[6] = I[7], I[7] = I[8], I[8] = I[9], \
  1444. I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], \
  1445. I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], \
  1446. I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \
  1447. _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x)
  1448. #define cimg_for6x6(img,x,y,z,c,I,T) \
  1449. cimg_for6((img)._height,y) for (int x = 0, \
  1450. _p2##x = 0, _p1##x = 0, \
  1451. _n1##x = 1>=(img)._width?(img).width() - 1:1, \
  1452. _n2##x = 2>=(img)._width?(img).width() - 1:2, \
  1453. _n3##x = (int)( \
  1454. (I[0] = I[1] = I[2] = (T)(img)(_p2##x,_p2##y,z,c)), \
  1455. (I[6] = I[7] = I[8] = (T)(img)(0,_p1##y,z,c)), \
  1456. (I[12] = I[13] = I[14] = (T)(img)(0,y,z,c)), \
  1457. (I[18] = I[19] = I[20] = (T)(img)(0,_n1##y,z,c)), \
  1458. (I[24] = I[25] = I[26] = (T)(img)(0,_n2##y,z,c)), \
  1459. (I[30] = I[31] = I[32] = (T)(img)(0,_n3##y,z,c)), \
  1460. (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
  1461. (I[9] = (T)(img)(_n1##x,_p1##y,z,c)), \
  1462. (I[15] = (T)(img)(_n1##x,y,z,c)), \
  1463. (I[21] = (T)(img)(_n1##x,_n1##y,z,c)), \
  1464. (I[27] = (T)(img)(_n1##x,_n2##y,z,c)), \
  1465. (I[33] = (T)(img)(_n1##x,_n3##y,z,c)), \
  1466. (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
  1467. (I[10] = (T)(img)(_n2##x,_p1##y,z,c)), \
  1468. (I[16] = (T)(img)(_n2##x,y,z,c)), \
  1469. (I[22] = (T)(img)(_n2##x,_n1##y,z,c)), \
  1470. (I[28] = (T)(img)(_n2##x,_n2##y,z,c)), \
  1471. (I[34] = (T)(img)(_n2##x,_n3##y,z,c)), \
  1472. 3>=(img)._width?(img).width() - 1:3); \
  1473. (_n3##x<(img).width() && ( \
  1474. (I[5] = (T)(img)(_n3##x,_p2##y,z,c)), \
  1475. (I[11] = (T)(img)(_n3##x,_p1##y,z,c)), \
  1476. (I[17] = (T)(img)(_n3##x,y,z,c)), \
  1477. (I[23] = (T)(img)(_n3##x,_n1##y,z,c)), \
  1478. (I[29] = (T)(img)(_n3##x,_n2##y,z,c)), \
  1479. (I[35] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
  1480. _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3## x = _n2##x = --_n1##x); \
  1481. I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], \
  1482. I[6] = I[7], I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], \
  1483. I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \
  1484. I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \
  1485. I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], \
  1486. I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \
  1487. _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
  1488. #define cimg_for_in6x6(img,x0,y0,x1,y1,x,y,z,c,I,T) \
  1489. cimg_for_in6((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)x0, \
  1490. _p2##x = x - 2<0?0:x - 2, \
  1491. _p1##x = x - 1<0?0:x - 1, \
  1492. _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \
  1493. _n2##x = x + 2>=(int)(img)._width?(img).width() - 1:x + 2, \
  1494. _n3##x = (int)( \
  1495. (I[0] = (T)(img)(_p2##x,_p2##y,z,c)), \
  1496. (I[6] = (T)(img)(_p2##x,_p1##y,z,c)), \
  1497. (I[12] = (T)(img)(_p2##x,y,z,c)), \
  1498. (I[18] = (T)(img)(_p2##x,_n1##y,z,c)), \
  1499. (I[24] = (T)(img)(_p2##x,_n2##y,z,c)), \
  1500. (I[30] = (T)(img)(_p2##x,_n3##y,z,c)), \
  1501. (I[1] = (T)(img)(_p1##x,_p2##y,z,c)), \
  1502. (I[7] = (T)(img)(_p1##x,_p1##y,z,c)), \
  1503. (I[13] = (T)(img)(_p1##x,y,z,c)), \
  1504. (I[19] = (T)(img)(_p1##x,_n1##y,z,c)), \
  1505. (I[25] = (T)(img)(_p1##x,_n2##y,z,c)), \
  1506. (I[31] = (T)(img)(_p1##x,_n3##y,z,c)), \
  1507. (I[2] = (T)(img)(x,_p2##y,z,c)), \
  1508. (I[8] = (T)(img)(x,_p1##y,z,c)), \
  1509. (I[14] = (T)(img)(x,y,z,c)), \
  1510. (I[20] = (T)(img)(x,_n1##y,z,c)), \
  1511. (I[26] = (T)(img)(x,_n2##y,z,c)), \
  1512. (I[32] = (T)(img)(x,_n3##y,z,c)), \
  1513. (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
  1514. (I[9] = (T)(img)(_n1##x,_p1##y,z,c)), \
  1515. (I[15] = (T)(img)(_n1##x,y,z,c)), \
  1516. (I[21] = (T)(img)(_n1##x,_n1##y,z,c)), \
  1517. (I[27] = (T)(img)(_n1##x,_n2##y,z,c)), \
  1518. (I[33] = (T)(img)(_n1##x,_n3##y,z,c)), \
  1519. (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
  1520. (I[10] = (T)(img)(_n2##x,_p1##y,z,c)), \
  1521. (I[16] = (T)(img)(_n2##x,y,z,c)), \
  1522. (I[22] = (T)(img)(_n2##x,_n1##y,z,c)), \
  1523. (I[28] = (T)(img)(_n2##x,_n2##y,z,c)), \
  1524. (I[34] = (T)(img)(_n2##x,_n3##y,z,c)), \
  1525. x + 3>=(int)(img)._width?(img).width() - 1:x + 3); \
  1526. x<=(int)(x1) && ((_n3##x<(img).width() && ( \
  1527. (I[5] = (T)(img)(_n3##x,_p2##y,z,c)), \
  1528. (I[11] = (T)(img)(_n3##x,_p1##y,z,c)), \
  1529. (I[17] = (T)(img)(_n3##x,y,z,c)), \
  1530. (I[23] = (T)(img)(_n3##x,_n1##y,z,c)), \
  1531. (I[29] = (T)(img)(_n3##x,_n2##y,z,c)), \
  1532. (I[35] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
  1533. _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3## x = _n2##x = --_n1##x)); \
  1534. I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], \
  1535. I[6] = I[7], I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], \
  1536. I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \
  1537. I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \
  1538. I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], \
  1539. I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \
  1540. _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
  1541. #define cimg_for7x7(img,x,y,z,c,I,T) \
  1542. cimg_for7((img)._height,y) for (int x = 0, \
  1543. _p3##x = 0, _p2##x = 0, _p1##x = 0, \
  1544. _n1##x = 1>=(img)._width?(img).width() - 1:1, \
  1545. _n2##x = 2>=(img)._width?(img).width() - 1:2, \
  1546. _n3##x = (int)( \
  1547. (I[0] = I[1] = I[2] = I[3] = (T)(img)(_p3##x,_p3##y,z,c)), \
  1548. (I[7] = I[8] = I[9] = I[10] = (T)(img)(0,_p2##y,z,c)), \
  1549. (I[14] = I[15] = I[16] = I[17] = (T)(img)(0,_p1##y,z,c)), \
  1550. (I[21] = I[22] = I[23] = I[24] = (T)(img)(0,y,z,c)), \
  1551. (I[28] = I[29] = I[30] = I[31] = (T)(img)(0,_n1##y,z,c)), \
  1552. (I[35] = I[36] = I[37] = I[38] = (T)(img)(0,_n2##y,z,c)), \
  1553. (I[42] = I[43] = I[44] = I[45] = (T)(img)(0,_n3##y,z,c)), \
  1554. (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
  1555. (I[11] = (T)(img)(_n1##x,_p2##y,z,c)), \
  1556. (I[18] = (T)(img)(_n1##x,_p1##y,z,c)), \
  1557. (I[25] = (T)(img)(_n1##x,y,z,c)), \
  1558. (I[32] = (T)(img)(_n1##x,_n1##y,z,c)), \
  1559. (I[39] = (T)(img)(_n1##x,_n2##y,z,c)), \
  1560. (I[46] = (T)(img)(_n1##x,_n3##y,z,c)), \
  1561. (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
  1562. (I[12] = (T)(img)(_n2##x,_p2##y,z,c)), \
  1563. (I[19] = (T)(img)(_n2##x,_p1##y,z,c)), \
  1564. (I[26] = (T)(img)(_n2##x,y,z,c)), \
  1565. (I[33] = (T)(img)(_n2##x,_n1##y,z,c)), \
  1566. (I[40] = (T)(img)(_n2##x,_n2##y,z,c)), \
  1567. (I[47] = (T)(img)(_n2##x,_n3##y,z,c)), \
  1568. 3>=(img)._width?(img).width() - 1:3); \
  1569. (_n3##x<(img).width() && ( \
  1570. (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
  1571. (I[13] = (T)(img)(_n3##x,_p2##y,z,c)), \
  1572. (I[20] = (T)(img)(_n3##x,_p1##y,z,c)), \
  1573. (I[27] = (T)(img)(_n3##x,y,z,c)), \
  1574. (I[34] = (T)(img)(_n3##x,_n1##y,z,c)), \
  1575. (I[41] = (T)(img)(_n3##x,_n2##y,z,c)), \
  1576. (I[48] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
  1577. _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3##x = _n2##x = --_n1##x); \
  1578. I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], \
  1579. I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], \
  1580. I[14] = I[15], I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], \
  1581. I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], I[26] = I[27], \
  1582. I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], \
  1583. I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], \
  1584. I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], I[47] = I[48], \
  1585. _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
  1586. #define cimg_for_in7x7(img,x0,y0,x1,y1,x,y,z,c,I,T) \
  1587. cimg_for_in7((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
  1588. _p3##x = x - 3<0?0:x - 3, \
  1589. _p2##x = x - 2<0?0:x - 2, \
  1590. _p1##x = x - 1<0?0:x - 1, \
  1591. _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \
  1592. _n2##x = x + 2>=(int)(img)._width?(img).width() - 1:x + 2, \
  1593. _n3##x = (int)( \
  1594. (I[0] = (T)(img)(_p3##x,_p3##y,z,c)), \
  1595. (I[7] = (T)(img)(_p3##x,_p2##y,z,c)), \
  1596. (I[14] = (T)(img)(_p3##x,_p1##y,z,c)), \
  1597. (I[21] = (T)(img)(_p3##x,y,z,c)), \
  1598. (I[28] = (T)(img)(_p3##x,_n1##y,z,c)), \
  1599. (I[35] = (T)(img)(_p3##x,_n2##y,z,c)), \
  1600. (I[42] = (T)(img)(_p3##x,_n3##y,z,c)), \
  1601. (I[1] = (T)(img)(_p2##x,_p3##y,z,c)), \
  1602. (I[8] = (T)(img)(_p2##x,_p2##y,z,c)), \
  1603. (I[15] = (T)(img)(_p2##x,_p1##y,z,c)), \
  1604. (I[22] = (T)(img)(_p2##x,y,z,c)), \
  1605. (I[29] = (T)(img)(_p2##x,_n1##y,z,c)), \
  1606. (I[36] = (T)(img)(_p2##x,_n2##y,z,c)), \
  1607. (I[43] = (T)(img)(_p2##x,_n3##y,z,c)), \
  1608. (I[2] = (T)(img)(_p1##x,_p3##y,z,c)), \
  1609. (I[9] = (T)(img)(_p1##x,_p2##y,z,c)), \
  1610. (I[16] = (T)(img)(_p1##x,_p1##y,z,c)), \
  1611. (I[23] = (T)(img)(_p1##x,y,z,c)), \
  1612. (I[30] = (T)(img)(_p1##x,_n1##y,z,c)), \
  1613. (I[37] = (T)(img)(_p1##x,_n2##y,z,c)), \
  1614. (I[44] = (T)(img)(_p1##x,_n3##y,z,c)), \
  1615. (I[3] = (T)(img)(x,_p3##y,z,c)), \
  1616. (I[10] = (T)(img)(x,_p2##y,z,c)), \
  1617. (I[17] = (T)(img)(x,_p1##y,z,c)), \
  1618. (I[24] = (T)(img)(x,y,z,c)), \
  1619. (I[31] = (T)(img)(x,_n1##y,z,c)), \
  1620. (I[38] = (T)(img)(x,_n2##y,z,c)), \
  1621. (I[45] = (T)(img)(x,_n3##y,z,c)), \
  1622. (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
  1623. (I[11] = (T)(img)(_n1##x,_p2##y,z,c)), \
  1624. (I[18] = (T)(img)(_n1##x,_p1##y,z,c)), \
  1625. (I[25] = (T)(img)(_n1##x,y,z,c)), \
  1626. (I[32] = (T)(img)(_n1##x,_n1##y,z,c)), \
  1627. (I[39] = (T)(img)(_n1##x,_n2##y,z,c)), \
  1628. (I[46] = (T)(img)(_n1##x,_n3##y,z,c)), \
  1629. (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
  1630. (I[12] = (T)(img)(_n2##x,_p2##y,z,c)), \
  1631. (I[19] = (T)(img)(_n2##x,_p1##y,z,c)), \
  1632. (I[26] = (T)(img)(_n2##x,y,z,c)), \
  1633. (I[33] = (T)(img)(_n2##x,_n1##y,z,c)), \
  1634. (I[40] = (T)(img)(_n2##x,_n2##y,z,c)), \
  1635. (I[47] = (T)(img)(_n2##x,_n3##y,z,c)), \
  1636. x + 3>=(int)(img)._width?(img).width() - 1:x + 3); \
  1637. x<=(int)(x1) && ((_n3##x<(img).width() && ( \
  1638. (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
  1639. (I[13] = (T)(img)(_n3##x,_p2##y,z,c)), \
  1640. (I[20] = (T)(img)(_n3##x,_p1##y,z,c)), \
  1641. (I[27] = (T)(img)(_n3##x,y,z,c)), \
  1642. (I[34] = (T)(img)(_n3##x,_n1##y,z,c)), \
  1643. (I[41] = (T)(img)(_n3##x,_n2##y,z,c)), \
  1644. (I[48] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
  1645. _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3##x = _n2##x = --_n1##x)); \
  1646. I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], \
  1647. I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], \
  1648. I[14] = I[15], I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], \
  1649. I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], I[26] = I[27], \
  1650. I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], \
  1651. I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], \
  1652. I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], I[47] = I[48], \
  1653. _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
  1654. #define cimg_for8x8(img,x,y,z,c,I,T) \
  1655. cimg_for8((img)._height,y) for (int x = 0, \
  1656. _p3##x = 0, _p2##x = 0, _p1##x = 0, \
  1657. _n1##x = 1>=((img)._width)?(img).width() - 1:1, \
  1658. _n2##x = 2>=((img)._width)?(img).width() - 1:2, \
  1659. _n3##x = 3>=((img)._width)?(img).width() - 1:3, \
  1660. _n4##x = (int)( \
  1661. (I[0] = I[1] = I[2] = I[3] = (T)(img)(_p3##x,_p3##y,z,c)), \
  1662. (I[8] = I[9] = I[10] = I[11] = (T)(img)(0,_p2##y,z,c)), \
  1663. (I[16] = I[17] = I[18] = I[19] = (T)(img)(0,_p1##y,z,c)), \
  1664. (I[24] = I[25] = I[26] = I[27] = (T)(img)(0,y,z,c)), \
  1665. (I[32] = I[33] = I[34] = I[35] = (T)(img)(0,_n1##y,z,c)), \
  1666. (I[40] = I[41] = I[42] = I[43] = (T)(img)(0,_n2##y,z,c)), \
  1667. (I[48] = I[49] = I[50] = I[51] = (T)(img)(0,_n3##y,z,c)), \
  1668. (I[56] = I[57] = I[58] = I[59] = (T)(img)(0,_n4##y,z,c)), \
  1669. (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
  1670. (I[12] = (T)(img)(_n1##x,_p2##y,z,c)), \
  1671. (I[20] = (T)(img)(_n1##x,_p1##y,z,c)), \
  1672. (I[28] = (T)(img)(_n1##x,y,z,c)), \
  1673. (I[36] = (T)(img)(_n1##x,_n1##y,z,c)), \
  1674. (I[44] = (T)(img)(_n1##x,_n2##y,z,c)), \
  1675. (I[52] = (T)(img)(_n1##x,_n3##y,z,c)), \
  1676. (I[60] = (T)(img)(_n1##x,_n4##y,z,c)), \
  1677. (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
  1678. (I[13] = (T)(img)(_n2##x,_p2##y,z,c)), \
  1679. (I[21] = (T)(img)(_n2##x,_p1##y,z,c)), \
  1680. (I[29] = (T)(img)(_n2##x,y,z,c)), \
  1681. (I[37] = (T)(img)(_n2##x,_n1##y,z,c)), \
  1682. (I[45] = (T)(img)(_n2##x,_n2##y,z,c)), \
  1683. (I[53] = (T)(img)(_n2##x,_n3##y,z,c)), \
  1684. (I[61] = (T)(img)(_n2##x,_n4##y,z,c)), \
  1685. (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
  1686. (I[14] = (T)(img)(_n3##x,_p2##y,z,c)), \
  1687. (I[22] = (T)(img)(_n3##x,_p1##y,z,c)), \
  1688. (I[30] = (T)(img)(_n3##x,y,z,c)), \
  1689. (I[38] = (T)(img)(_n3##x,_n1##y,z,c)), \
  1690. (I[46] = (T)(img)(_n3##x,_n2##y,z,c)), \
  1691. (I[54] = (T)(img)(_n3##x,_n3##y,z,c)), \
  1692. (I[62] = (T)(img)(_n3##x,_n4##y,z,c)), \
  1693. 4>=((img)._width)?(img).width() - 1:4); \
  1694. (_n4##x<(img).width() && ( \
  1695. (I[7] = (T)(img)(_n4##x,_p3##y,z,c)), \
  1696. (I[15] = (T)(img)(_n4##x,_p2##y,z,c)), \
  1697. (I[23] = (T)(img)(_n4##x,_p1##y,z,c)), \
  1698. (I[31] = (T)(img)(_n4##x,y,z,c)), \
  1699. (I[39] = (T)(img)(_n4##x,_n1##y,z,c)), \
  1700. (I[47] = (T)(img)(_n4##x,_n2##y,z,c)), \
  1701. (I[55] = (T)(img)(_n4##x,_n3##y,z,c)), \
  1702. (I[63] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
  1703. _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x); \
  1704. 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], \
  1705. 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], \
  1706. 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], \
  1707. 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], \
  1708. 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], \
  1709. 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], \
  1710. 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], \
  1711. 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], \
  1712. _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
  1713. #define cimg_for_in8x8(img,x0,y0,x1,y1,x,y,z,c,I,T) \
  1714. cimg_for_in8((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
  1715. _p3##x = x - 3<0?0:x - 3, \
  1716. _p2##x = x - 2<0?0:x - 2, \
  1717. _p1##x = x - 1<0?0:x - 1, \
  1718. _n1##x = x + 1>=(img).width()?(img).width() - 1:x + 1, \
  1719. _n2##x = x + 2>=(img).width()?(img).width() - 1:x + 2, \
  1720. _n3##x = x + 3>=(img).width()?(img).width() - 1:x + 3, \
  1721. _n4##x = (int)( \
  1722. (I[0] = (T)(img)(_p3##x,_p3##y,z,c)), \
  1723. (I[8] = (T)(img)(_p3##x,_p2##y,z,c)), \
  1724. (I[16] = (T)(img)(_p3##x,_p1##y,z,c)), \
  1725. (I[24] = (T)(img)(_p3##x,y,z,c)), \
  1726. (I[32] = (T)(img)(_p3##x,_n1##y,z,c)), \
  1727. (I[40] = (T)(img)(_p3##x,_n2##y,z,c)), \
  1728. (I[48] = (T)(img)(_p3##x,_n3##y,z,c)), \
  1729. (I[56] = (T)(img)(_p3##x,_n4##y,z,c)), \
  1730. (I[1] = (T)(img)(_p2##x,_p3##y,z,c)), \
  1731. (I[9] = (T)(img)(_p2##x,_p2##y,z,c)), \
  1732. (I[17] = (T)(img)(_p2##x,_p1##y,z,c)), \
  1733. (I[25] = (T)(img)(_p2##x,y,z,c)), \
  1734. (I[33] = (T)(img)(_p2##x,_n1##y,z,c)), \
  1735. (I[41] = (T)(img)(_p2##x,_n2##y,z,c)), \
  1736. (I[49] = (T)(img)(_p2##x,_n3##y,z,c)), \
  1737. (I[57] = (T)(img)(_p2##x,_n4##y,z,c)), \
  1738. (I[2] = (T)(img)(_p1##x,_p3##y,z,c)), \
  1739. (I[10] = (T)(img)(_p1##x,_p2##y,z,c)), \
  1740. (I[18] = (T)(img)(_p1##x,_p1##y,z,c)), \
  1741. (I[26] = (T)(img)(_p1##x,y,z,c)), \
  1742. (I[34] = (T)(img)(_p1##x,_n1##y,z,c)), \
  1743. (I[42] = (T)(img)(_p1##x,_n2##y,z,c)), \
  1744. (I[50] = (T)(img)(_p1##x,_n3##y,z,c)), \
  1745. (I[58] = (T)(img)(_p1##x,_n4##y,z,c)), \
  1746. (I[3] = (T)(img)(x,_p3##y,z,c)), \
  1747. (I[11] = (T)(img)(x,_p2##y,z,c)), \
  1748. (I[19] = (T)(img)(x,_p1##y,z,c)), \
  1749. (I[27] = (T)(img)(x,y,z,c)), \
  1750. (I[35] = (T)(img)(x,_n1##y,z,c)), \
  1751. (I[43] = (T)(img)(x,_n2##y,z,c)), \
  1752. (I[51] = (T)(img)(x,_n3##y,z,c)), \
  1753. (I[59] = (T)(img)(x,_n4##y,z,c)), \
  1754. (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
  1755. (I[12] = (T)(img)(_n1##x,_p2##y,z,c)), \
  1756. (I[20] = (T)(img)(_n1##x,_p1##y,z,c)), \
  1757. (I[28] = (T)(img)(_n1##x,y,z,c)), \
  1758. (I[36] = (T)(img)(_n1##x,_n1##y,z,c)), \
  1759. (I[44] = (T)(img)(_n1##x,_n2##y,z,c)), \
  1760. (I[52] = (T)(img)(_n1##x,_n3##y,z,c)), \
  1761. (I[60] = (T)(img)(_n1##x,_n4##y,z,c)), \
  1762. (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
  1763. (I[13] = (T)(img)(_n2##x,_p2##y,z,c)), \
  1764. (I[21] = (T)(img)(_n2##x,_p1##y,z,c)), \
  1765. (I[29] = (T)(img)(_n2##x,y,z,c)), \
  1766. (I[37] = (T)(img)(_n2##x,_n1##y,z,c)), \
  1767. (I[45] = (T)(img)(_n2##x,_n2##y,z,c)), \
  1768. (I[53] = (T)(img)(_n2##x,_n3##y,z,c)), \
  1769. (I[61] = (T)(img)(_n2##x,_n4##y,z,c)), \
  1770. (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
  1771. (I[14] = (T)(img)(_n3##x,_p2##y,z,c)), \
  1772. (I[22] = (T)(img)(_n3##x,_p1##y,z,c)), \
  1773. (I[30] = (T)(img)(_n3##x,y,z,c)), \
  1774. (I[38] = (T)(img)(_n3##x,_n1##y,z,c)), \
  1775. (I[46] = (T)(img)(_n3##x,_n2##y,z,c)), \
  1776. (I[54] = (T)(img)(_n3##x,_n3##y,z,c)), \
  1777. (I[62] = (T)(img)(_n3##x,_n4##y,z,c)), \
  1778. x + 4>=(img).width()?(img).width() - 1:x + 4); \
  1779. x<=(int)(x1) && ((_n4##x<(img).width() && ( \
  1780. (I[7] = (T)(img)(_n4##x,_p3##y,z,c)), \
  1781. (I[15] = (T)(img)(_n4##x,_p2##y,z,c)), \
  1782. (I[23] = (T)(img)(_n4##x,_p1##y,z,c)), \
  1783. (I[31] = (T)(img)(_n4##x,y,z,c)), \
  1784. (I[39] = (T)(img)(_n4##x,_n1##y,z,c)), \
  1785. (I[47] = (T)(img)(_n4##x,_n2##y,z,c)), \
  1786. (I[55] = (T)(img)(_n4##x,_n3##y,z,c)), \
  1787. (I[63] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
  1788. _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x)); \
  1789. 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], \
  1790. 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], \
  1791. 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], \
  1792. 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], \
  1793. 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], \
  1794. 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], \
  1795. 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], \
  1796. 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], \
  1797. _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
  1798. #define cimg_for9x9(img,x,y,z,c,I,T) \
  1799. cimg_for9((img)._height,y) for (int x = 0, \
  1800. _p4##x = 0, _p3##x = 0, _p2##x = 0, _p1##x = 0, \
  1801. _n1##x = 1>=((img)._width)?(img).width() - 1:1, \
  1802. _n2##x = 2>=((img)._width)?(img).width() - 1:2, \
  1803. _n3##x = 3>=((img)._width)?(img).width() - 1:3, \
  1804. _n4##x = (int)( \
  1805. (I[0] = I[1] = I[2] = I[3] = I[4] = (T)(img)(_p4##x,_p4##y,z,c)), \
  1806. (I[9] = I[10] = I[11] = I[12] = I[13] = (T)(img)(0,_p3##y,z,c)), \
  1807. (I[18] = I[19] = I[20] = I[21] = I[22] = (T)(img)(0,_p2##y,z,c)), \
  1808. (I[27] = I[28] = I[29] = I[30] = I[31] = (T)(img)(0,_p1##y,z,c)), \
  1809. (I[36] = I[37] = I[38] = I[39] = I[40] = (T)(img)(0,y,z,c)), \
  1810. (I[45] = I[46] = I[47] = I[48] = I[49] = (T)(img)(0,_n1##y,z,c)), \
  1811. (I[54] = I[55] = I[56] = I[57] = I[58] = (T)(img)(0,_n2##y,z,c)), \
  1812. (I[63] = I[64] = I[65] = I[66] = I[67] = (T)(img)(0,_n3##y,z,c)), \
  1813. (I[72] = I[73] = I[74] = I[75] = I[76] = (T)(img)(0,_n4##y,z,c)), \
  1814. (I[5] = (T)(img)(_n1##x,_p4##y,z,c)), \
  1815. (I[14] = (T)(img)(_n1##x,_p3##y,z,c)), \
  1816. (I[23] = (T)(img)(_n1##x,_p2##y,z,c)), \
  1817. (I[32] = (T)(img)(_n1##x,_p1##y,z,c)), \
  1818. (I[41] = (T)(img)(_n1##x,y,z,c)), \
  1819. (I[50] = (T)(img)(_n1##x,_n1##y,z,c)), \
  1820. (I[59] = (T)(img)(_n1##x,_n2##y,z,c)), \
  1821. (I[68] = (T)(img)(_n1##x,_n3##y,z,c)), \
  1822. (I[77] = (T)(img)(_n1##x,_n4##y,z,c)), \
  1823. (I[6] = (T)(img)(_n2##x,_p4##y,z,c)), \
  1824. (I[15] = (T)(img)(_n2##x,_p3##y,z,c)), \
  1825. (I[24] = (T)(img)(_n2##x,_p2##y,z,c)), \
  1826. (I[33] = (T)(img)(_n2##x,_p1##y,z,c)), \
  1827. (I[42] = (T)(img)(_n2##x,y,z,c)), \
  1828. (I[51] = (T)(img)(_n2##x,_n1##y,z,c)), \
  1829. (I[60] = (T)(img)(_n2##x,_n2##y,z,c)), \
  1830. (I[69] = (T)(img)(_n2##x,_n3##y,z,c)), \
  1831. (I[78] = (T)(img)(_n2##x,_n4##y,z,c)), \
  1832. (I[7] = (T)(img)(_n3##x,_p4##y,z,c)), \
  1833. (I[16] = (T)(img)(_n3##x,_p3##y,z,c)), \
  1834. (I[25] = (T)(img)(_n3##x,_p2##y,z,c)), \
  1835. (I[34] = (T)(img)(_n3##x,_p1##y,z,c)), \
  1836. (I[43] = (T)(img)(_n3##x,y,z,c)), \
  1837. (I[52] = (T)(img)(_n3##x,_n1##y,z,c)), \
  1838. (I[61] = (T)(img)(_n3##x,_n2##y,z,c)), \
  1839. (I[70] = (T)(img)(_n3##x,_n3##y,z,c)), \
  1840. (I[79] = (T)(img)(_n3##x,_n4##y,z,c)), \
  1841. 4>=((img)._width)?(img).width() - 1:4); \
  1842. (_n4##x<(img).width() && ( \
  1843. (I[8] = (T)(img)(_n4##x,_p4##y,z,c)), \
  1844. (I[17] = (T)(img)(_n4##x,_p3##y,z,c)), \
  1845. (I[26] = (T)(img)(_n4##x,_p2##y,z,c)), \
  1846. (I[35] = (T)(img)(_n4##x,_p1##y,z,c)), \
  1847. (I[44] = (T)(img)(_n4##x,y,z,c)), \
  1848. (I[53] = (T)(img)(_n4##x,_n1##y,z,c)), \
  1849. (I[62] = (T)(img)(_n4##x,_n2##y,z,c)), \
  1850. (I[71] = (T)(img)(_n4##x,_n3##y,z,c)), \
  1851. (I[80] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
  1852. _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x); \
  1853. 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], \
  1854. 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], \
  1855. 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], \
  1856. 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], \
  1857. 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], \
  1858. 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], \
  1859. 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], \
  1860. 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], \
  1861. 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], \
  1862. 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], \
  1863. I[79] = I[80], \
  1864. _p4##x = _p3##x, _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
  1865. #define cimg_for_in9x9(img,x0,y0,x1,y1,x,y,z,c,I,T) \
  1866. cimg_for_in9((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
  1867. _p4##x = x - 4<0?0:x - 4, \
  1868. _p3##x = x - 3<0?0:x - 3, \
  1869. _p2##x = x - 2<0?0:x - 2, \
  1870. _p1##x = x - 1<0?0:x - 1, \
  1871. _n1##x = x + 1>=(img).width()?(img).width() - 1:x + 1, \
  1872. _n2##x = x + 2>=(img).width()?(img).width() - 1:x + 2, \
  1873. _n3##x = x + 3>=(img).width()?(img).width() - 1:x + 3, \
  1874. _n4##x = (int)( \
  1875. (I[0] = (T)(img)(_p4##x,_p4##y,z,c)), \
  1876. (I[9] = (T)(img)(_p4##x,_p3##y,z,c)), \
  1877. (I[18] = (T)(img)(_p4##x,_p2##y,z,c)), \
  1878. (I[27] = (T)(img)(_p4##x,_p1##y,z,c)), \
  1879. (I[36] = (T)(img)(_p4##x,y,z,c)), \
  1880. (I[45] = (T)(img)(_p4##x,_n1##y,z,c)), \
  1881. (I[54] = (T)(img)(_p4##x,_n2##y,z,c)), \
  1882. (I[63] = (T)(img)(_p4##x,_n3##y,z,c)), \
  1883. (I[72] = (T)(img)(_p4##x,_n4##y,z,c)), \
  1884. (I[1] = (T)(img)(_p3##x,_p4##y,z,c)), \
  1885. (I[10] = (T)(img)(_p3##x,_p3##y,z,c)), \
  1886. (I[19] = (T)(img)(_p3##x,_p2##y,z,c)), \
  1887. (I[28] = (T)(img)(_p3##x,_p1##y,z,c)), \
  1888. (I[37] = (T)(img)(_p3##x,y,z,c)), \
  1889. (I[46] = (T)(img)(_p3##x,_n1##y,z,c)), \
  1890. (I[55] = (T)(img)(_p3##x,_n2##y,z,c)), \
  1891. (I[64] = (T)(img)(_p3##x,_n3##y,z,c)), \
  1892. (I[73] = (T)(img)(_p3##x,_n4##y,z,c)), \
  1893. (I[2] = (T)(img)(_p2##x,_p4##y,z,c)), \
  1894. (I[11] = (T)(img)(_p2##x,_p3##y,z,c)), \
  1895. (I[20] = (T)(img)(_p2##x,_p2##y,z,c)), \
  1896. (I[29] = (T)(img)(_p2##x,_p1##y,z,c)), \
  1897. (I[38] = (T)(img)(_p2##x,y,z,c)), \
  1898. (I[47] = (T)(img)(_p2##x,_n1##y,z,c)), \
  1899. (I[56] = (T)(img)(_p2##x,_n2##y,z,c)), \
  1900. (I[65] = (T)(img)(_p2##x,_n3##y,z,c)), \
  1901. (I[74] = (T)(img)(_p2##x,_n4##y,z,c)), \
  1902. (I[3] = (T)(img)(_p1##x,_p4##y,z,c)), \
  1903. (I[12] = (T)(img)(_p1##x,_p3##y,z,c)), \
  1904. (I[21] = (T)(img)(_p1##x,_p2##y,z,c)), \
  1905. (I[30] = (T)(img)(_p1##x,_p1##y,z,c)), \
  1906. (I[39] = (T)(img)(_p1##x,y,z,c)), \
  1907. (I[48] = (T)(img)(_p1##x,_n1##y,z,c)), \
  1908. (I[57] = (T)(img)(_p1##x,_n2##y,z,c)), \
  1909. (I[66] = (T)(img)(_p1##x,_n3##y,z,c)), \
  1910. (I[75] = (T)(img)(_p1##x,_n4##y,z,c)), \
  1911. (I[4] = (T)(img)(x,_p4##y,z,c)), \
  1912. (I[13] = (T)(img)(x,_p3##y,z,c)), \
  1913. (I[22] = (T)(img)(x,_p2##y,z,c)), \
  1914. (I[31] = (T)(img)(x,_p1##y,z,c)), \
  1915. (I[40] = (T)(img)(x,y,z,c)), \
  1916. (I[49] = (T)(img)(x,_n1##y,z,c)), \
  1917. (I[58] = (T)(img)(x,_n2##y,z,c)), \
  1918. (I[67] = (T)(img)(x,_n3##y,z,c)), \
  1919. (I[76] = (T)(img)(x,_n4##y,z,c)), \
  1920. (I[5] = (T)(img)(_n1##x,_p4##y,z,c)), \
  1921. (I[14] = (T)(img)(_n1##x,_p3##y,z,c)), \
  1922. (I[23] = (T)(img)(_n1##x,_p2##y,z,c)), \
  1923. (I[32] = (T)(img)(_n1##x,_p1##y,z,c)), \
  1924. (I[41] = (T)(img)(_n1##x,y,z,c)), \
  1925. (I[50] = (T)(img)(_n1##x,_n1##y,z,c)), \
  1926. (I[59] = (T)(img)(_n1##x,_n2##y,z,c)), \
  1927. (I[68] = (T)(img)(_n1##x,_n3##y,z,c)), \
  1928. (I[77] = (T)(img)(_n1##x,_n4##y,z,c)), \
  1929. (I[6] = (T)(img)(_n2##x,_p4##y,z,c)), \
  1930. (I[15] = (T)(img)(_n2##x,_p3##y,z,c)), \
  1931. (I[24] = (T)(img)(_n2##x,_p2##y,z,c)), \
  1932. (I[33] = (T)(img)(_n2##x,_p1##y,z,c)), \
  1933. (I[42] = (T)(img)(_n2##x,y,z,c)), \
  1934. (I[51] = (T)(img)(_n2##x,_n1##y,z,c)), \
  1935. (I[60] = (T)(img)(_n2##x,_n2##y,z,c)), \
  1936. (I[69] = (T)(img)(_n2##x,_n3##y,z,c)), \
  1937. (I[78] = (T)(img)(_n2##x,_n4##y,z,c)), \
  1938. (I[7] = (T)(img)(_n3##x,_p4##y,z,c)), \
  1939. (I[16] = (T)(img)(_n3##x,_p3##y,z,c)), \
  1940. (I[25] = (T)(img)(_n3##x,_p2##y,z,c)), \
  1941. (I[34] = (T)(img)(_n3##x,_p1##y,z,c)), \
  1942. (I[43] = (T)(img)(_n3##x,y,z,c)), \
  1943. (I[52] = (T)(img)(_n3##x,_n1##y,z,c)), \
  1944. (I[61] = (T)(img)(_n3##x,_n2##y,z,c)), \
  1945. (I[70] = (T)(img)(_n3##x,_n3##y,z,c)), \
  1946. (I[79] = (T)(img)(_n3##x,_n4##y,z,c)), \
  1947. x + 4>=(img).width()?(img).width() - 1:x + 4); \
  1948. x<=(int)(x1) && ((_n4##x<(img).width() && ( \
  1949. (I[8] = (T)(img)(_n4##x,_p4##y,z,c)), \
  1950. (I[17] = (T)(img)(_n4##x,_p3##y,z,c)), \
  1951. (I[26] = (T)(img)(_n4##x,_p2##y,z,c)), \
  1952. (I[35] = (T)(img)(_n4##x,_p1##y,z,c)), \
  1953. (I[44] = (T)(img)(_n4##x,y,z,c)), \
  1954. (I[53] = (T)(img)(_n4##x,_n1##y,z,c)), \
  1955. (I[62] = (T)(img)(_n4##x,_n2##y,z,c)), \
  1956. (I[71] = (T)(img)(_n4##x,_n3##y,z,c)), \
  1957. (I[80] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
  1958. _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x)); \
  1959. 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], \
  1960. 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], \
  1961. 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], \
  1962. 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], \
  1963. 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], \
  1964. 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], \
  1965. 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], \
  1966. 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], \
  1967. 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], \
  1968. 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], \
  1969. I[79] = I[80], \
  1970. _p4##x = _p3##x, _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
  1971. #define cimg_for2x2x2(img,x,y,z,c,I,T) \
  1972. cimg_for2((img)._depth,z) cimg_for2((img)._height,y) for (int x = 0, \
  1973. _n1##x = (int)( \
  1974. (I[0] = (T)(img)(0,y,z,c)), \
  1975. (I[2] = (T)(img)(0,_n1##y,z,c)), \
  1976. (I[4] = (T)(img)(0,y,_n1##z,c)), \
  1977. (I[6] = (T)(img)(0,_n1##y,_n1##z,c)), \
  1978. 1>=(img)._width?(img).width() - 1:1); \
  1979. (_n1##x<(img).width() && ( \
  1980. (I[1] = (T)(img)(_n1##x,y,z,c)), \
  1981. (I[3] = (T)(img)(_n1##x,_n1##y,z,c)), \
  1982. (I[5] = (T)(img)(_n1##x,y,_n1##z,c)), \
  1983. (I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
  1984. x==--_n1##x; \
  1985. I[0] = I[1], I[2] = I[3], I[4] = I[5], I[6] = I[7], \
  1986. ++x, ++_n1##x)
  1987. #define cimg_for_in2x2x2(img,x0,y0,z0,x1,y1,z1,x,y,z,c,I,T) \
  1988. 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), \
  1989. _n1##x = (int)( \
  1990. (I[0] = (T)(img)(x,y,z,c)), \
  1991. (I[2] = (T)(img)(x,_n1##y,z,c)), \
  1992. (I[4] = (T)(img)(x,y,_n1##z,c)), \
  1993. (I[6] = (T)(img)(x,_n1##y,_n1##z,c)), \
  1994. x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \
  1995. x<=(int)(x1) && ((_n1##x<(img).width() && ( \
  1996. (I[1] = (T)(img)(_n1##x,y,z,c)), \
  1997. (I[3] = (T)(img)(_n1##x,_n1##y,z,c)), \
  1998. (I[5] = (T)(img)(_n1##x,y,_n1##z,c)), \
  1999. (I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
  2000. x==--_n1##x); \
  2001. I[0] = I[1], I[2] = I[3], I[4] = I[5], I[6] = I[7], \
  2002. ++x, ++_n1##x)
  2003. #define cimg_for3x3x3(img,x,y,z,c,I,T) \
  2004. cimg_for3((img)._depth,z) cimg_for3((img)._height,y) for (int x = 0, \
  2005. _p1##x = 0, \
  2006. _n1##x = (int)( \
  2007. (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,_p1##z,c)), \
  2008. (I[3] = I[4] = (T)(img)(0,y,_p1##z,c)), \
  2009. (I[6] = I[7] = (T)(img)(0,_n1##y,_p1##z,c)), \
  2010. (I[9] = I[10] = (T)(img)(0,_p1##y,z,c)), \
  2011. (I[12] = I[13] = (T)(img)(0,y,z,c)), \
  2012. (I[15] = I[16] = (T)(img)(0,_n1##y,z,c)), \
  2013. (I[18] = I[19] = (T)(img)(0,_p1##y,_n1##z,c)), \
  2014. (I[21] = I[22] = (T)(img)(0,y,_n1##z,c)), \
  2015. (I[24] = I[25] = (T)(img)(0,_n1##y,_n1##z,c)), \
  2016. 1>=(img)._width?(img).width() - 1:1); \
  2017. (_n1##x<(img).width() && ( \
  2018. (I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c)), \
  2019. (I[5] = (T)(img)(_n1##x,y,_p1##z,c)), \
  2020. (I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c)), \
  2021. (I[11] = (T)(img)(_n1##x,_p1##y,z,c)), \
  2022. (I[14] = (T)(img)(_n1##x,y,z,c)), \
  2023. (I[17] = (T)(img)(_n1##x,_n1##y,z,c)), \
  2024. (I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c)), \
  2025. (I[23] = (T)(img)(_n1##x,y,_n1##z,c)), \
  2026. (I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
  2027. x==--_n1##x; \
  2028. I[0] = I[1], I[1] = I[2], I[3] = I[4], I[4] = I[5], I[6] = I[7], I[7] = I[8], \
  2029. I[9] = I[10], I[10] = I[11], I[12] = I[13], I[13] = I[14], I[15] = I[16], I[16] = I[17], \
  2030. I[18] = I[19], I[19] = I[20], I[21] = I[22], I[22] = I[23], I[24] = I[25], I[25] = I[26], \
  2031. _p1##x = x++, ++_n1##x)
  2032. #define cimg_for_in3x3x3(img,x0,y0,z0,x1,y1,z1,x,y,z,c,I,T) \
  2033. 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), \
  2034. _p1##x = x - 1<0?0:x - 1, \
  2035. _n1##x = (int)( \
  2036. (I[0] = (T)(img)(_p1##x,_p1##y,_p1##z,c)), \
  2037. (I[3] = (T)(img)(_p1##x,y,_p1##z,c)), \
  2038. (I[6] = (T)(img)(_p1##x,_n1##y,_p1##z,c)), \
  2039. (I[9] = (T)(img)(_p1##x,_p1##y,z,c)), \
  2040. (I[12] = (T)(img)(_p1##x,y,z,c)), \
  2041. (I[15] = (T)(img)(_p1##x,_n1##y,z,c)), \
  2042. (I[18] = (T)(img)(_p1##x,_p1##y,_n1##z,c)), \
  2043. (I[21] = (T)(img)(_p1##x,y,_n1##z,c)), \
  2044. (I[24] = (T)(img)(_p1##x,_n1##y,_n1##z,c)), \
  2045. (I[1] = (T)(img)(x,_p1##y,_p1##z,c)), \
  2046. (I[4] = (T)(img)(x,y,_p1##z,c)), \
  2047. (I[7] = (T)(img)(x,_n1##y,_p1##z,c)), \
  2048. (I[10] = (T)(img)(x,_p1##y,z,c)), \
  2049. (I[13] = (T)(img)(x,y,z,c)), \
  2050. (I[16] = (T)(img)(x,_n1##y,z,c)), \
  2051. (I[19] = (T)(img)(x,_p1##y,_n1##z,c)), \
  2052. (I[22] = (T)(img)(x,y,_n1##z,c)), \
  2053. (I[25] = (T)(img)(x,_n1##y,_n1##z,c)), \
  2054. x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \
  2055. x<=(int)(x1) && ((_n1##x<(img).width() && ( \
  2056. (I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c)), \
  2057. (I[5] = (T)(img)(_n1##x,y,_p1##z,c)), \
  2058. (I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c)), \
  2059. (I[11] = (T)(img)(_n1##x,_p1##y,z,c)), \
  2060. (I[14] = (T)(img)(_n1##x,y,z,c)), \
  2061. (I[17] = (T)(img)(_n1##x,_n1##y,z,c)), \
  2062. (I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c)), \
  2063. (I[23] = (T)(img)(_n1##x,y,_n1##z,c)), \
  2064. (I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
  2065. x==--_n1##x); \
  2066. I[0] = I[1], I[1] = I[2], I[3] = I[4], I[4] = I[5], I[6] = I[7], I[7] = I[8], \
  2067. I[9] = I[10], I[10] = I[11], I[12] = I[13], I[13] = I[14], I[15] = I[16], I[16] = I[17], \
  2068. I[18] = I[19], I[19] = I[20], I[21] = I[22], I[22] = I[23], I[24] = I[25], I[25] = I[26], \
  2069. _p1##x = x++, ++_n1##x)
  2070. #define cimglist_for(list,l) for (int l = 0; l<(int)(list)._width; ++l)
  2071. #define cimglist_for_in(list,l0,l1,l) \
  2072. for (int l = (int)(l0)<0?0:(int)(l0), _max##l = (unsigned int)l1<(list)._width?(int)(l1):(int)(list)._width - 1; \
  2073. l<=_max##l; ++l)
  2074. #define cimglist_apply(list,fn) cimglist_for(list,__##fn) (list)[__##fn].fn
  2075. // Macros used to display error messages when exceptions are thrown.
  2076. // You should not use these macros is your own code.
  2077. #define _cimgdisplay_instance "[instance(%u,%u,%u,%c%s%c)] CImgDisplay::"
  2078. #define cimgdisplay_instance _width,_height,_normalization,_title?'\"':'[',_title?_title:"untitled",_title?'\"':']'
  2079. #define _cimg_instance "[instance(%u,%u,%u,%u,%p,%sshared)] CImg<%s>::"
  2080. #define cimg_instance _width,_height,_depth,_spectrum,_data,_is_shared?"":"non-",pixel_type()
  2081. #define _cimglist_instance "[instance(%u,%u,%p)] CImgList<%s>::"
  2082. #define cimglist_instance _width,_allocated_width,_data,pixel_type()
  2083. /*------------------------------------------------
  2084. #
  2085. #
  2086. # Define cimg_library:: namespace
  2087. #
  2088. #
  2089. -------------------------------------------------*/
  2090. //! Contains <i>all classes and functions</i> of the \CImg library.
  2091. /**
  2092. This namespace is defined to avoid functions and class names collisions
  2093. that could happen with the inclusion of other C++ header files.
  2094. Anyway, it should not happen often and you should reasonnably start most of your
  2095. \CImg-based programs with
  2096. \code
  2097. #include "CImg.h"
  2098. using namespace cimg_library;
  2099. \endcode
  2100. to simplify the declaration of \CImg Library objects afterwards.
  2101. **/
  2102. namespace cimg_library_suffixed {
  2103. // Declare the four classes of the CImg Library.
  2104. template<typename T=float> struct CImg;
  2105. template<typename T=float> struct CImgList;
  2106. struct CImgDisplay;
  2107. struct CImgException;
  2108. // Declare cimg:: namespace.
  2109. // This is an uncomplete namespace definition here. It only contains some
  2110. // necessary stuff to ensure a correct declaration order of the classes and functions
  2111. // defined afterwards.
  2112. namespace cimg {
  2113. // Define ascii sequences for colored terminal output.
  2114. #ifdef cimg_use_vt100
  2115. static const char t_normal[] = { 0x1b, '[', '0', ';', '0', ';', '0', 'm', 0 };
  2116. static const char t_black[] = { 0x1b, '[', '0', ';', '3', '0', ';', '5', '9', 'm', 0 };
  2117. static const char t_red[] = { 0x1b, '[', '0', ';', '3', '1', ';', '5', '9', 'm', 0 };
  2118. static const char t_green[] = { 0x1b, '[', '0', ';', '3', '2', ';', '5', '9', 'm', 0 };
  2119. static const char t_yellow[] = { 0x1b, '[', '0', ';', '3', '3', ';', '5', '9', 'm', 0 };
  2120. static const char t_blue[] = { 0x1b, '[', '0', ';', '3', '4', ';', '5', '9', 'm', 0 };
  2121. static const char t_magenta[] = { 0x1b, '[', '0', ';', '3', '5', ';', '5', '9', 'm', 0 };
  2122. static const char t_cyan[] = { 0x1b, '[', '0', ';', '3', '6', ';', '5', '9', 'm', 0 };
  2123. static const char t_white[] = { 0x1b, '[', '0', ';', '3', '7', ';', '5', '9', 'm', 0 };
  2124. static const char t_bold[] = { 0x1b, '[', '1', 'm', 0 };
  2125. static const char t_underscore[] = { 0x1b, '[', '4', 'm', 0 };
  2126. #else
  2127. static const char t_normal[] = { 0 };
  2128. static const char *const t_black = cimg::t_normal,
  2129. *const t_red = cimg::t_normal,
  2130. *const t_green = cimg::t_normal,
  2131. *const t_yellow = cimg::t_normal,
  2132. *const t_blue = cimg::t_normal,
  2133. *const t_magenta = cimg::t_normal,
  2134. *const t_cyan = cimg::t_normal,
  2135. *const t_white = cimg::t_normal,
  2136. *const t_bold = cimg::t_normal,
  2137. *const t_underscore = cimg::t_normal;
  2138. #endif
  2139. inline std::FILE* output(std::FILE *file=0);
  2140. inline void info();
  2141. //! Avoid warning messages due to unused parameters. Do nothing actually.
  2142. template<typename T>
  2143. inline void unused(const T&, ...) {}
  2144. // [internal] Lock/unlock a mutex for managing concurrent threads.
  2145. // 'lock_mode' can be { 0=unlock | 1=lock | 2=trylock }.
  2146. // 'n' can be in [0,31] but mutex range [0,15] is reserved by CImg.
  2147. inline int mutex(const unsigned int n, const int lock_mode=1);
  2148. inline unsigned int& _exception_mode(const unsigned int value, const bool is_set) {
  2149. static unsigned int mode = cimg_verbosity;
  2150. if (is_set) { cimg::mutex(0); mode = value<4?value:4; cimg::mutex(0,0); }
  2151. return mode;
  2152. }
  2153. // Functions to return standard streams 'stdin', 'stdout' and 'stderr'.
  2154. inline FILE* _stdin(const bool throw_exception=true);
  2155. inline FILE* _stdout(const bool throw_exception=true);
  2156. inline FILE* _stderr(const bool throw_exception=true);
  2157. // Mandatory because Microsoft's _snprintf() and _vsnprintf() do not add the '\0' character
  2158. // at the end of the string.
  2159. #if cimg_OS==2 && defined(_MSC_VER)
  2160. inline int _snprintf(char *const s, const size_t size, const char *const format, ...) {
  2161. va_list ap;
  2162. va_start(ap,format);
  2163. const int result = _vsnprintf(s,size,format,ap);
  2164. va_end(ap);
  2165. return result;
  2166. }
  2167. inline int _vsnprintf(char *const s, const size_t size, const char *const format, va_list ap) {
  2168. int result = -1;
  2169. cimg::mutex(6);
  2170. if (size) result = _vsnprintf_s(s,size,_TRUNCATE,format,ap);
  2171. if (result==-1) result = _vscprintf(format,ap);
  2172. cimg::mutex(6,0);
  2173. return result;
  2174. }
  2175. // Mutex-protected version of sscanf, sprintf and snprintf.
  2176. // Used only MacOSX, as it seems those functions are not re-entrant on MacOSX.
  2177. #elif defined(__MACOSX__) || defined(__APPLE__)
  2178. inline int _sscanf(const char *const s, const char *const format, ...) {
  2179. cimg::mutex(6);
  2180. va_list args;
  2181. va_start(args,format);
  2182. const int result = std::vsscanf(s,format,args);
  2183. va_end(args);
  2184. cimg::mutex(6,0);
  2185. return result;
  2186. }
  2187. inline int _sprintf(char *const s, const char *const format, ...) {
  2188. cimg::mutex(6);
  2189. va_list args;
  2190. va_start(args,format);
  2191. const int result = std::vsprintf(s,format,args);
  2192. va_end(args);
  2193. cimg::mutex(6,0);
  2194. return result;
  2195. }
  2196. inline int _snprintf(char *const s, const size_t n, const char *const format, ...) {
  2197. cimg::mutex(6);
  2198. va_list args;
  2199. va_start(args,format);
  2200. const int result = std::vsnprintf(s,n,format,args);
  2201. va_end(args);
  2202. cimg::mutex(6,0);
  2203. return result;
  2204. }
  2205. inline int _vsnprintf(char *const s, const size_t size, const char* format, va_list ap) {
  2206. cimg::mutex(6);
  2207. const int result = std::vsnprintf(s,size,format,ap);
  2208. cimg::mutex(6,0);
  2209. return result;
  2210. }
  2211. #endif
  2212. //! Set current \CImg exception mode.
  2213. /**
  2214. The way error messages are handled by \CImg can be changed dynamically, using this function.
  2215. \param mode Desired exception mode. Possible values are:
  2216. - \c 0: Hide library messages (quiet mode).
  2217. - \c 1: Print library messages on the console.
  2218. - \c 2: Display library messages on a dialog window.
  2219. - \c 3: Do as \c 1 + add extra debug warnings (slow down the code!).
  2220. - \c 4: Do as \c 2 + add extra debug warnings (slow down the code!).
  2221. **/
  2222. inline unsigned int& exception_mode(const unsigned int mode) {
  2223. return _exception_mode(mode,true);
  2224. }
  2225. //! Return current \CImg exception mode.
  2226. /**
  2227. \note By default, return the value of configuration macro \c cimg_verbosity
  2228. **/
  2229. inline unsigned int& exception_mode() {
  2230. return _exception_mode(0,false);
  2231. }
  2232. //! Set current \CImg openmp mode.
  2233. /**
  2234. The way openmp-based methods are handled by \CImg can be changed dynamically, using this function.
  2235. \param mode Desired openmp mode. Possible values are:
  2236. - \c 0: Never parallelize.
  2237. - \c 1: Always parallelize.
  2238. - \c 2: Adaptive parallelization mode (default behavior).
  2239. **/
  2240. inline unsigned int& _openmp_mode(const unsigned int value, const bool is_set) {
  2241. static unsigned int mode = 2;
  2242. if (is_set) { cimg::mutex(0); mode = value<2?value:2; cimg::mutex(0,0); }
  2243. return mode;
  2244. }
  2245. inline unsigned int& openmp_mode(const unsigned int mode) {
  2246. return _openmp_mode(mode,true);
  2247. }
  2248. //! Return current \CImg openmp mode.
  2249. inline unsigned int& openmp_mode() {
  2250. return _openmp_mode(0,false);
  2251. }
  2252. #define cimg_openmp_if(cond) if (cimg::openmp_mode()==1 || (cimg::openmp_mode()>1 && (cond)))
  2253. // Display a simple dialog box, and wait for the user's response.
  2254. inline int dialog(const char *const title, const char *const msg, const char *const button1_label="OK",
  2255. const char *const button2_label=0, const char *const button3_label=0,
  2256. const char *const button4_label=0, const char *const button5_label=0,
  2257. const char *const button6_label=0, const bool centering=false);
  2258. // Evaluate math expression.
  2259. inline double eval(const char *const expression,
  2260. const double x=0, const double y=0, const double z=0, const double c=0);
  2261. }
  2262. /*---------------------------------------
  2263. #
  2264. # Define the CImgException structures
  2265. #
  2266. --------------------------------------*/
  2267. //! Instances of \c CImgException are thrown when errors are encountered in a \CImg function call.
  2268. /**
  2269. \par Overview
  2270. CImgException is the base class of all exceptions thrown by \CImg (except \b CImgAbortException).
  2271. CImgException is never thrown itself. Derived classes that specify the type of errord are thrown instead.
  2272. These classes can be:
  2273. - \b CImgAbortException: Thrown when a computationally-intensive function is aborted by an external signal.
  2274. This is the only \c non-derived exception class.
  2275. - \b CImgArgumentException: Thrown when one argument of a called \CImg function is invalid.
  2276. This is probably one of the most thrown exception by \CImg.
  2277. For instance, the following example throws a \c CImgArgumentException:
  2278. \code
  2279. CImg<float> img(100,100,1,3); // Define a 100x100 color image with float-valued pixels.
  2280. img.mirror('e'); // Try to mirror image along the (non-existing) 'e'-axis.
  2281. \endcode
  2282. - \b CImgDisplayException: Thrown when something went wrong during the display of images in CImgDisplay instances.
  2283. - \b CImgInstanceException: Thrown when an instance associated to a called \CImg method does not fit
  2284. the function requirements. For instance, the following example throws a \c CImgInstanceException:
  2285. \code
  2286. const CImg<float> img; // Define an empty image.
  2287. const float value = img.at(0); // Try to read first pixel value (does not exist).
  2288. \endcode
  2289. - \b CImgIOException: Thrown when an error occured when trying to load or save image files.
  2290. This happens when trying to read files that do not exist or with invalid formats.
  2291. For instance, the following example throws a \c CImgIOException:
  2292. \code
  2293. const CImg<float> img("missing_file.jpg"); // Try to load a file that does not exist.
  2294. \endcode
  2295. - \b CImgWarningException: Thrown only if configuration macro \c cimg_strict_warnings is set, and
  2296. when a \CImg function has to display a warning message (see cimg::warn()).
  2297. It is not recommended to throw CImgException instances by yourself,
  2298. since they are expected to be thrown only by \CImg.
  2299. When an error occurs in a library function call, \CImg may display error messages on the screen or on the
  2300. standard output, depending on the current \CImg exception mode.
  2301. The \CImg exception mode can be get and set by functions cimg::exception_mode() and
  2302. cimg::exception_mode(unsigned int).
  2303. \par Exceptions handling
  2304. In all cases, when an error occurs in \CImg, an instance of the corresponding exception class is thrown.
  2305. This may lead the program to break (this is the default behavior), but you can bypass this behavior by
  2306. handling the exceptions by yourself,
  2307. using a usual <tt>try { ... } catch () { ... }</tt> bloc, as in the following example:
  2308. \code
  2309. #define "CImg.h"
  2310. using namespace cimg_library;
  2311. int main() {
  2312. cimg::exception_mode(0); // Enable quiet exception mode.
  2313. try {
  2314. ... // Here, do what you want to stress CImg.
  2315. } catch (CImgException& e) { // You succeeded: something went wrong!
  2316. std::fprintf(stderr,"CImg Library Error: %s",e.what()); // Display your custom error message.
  2317. ... // Do what you want now to save the ship!
  2318. }
  2319. }
  2320. \endcode
  2321. **/
  2322. struct CImgException : public std::exception {
  2323. #define _cimg_exception_err(etype,disp_flag) \
  2324. std::va_list ap, ap2; \
  2325. va_start(ap,format); va_start(ap2,format); \
  2326. int size = cimg_vsnprintf(0,0,format,ap2); \
  2327. if (size++>=0) { \
  2328. delete[] _message; \
  2329. _message = new char[size]; \
  2330. cimg_vsnprintf(_message,size,format,ap); \
  2331. if (cimg::exception_mode()) { \
  2332. std::fprintf(cimg::output(),"\n%s[CImg] *** %s ***%s %s\n",cimg::t_red,etype,cimg::t_normal,_message); \
  2333. if (cimg_display && disp_flag && !(cimg::exception_mode()%2)) try { cimg::dialog(etype,_message,"Abort"); } \
  2334. catch (CImgException&) {} \
  2335. if (cimg::exception_mode()>=3) cimg_library_suffixed::cimg::info(); \
  2336. } \
  2337. } \
  2338. va_end(ap); va_end(ap2); \
  2339. char *_message;
  2340. CImgException() { _message = new char[1]; *_message = 0; }
  2341. CImgException(const char *const format, ...):_message(0) { _cimg_exception_err("CImgException",true); }
  2342. CImgException(const CImgException& e):std::exception(e) {
  2343. const size_t size = std::strlen(e._message);
  2344. _message = new char[size + 1];
  2345. std::strncpy(_message,e._message,size);
  2346. _message[size] = 0;
  2347. }
  2348. ~CImgException() throw() { delete[] _message; }
  2349. CImgException& operator=(const CImgException& e) {
  2350. const size_t size = std::strlen(e._message);
  2351. _message = new char[size + 1];
  2352. std::strncpy(_message,e._message,size);
  2353. _message[size] = 0;
  2354. return *this;
  2355. }
  2356. //! Return a C-string containing the error message associated to the thrown exception.
  2357. const char *what() const throw() { return _message; }
  2358. };
  2359. // The CImgAbortException class is used to throw an exception when
  2360. // a computationally-intensive function has been aborted by an external signal.
  2361. struct CImgAbortException : public std::exception {
  2362. char *_message;
  2363. CImgAbortException() { _message = new char[1]; *_message = 0; }
  2364. CImgAbortException(const char *const format, ...):_message(0) { _cimg_exception_err("CImgAbortException",true); }
  2365. CImgAbortException(const CImgAbortException& e):std::exception(e) {
  2366. const size_t size = std::strlen(e._message);
  2367. _message = new char[size + 1];
  2368. std::strncpy(_message,e._message,size);
  2369. _message[size] = 0;
  2370. }
  2371. ~CImgAbortException() throw() { delete[] _message; }
  2372. CImgAbortException& operator=(const CImgAbortException& e) {
  2373. const size_t size = std::strlen(e._message);
  2374. _message = new char[size + 1];
  2375. std::strncpy(_message,e._message,size);
  2376. _message[size] = 0;
  2377. return *this;
  2378. }
  2379. //! Return a C-string containing the error message associated to the thrown exception.
  2380. const char *what() const throw() { return _message; }
  2381. };
  2382. // The CImgArgumentException class is used to throw an exception related
  2383. // to invalid arguments encountered in a library function call.
  2384. struct CImgArgumentException : public CImgException {
  2385. CImgArgumentException(const char *const format, ...) { _cimg_exception_err("CImgArgumentException",true); }
  2386. };
  2387. // The CImgDisplayException class is used to throw an exception related
  2388. // to display problems encountered in a library function call.
  2389. struct CImgDisplayException : public CImgException {
  2390. CImgDisplayException(const char *const format, ...) { _cimg_exception_err("CImgDisplayException",false); }
  2391. };
  2392. // The CImgInstanceException class is used to throw an exception related
  2393. // to an invalid instance encountered in a library function call.
  2394. struct CImgInstanceException : public CImgException {
  2395. CImgInstanceException(const char *const format, ...) { _cimg_exception_err("CImgInstanceException",true); }
  2396. };
  2397. // The CImgIOException class is used to throw an exception related
  2398. // to input/output file problems encountered in a library function call.
  2399. struct CImgIOException : public CImgException {
  2400. CImgIOException(const char *const format, ...) { _cimg_exception_err("CImgIOException",true); }
  2401. };
  2402. // The CImgWarningException class is used to throw an exception for warnings
  2403. // encountered in a library function call.
  2404. struct CImgWarningException : public CImgException {
  2405. CImgWarningException(const char *const format, ...) { _cimg_exception_err("CImgWarningException",false); }
  2406. };
  2407. /*-------------------------------------
  2408. #
  2409. # Define cimg:: namespace
  2410. #
  2411. -----------------------------------*/
  2412. //! Contains \a low-level functions and variables of the \CImg Library.
  2413. /**
  2414. Most of the functions and variables within this namespace are used by the \CImg library for low-level operations.
  2415. You may use them to access specific const values or environment variables internally used by \CImg.
  2416. \warning Never write <tt>using namespace cimg_library::cimg;</tt> in your source code. Lot of functions in the
  2417. <tt>cimg:: namespace</tt> have the same names as standard C functions that may be defined in the global
  2418. namespace <tt>::</tt>.
  2419. **/
  2420. namespace cimg {
  2421. // Define traits that will be used to determine the best data type to work in CImg functions.
  2422. //
  2423. template<typename T> struct type {
  2424. static const char* string() {
  2425. static const char* s[] = { "unknown", "unknown8", "unknown16", "unknown24",
  2426. "unknown32", "unknown40", "unknown48", "unknown56",
  2427. "unknown64", "unknown72", "unknown80", "unknown88",
  2428. "unknown96", "unknown104", "unknown112", "unknown120",
  2429. "unknown128" };
  2430. return s[(sizeof(T)<17)?sizeof(T):0];
  2431. }
  2432. static bool is_float() { return false; }
  2433. static bool is_inf(const T) { return false; }
  2434. static bool is_nan(const T) { return false; }
  2435. static T min() { return ~max(); }
  2436. static T max() { return (T)1<<(8*sizeof(T) - 1); }
  2437. static T inf() { return max(); }
  2438. static T cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(T)val; }
  2439. static const char* format() { return "%s"; }
  2440. static const char* format_s() { return "%s"; }
  2441. static const char* format(const T& val) { static const char *const s = "unknown"; cimg::unused(val); return s; }
  2442. };
  2443. template<> struct type<bool> {
  2444. static const char* string() { static const char *const s = "bool"; return s; }
  2445. static bool is_float() { return false; }
  2446. static bool is_inf(const bool) { return false; }
  2447. static bool is_nan(const bool) { return false; }
  2448. static bool min() { return false; }
  2449. static bool max() { return true; }
  2450. static bool inf() { return max(); }
  2451. static bool is_inf() { return false; }
  2452. static bool cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(bool)val; }
  2453. static const char* format() { return "%s"; }
  2454. static const char* format_s() { return "%s"; }
  2455. static const char* format(const bool val) { static const char* s[] = { "false", "true" }; return s[val?1:0]; }
  2456. };
  2457. template<> struct type<unsigned char> {
  2458. static const char* string() { static const char *const s = "unsigned char"; return s; }
  2459. static bool is_float() { return false; }
  2460. static bool is_inf(const unsigned char) { return false; }
  2461. static bool is_nan(const unsigned char) { return false; }
  2462. static unsigned char min() { return 0; }
  2463. static unsigned char max() { return (unsigned char)-1; }
  2464. static unsigned char inf() { return max(); }
  2465. static unsigned char cut(const double val) {
  2466. return val<(double)min()?min():val>(double)max()?max():(unsigned char)val; }
  2467. static const char* format() { return "%u"; }
  2468. static const char* format_s() { return "%u"; }
  2469. static unsigned int format(const unsigned char val) { return (unsigned int)val; }
  2470. };
  2471. #if defined(CHAR_MAX) && CHAR_MAX==255
  2472. template<> struct type<char> {
  2473. static const char* string() { static const char *const s = "char"; return s; }
  2474. static bool is_float() { return false; }
  2475. static bool is_inf(const char) { return false; }
  2476. static bool is_nan(const char) { return false; }
  2477. static char min() { return 0; }
  2478. static char max() { return (char)-1; }
  2479. static char inf() { return max(); }
  2480. static char cut(const double val) {
  2481. return val<(double)min()?min():val>(double)max()?max():(unsigned char)val; }
  2482. static const char* format() { return "%u"; }
  2483. static const char* format_s() { return "%u"; }
  2484. static unsigned int format(const char val) { return (unsigned int)val; }
  2485. };
  2486. #else
  2487. template<> struct type<char> {
  2488. static const char* string() { static const char *const s = "char"; return s; }
  2489. static bool is_float() { return false; }
  2490. static bool is_inf(const char) { return false; }
  2491. static bool is_nan(const char) { return false; }
  2492. static char min() { return ~max(); }
  2493. static char max() { return (char)((unsigned char)-1>>1); }
  2494. static char inf() { return max(); }
  2495. static char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(char)val; }
  2496. static const char* format() { return "%d"; }
  2497. static const char* format_s() { return "%d"; }
  2498. static int format(const char val) { return (int)val; }
  2499. };
  2500. #endif
  2501. template<> struct type<signed char> {
  2502. static const char* string() { static const char *const s = "signed char"; return s; }
  2503. static bool is_float() { return false; }
  2504. static bool is_inf(const signed char) { return false; }
  2505. static bool is_nan(const signed char) { return false; }
  2506. static signed char min() { return ~max(); }
  2507. static signed char max() { return (signed char)((unsigned char)-1>>1); }
  2508. static signed char inf() { return max(); }
  2509. static signed char cut(const double val) {
  2510. return val<(double)min()?min():val>(double)max()?max():(signed char)val; }
  2511. static const char* format() { return "%d"; }
  2512. static const char* format_s() { return "%d"; }
  2513. static int format(const signed char val) { return (int)val; }
  2514. };
  2515. template<> struct type<unsigned short> {
  2516. static const char* string() { static const char *const s = "unsigned short"; return s; }
  2517. static bool is_float() { return false; }
  2518. static bool is_inf(const unsigned short) { return false; }
  2519. static bool is_nan(const unsigned short) { return false; }
  2520. static unsigned short min() { return 0; }
  2521. static unsigned short max() { return (unsigned short)-1; }
  2522. static unsigned short inf() { return max(); }
  2523. static unsigned short cut(const double val) {
  2524. return val<(double)min()?min():val>(double)max()?max():(unsigned short)val; }
  2525. static const char* format() { return "%u"; }
  2526. static const char* format_s() { return "%u"; }
  2527. static unsigned int format(const unsigned short val) { return (unsigned int)val; }
  2528. };
  2529. template<> struct type<short> {
  2530. static const char* string() { static const char *const s = "short"; return s; }
  2531. static bool is_float() { return false; }
  2532. static bool is_inf(const short) { return false; }
  2533. static bool is_nan(const short) { return false; }
  2534. static short min() { return ~max(); }
  2535. static short max() { return (short)((unsigned short)-1>>1); }
  2536. static short inf() { return max(); }
  2537. static short cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(short)val; }
  2538. static const char* format() { return "%d"; }
  2539. static const char* format_s() { return "%d"; }
  2540. static int format(const short val) { return (int)val; }
  2541. };
  2542. template<> struct type<unsigned int> {
  2543. static const char* string() { static const char *const s = "unsigned int"; return s; }
  2544. static bool is_float() { return false; }
  2545. static bool is_inf(const unsigned int) { return false; }
  2546. static bool is_nan(const unsigned int) { return false; }
  2547. static unsigned int min() { return 0; }
  2548. static unsigned int max() { return (unsigned int)-1; }
  2549. static unsigned int inf() { return max(); }
  2550. static unsigned int cut(const double val) {
  2551. return val<(double)min()?min():val>(double)max()?max():(unsigned int)val; }
  2552. static const char* format() { return "%u"; }
  2553. static const char* format_s() { return "%u"; }
  2554. static unsigned int format(const unsigned int val) { return val; }
  2555. };
  2556. template<> struct type<int> {
  2557. static const char* string() { static const char *const s = "int"; return s; }
  2558. static bool is_float() { return false; }
  2559. static bool is_inf(const int) { return false; }
  2560. static bool is_nan(const int) { return false; }
  2561. static int min() { return ~max(); }
  2562. static int max() { return (int)((unsigned int)-1>>1); }
  2563. static int inf() { return max(); }
  2564. static int cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(int)val; }
  2565. static const char* format() { return "%d"; }
  2566. static const char* format_s() { return "%d"; }
  2567. static int format(const int val) { return val; }
  2568. };
  2569. template<> struct type<cimg_uint64> {
  2570. static const char* string() { static const char *const s = "unsigned int64"; return s; }
  2571. static bool is_float() { return false; }
  2572. static bool is_inf(const cimg_uint64) { return false; }
  2573. static bool is_nan(const cimg_uint64) { return false; }
  2574. static cimg_uint64 min() { return 0; }
  2575. static cimg_uint64 max() { return (cimg_uint64)-1; }
  2576. static cimg_uint64 inf() { return max(); }
  2577. static cimg_uint64 cut(const double val) {
  2578. return val<(double)min()?min():val>(double)max()?max():(cimg_uint64)val; }
  2579. static const char* format() { return cimg_fuint64; }
  2580. static const char* format_s() { return cimg_fuint64; }
  2581. static unsigned long format(const cimg_uint64 val) { return (unsigned long)val; }
  2582. };
  2583. template<> struct type<cimg_int64> {
  2584. static const char* string() { static const char *const s = "int64"; return s; }
  2585. static bool is_float() { return false; }
  2586. static bool is_inf(const cimg_int64) { return false; }
  2587. static bool is_nan(const cimg_int64) { return false; }
  2588. static cimg_int64 min() { return ~max(); }
  2589. static cimg_int64 max() { return (cimg_int64)((cimg_uint64)-1>>1); }
  2590. static cimg_int64 inf() { return max(); }
  2591. static cimg_int64 cut(const double val) {
  2592. return val<(double)min()?min():val>(double)max()?max():(cimg_int64)val;
  2593. }
  2594. static const char* format() { return cimg_fint64; }
  2595. static const char* format_s() { return cimg_fint64; }
  2596. static long format(const long val) { return (long)val; }
  2597. };
  2598. template<> struct type<double> {
  2599. static const char* string() { static const char *const s = "double"; return s; }
  2600. static bool is_float() { return true; }
  2601. static bool is_inf(const double val) {
  2602. #ifdef isinf
  2603. return (bool)isinf(val);
  2604. #else
  2605. return !is_nan(val) && (val<cimg::type<double>::min() || val>cimg::type<double>::max());
  2606. #endif
  2607. }
  2608. static bool is_nan(const double val) { // Custom version that works with '-ffast-math'
  2609. if (sizeof(double)==8) {
  2610. cimg_uint64 u;
  2611. std::memcpy(&u,&val,sizeof(double));
  2612. return ((unsigned int)(u>>32)&0x7fffffff) + ((unsigned int)u!=0)>0x7ff00000;
  2613. }
  2614. #ifdef isnan
  2615. return (bool)isnan(val);
  2616. #else
  2617. return !(val==val);
  2618. #endif
  2619. }
  2620. static double min() { return -DBL_MAX; }
  2621. static double max() { return DBL_MAX; }
  2622. static double inf() {
  2623. #ifdef INFINITY
  2624. return (double)INFINITY;
  2625. #else
  2626. return max()*max();
  2627. #endif
  2628. }
  2629. static double nan() {
  2630. #ifdef NAN
  2631. return (double)NAN;
  2632. #else
  2633. const double val_nan = -std::sqrt(-1.0); return val_nan;
  2634. #endif
  2635. }
  2636. static double cut(const double val) { return val; }
  2637. static const char* format() { return "%.17g"; }
  2638. static const char* format_s() { return "%g"; }
  2639. static double format(const double val) { return val; }
  2640. };
  2641. template<> struct type<float> {
  2642. static const char* string() { static const char *const s = "float"; return s; }
  2643. static bool is_float() { return true; }
  2644. static bool is_inf(const float val) {
  2645. #ifdef isinf
  2646. return (bool)isinf(val);
  2647. #else
  2648. return !is_nan(val) && (val<cimg::type<float>::min() || val>cimg::type<float>::max());
  2649. #endif
  2650. }
  2651. static bool is_nan(const float val) { // Custom version that works with '-ffast-math'
  2652. if (sizeof(float)==4) {
  2653. unsigned int u;
  2654. std::memcpy(&u,&val,sizeof(float));
  2655. return (u&0x7fffffff)>0x7f800000;
  2656. }
  2657. #ifdef isnan
  2658. return (bool)isnan(val);
  2659. #else
  2660. return !(val==val);
  2661. #endif
  2662. }
  2663. static float min() { return -FLT_MAX; }
  2664. static float max() { return FLT_MAX; }
  2665. static float inf() { return (float)cimg::type<double>::inf(); }
  2666. static float nan() { return (float)cimg::type<double>::nan(); }
  2667. static float cut(const double val) { return (float)val; }
  2668. static float cut(const float val) { return (float)val; }
  2669. static const char* format() { return "%.9g"; }
  2670. static const char* format_s() { return "%g"; }
  2671. static double format(const float val) { return (double)val; }
  2672. };
  2673. template<> struct type<long double> {
  2674. static const char* string() { static const char *const s = "long double"; return s; }
  2675. static bool is_float() { return true; }
  2676. static bool is_inf(const long double val) {
  2677. #ifdef isinf
  2678. return (bool)isinf(val);
  2679. #else
  2680. return !is_nan(val) && (val<cimg::type<long double>::min() || val>cimg::type<long double>::max());
  2681. #endif
  2682. }
  2683. static bool is_nan(const long double val) {
  2684. #ifdef isnan
  2685. return (bool)isnan(val);
  2686. #else
  2687. return !(val==val);
  2688. #endif
  2689. }
  2690. static long double min() { return -LDBL_MAX; }
  2691. static long double max() { return LDBL_MAX; }
  2692. static long double inf() { return max()*max(); }
  2693. static long double nan() { const long double val_nan = -std::sqrt(-1.0L); return val_nan; }
  2694. static long double cut(const long double val) { return val; }
  2695. static const char* format() { return "%.17g"; }
  2696. static const char* format_s() { return "%g"; }
  2697. static double format(const long double val) { return (double)val; }
  2698. };
  2699. #ifdef cimg_use_half
  2700. template<> struct type<half> {
  2701. static const char* string() { static const char *const s = "half"; return s; }
  2702. static bool is_float() { return true; }
  2703. static bool is_inf(const long double val) {
  2704. #ifdef isinf
  2705. return (bool)isinf(val);
  2706. #else
  2707. return !is_nan(val) && (val<cimg::type<half>::min() || val>cimg::type<half>::max());
  2708. #endif
  2709. }
  2710. static bool is_nan(const half val) { // Custom version that works with '-ffast-math'
  2711. if (sizeof(half)==2) {
  2712. short u;
  2713. std::memcpy(&u,&val,sizeof(short));
  2714. return (bool)((u&0x7fff)>0x7c00);
  2715. }
  2716. return cimg::type<float>::is_nan((float)val);
  2717. }
  2718. static half min() { return (half)-65504; }
  2719. static half max() { return (half)65504; }
  2720. static half inf() { return max()*max(); }
  2721. static half nan() { const half val_nan = (half)-std::sqrt(-1.0); return val_nan; }
  2722. static half cut(const double val) { return (half)val; }
  2723. static const char* format() { return "%.9g"; }
  2724. static const char* format_s() { return "%g"; }
  2725. static double format(const half val) { return (double)val; }
  2726. };
  2727. #endif
  2728. template<typename T, typename t> struct superset { typedef T type; };
  2729. template<> struct superset<bool,unsigned char> { typedef unsigned char type; };
  2730. template<> struct superset<bool,char> { typedef char type; };
  2731. template<> struct superset<bool,signed char> { typedef signed char type; };
  2732. template<> struct superset<bool,unsigned short> { typedef unsigned short type; };
  2733. template<> struct superset<bool,short> { typedef short type; };
  2734. template<> struct superset<bool,unsigned int> { typedef unsigned int type; };
  2735. template<> struct superset<bool,int> { typedef int type; };
  2736. template<> struct superset<bool,cimg_uint64> { typedef cimg_uint64 type; };
  2737. template<> struct superset<bool,cimg_int64> { typedef cimg_int64 type; };
  2738. template<> struct superset<bool,float> { typedef float type; };
  2739. template<> struct superset<bool,double> { typedef double type; };
  2740. template<> struct superset<unsigned char,char> { typedef short type; };
  2741. template<> struct superset<unsigned char,signed char> { typedef short type; };
  2742. template<> struct superset<unsigned char,unsigned short> { typedef unsigned short type; };
  2743. template<> struct superset<unsigned char,short> { typedef short type; };
  2744. template<> struct superset<unsigned char,unsigned int> { typedef unsigned int type; };
  2745. template<> struct superset<unsigned char,int> { typedef int type; };
  2746. template<> struct superset<unsigned char,cimg_uint64> { typedef cimg_uint64 type; };
  2747. template<> struct superset<unsigned char,cimg_int64> { typedef cimg_int64 type; };
  2748. template<> struct superset<unsigned char,float> { typedef float type; };
  2749. template<> struct superset<unsigned char,double> { typedef double type; };
  2750. template<> struct superset<signed char,unsigned char> { typedef short type; };
  2751. template<> struct superset<signed char,char> { typedef short type; };
  2752. template<> struct superset<signed char,unsigned short> { typedef int type; };
  2753. template<> struct superset<signed char,short> { typedef short type; };
  2754. template<> struct superset<signed char,unsigned int> { typedef cimg_int64 type; };
  2755. template<> struct superset<signed char,int> { typedef int type; };
  2756. template<> struct superset<signed char,cimg_uint64> { typedef cimg_int64 type; };
  2757. template<> struct superset<signed char,cimg_int64> { typedef cimg_int64 type; };
  2758. template<> struct superset<signed char,float> { typedef float type; };
  2759. template<> struct superset<signed char,double> { typedef double type; };
  2760. template<> struct superset<char,unsigned char> { typedef short type; };
  2761. template<> struct superset<char,signed char> { typedef short type; };
  2762. template<> struct superset<char,unsigned short> { typedef int type; };
  2763. template<> struct superset<char,short> { typedef short type; };
  2764. template<> struct superset<char,unsigned int> { typedef cimg_int64 type; };
  2765. template<> struct superset<char,int> { typedef int type; };
  2766. template<> struct superset<char,cimg_uint64> { typedef cimg_int64 type; };
  2767. template<> struct superset<char,cimg_int64> { typedef cimg_int64 type; };
  2768. template<> struct superset<char,float> { typedef float type; };
  2769. template<> struct superset<char,double> { typedef double type; };
  2770. template<> struct superset<unsigned short,char> { typedef int type; };
  2771. template<> struct superset<unsigned short,signed char> { typedef int type; };
  2772. template<> struct superset<unsigned short,short> { typedef int type; };
  2773. template<> struct superset<unsigned short,unsigned int> { typedef unsigned int type; };
  2774. template<> struct superset<unsigned short,int> { typedef int type; };
  2775. template<> struct superset<unsigned short,cimg_uint64> { typedef cimg_uint64 type; };
  2776. template<> struct superset<unsigned short,cimg_int64> { typedef cimg_int64 type; };
  2777. template<> struct superset<unsigned short,float> { typedef float type; };
  2778. template<> struct superset<unsigned short,double> { typedef double type; };
  2779. template<> struct superset<short,unsigned short> { typedef int type; };
  2780. template<> struct superset<short,unsigned int> { typedef cimg_int64 type; };
  2781. template<> struct superset<short,int> { typedef int type; };
  2782. template<> struct superset<short,cimg_uint64> { typedef cimg_int64 type; };
  2783. template<> struct superset<short,cimg_int64> { typedef cimg_int64 type; };
  2784. template<> struct superset<short,float> { typedef float type; };
  2785. template<> struct superset<short,double> { typedef double type; };
  2786. template<> struct superset<unsigned int,char> { typedef cimg_int64 type; };
  2787. template<> struct superset<unsigned int,signed char> { typedef cimg_int64 type; };
  2788. template<> struct superset<unsigned int,short> { typedef cimg_int64 type; };
  2789. template<> struct superset<unsigned int,int> { typedef cimg_int64 type; };
  2790. template<> struct superset<unsigned int,cimg_uint64> { typedef cimg_uint64 type; };
  2791. template<> struct superset<unsigned int,cimg_int64> { typedef cimg_int64 type; };
  2792. template<> struct superset<unsigned int,float> { typedef float type; };
  2793. template<> struct superset<unsigned int,double> { typedef double type; };
  2794. template<> struct superset<int,unsigned int> { typedef cimg_int64 type; };
  2795. template<> struct superset<int,cimg_uint64> { typedef cimg_int64 type; };
  2796. template<> struct superset<int,cimg_int64> { typedef cimg_int64 type; };
  2797. template<> struct superset<int,float> { typedef float type; };
  2798. template<> struct superset<int,double> { typedef double type; };
  2799. template<> struct superset<cimg_uint64,char> { typedef cimg_int64 type; };
  2800. template<> struct superset<cimg_uint64,signed char> { typedef cimg_int64 type; };
  2801. template<> struct superset<cimg_uint64,short> { typedef cimg_int64 type; };
  2802. template<> struct superset<cimg_uint64,int> { typedef cimg_int64 type; };
  2803. template<> struct superset<cimg_uint64,cimg_int64> { typedef cimg_int64 type; };
  2804. template<> struct superset<cimg_uint64,float> { typedef double type; };
  2805. template<> struct superset<cimg_uint64,double> { typedef double type; };
  2806. template<> struct superset<cimg_int64,float> { typedef double type; };
  2807. template<> struct superset<cimg_int64,double> { typedef double type; };
  2808. template<> struct superset<float,double> { typedef double type; };
  2809. #ifdef cimg_use_half
  2810. template<> struct superset<half,unsigned short> { typedef float type; };
  2811. template<> struct superset<half,short> { typedef float type; };
  2812. template<> struct superset<half,unsigned int> { typedef float type; };
  2813. template<> struct superset<half,int> { typedef float type; };
  2814. template<> struct superset<half,cimg_uint64> { typedef float type; };
  2815. template<> struct superset<half,cimg_int64> { typedef float type; };
  2816. template<> struct superset<half,float> { typedef float type; };
  2817. template<> struct superset<half,double> { typedef double type; };
  2818. #endif
  2819. template<typename t1, typename t2, typename t3> struct superset2 {
  2820. typedef typename superset<t1, typename superset<t2,t3>::type>::type type;
  2821. };
  2822. template<typename t1, typename t2, typename t3, typename t4> struct superset3 {
  2823. typedef typename superset<t1, typename superset2<t2,t3,t4>::type>::type type;
  2824. };
  2825. template<typename t1, typename t2> struct last { typedef t2 type; };
  2826. #define _cimg_Tt typename cimg::superset<T,t>::type
  2827. #define _cimg_Tfloat typename cimg::superset<T,float>::type
  2828. #define _cimg_Ttfloat typename cimg::superset2<T,t,float>::type
  2829. #define _cimg_Ttdouble typename cimg::superset2<T,t,double>::type
  2830. // Define variables used internally by CImg.
  2831. #if cimg_display==1
  2832. struct X11_info {
  2833. unsigned int nb_wins;
  2834. pthread_t *events_thread;
  2835. pthread_cond_t wait_event;
  2836. pthread_mutex_t wait_event_mutex;
  2837. CImgDisplay **wins;
  2838. Display *display;
  2839. unsigned int nb_bits;
  2840. bool is_blue_first;
  2841. bool is_shm_enabled;
  2842. bool byte_order;
  2843. #ifdef cimg_use_xrandr
  2844. XRRScreenSize *resolutions;
  2845. Rotation curr_rotation;
  2846. unsigned int curr_resolution;
  2847. unsigned int nb_resolutions;
  2848. #endif
  2849. X11_info():nb_wins(0),events_thread(0),display(0),
  2850. nb_bits(0),is_blue_first(false),is_shm_enabled(false),byte_order(false) {
  2851. #ifdef __FreeBSD__
  2852. XInitThreads();
  2853. #endif
  2854. wins = new CImgDisplay*[1024];
  2855. pthread_mutex_init(&wait_event_mutex,0);
  2856. pthread_cond_init(&wait_event,0);
  2857. #ifdef cimg_use_xrandr
  2858. resolutions = 0;
  2859. curr_rotation = 0;
  2860. curr_resolution = nb_resolutions = 0;
  2861. #endif
  2862. }
  2863. ~X11_info() {
  2864. delete[] wins;
  2865. /*
  2866. if (events_thread) {
  2867. pthread_cancel(*events_thread);
  2868. delete events_thread;
  2869. }
  2870. if (display) { } // XCloseDisplay(display); }
  2871. pthread_cond_destroy(&wait_event);
  2872. pthread_mutex_unlock(&wait_event_mutex);
  2873. pthread_mutex_destroy(&wait_event_mutex);
  2874. */
  2875. }
  2876. };
  2877. #if defined(cimg_module)
  2878. X11_info& X11_attr();
  2879. #elif defined(cimg_main)
  2880. X11_info& X11_attr() { static X11_info val; return val; }
  2881. #else
  2882. inline X11_info& X11_attr() { static X11_info val; return val; }
  2883. #endif
  2884. #define cimg_lock_display() cimg::mutex(15)
  2885. #define cimg_unlock_display() cimg::mutex(15,0)
  2886. #elif cimg_display==2
  2887. struct Win32_info {
  2888. HANDLE wait_event;
  2889. Win32_info() { wait_event = CreateEvent(0,FALSE,FALSE,0); }
  2890. };
  2891. #if defined(cimg_module)
  2892. Win32_info& Win32_attr();
  2893. #elif defined(cimg_main)
  2894. Win32_info& Win32_attr() { static Win32_info val; return val; }
  2895. #else
  2896. inline Win32_info& Win32_attr() { static Win32_info val; return val; }
  2897. #endif
  2898. #endif
  2899. struct Mutex_info {
  2900. #if cimg_OS==2
  2901. HANDLE mutex[32];
  2902. Mutex_info() { for (unsigned int i = 0; i<32; ++i) mutex[i] = CreateMutex(0,FALSE,0); }
  2903. void lock(const unsigned int n) { WaitForSingleObject(mutex[n],INFINITE); }
  2904. void unlock(const unsigned int n) { ReleaseMutex(mutex[n]); }
  2905. int trylock(const unsigned int) { return 0; }
  2906. #elif defined(_PTHREAD_H)
  2907. pthread_mutex_t mutex[32];
  2908. Mutex_info() { for (unsigned int i = 0; i<32; ++i) pthread_mutex_init(&mutex[i],0); }
  2909. void lock(const unsigned int n) { pthread_mutex_lock(&mutex[n]); }
  2910. void unlock(const unsigned int n) { pthread_mutex_unlock(&mutex[n]); }
  2911. int trylock(const unsigned int n) { return pthread_mutex_trylock(&mutex[n]); }
  2912. #else
  2913. Mutex_info() {}
  2914. void lock(const unsigned int) {}
  2915. void unlock(const unsigned int) {}
  2916. int trylock(const unsigned int) { return 0; }
  2917. #endif
  2918. };
  2919. #if defined(cimg_module)
  2920. Mutex_info& Mutex_attr();
  2921. #elif defined(cimg_main)
  2922. Mutex_info& Mutex_attr() { static Mutex_info val; return val; }
  2923. #else
  2924. inline Mutex_info& Mutex_attr() { static Mutex_info val; return val; }
  2925. #endif
  2926. #if defined(cimg_use_magick)
  2927. static struct Magick_info {
  2928. Magick_info() {
  2929. Magick::InitializeMagick("");
  2930. }
  2931. } _Magick_info;
  2932. #endif
  2933. #if cimg_display==1
  2934. // Define keycodes for X11-based graphical systems.
  2935. const unsigned int keyESC = XK_Escape;
  2936. const unsigned int keyF1 = XK_F1;
  2937. const unsigned int keyF2 = XK_F2;
  2938. const unsigned int keyF3 = XK_F3;
  2939. const unsigned int keyF4 = XK_F4;
  2940. const unsigned int keyF5 = XK_F5;
  2941. const unsigned int keyF6 = XK_F6;
  2942. const unsigned int keyF7 = XK_F7;
  2943. const unsigned int keyF8 = XK_F8;
  2944. const unsigned int keyF9 = XK_F9;
  2945. const unsigned int keyF10 = XK_F10;
  2946. const unsigned int keyF11 = XK_F11;
  2947. const unsigned int keyF12 = XK_F12;
  2948. const unsigned int keyPAUSE = XK_Pause;
  2949. const unsigned int key1 = XK_1;
  2950. const unsigned int key2 = XK_2;
  2951. const unsigned int key3 = XK_3;
  2952. const unsigned int key4 = XK_4;
  2953. const unsigned int key5 = XK_5;
  2954. const unsigned int key6 = XK_6;
  2955. const unsigned int key7 = XK_7;
  2956. const unsigned int key8 = XK_8;
  2957. const unsigned int key9 = XK_9;
  2958. const unsigned int key0 = XK_0;
  2959. const unsigned int keyBACKSPACE = XK_BackSpace;
  2960. const unsigned int keyINSERT = XK_Insert;
  2961. const unsigned int keyHOME = XK_Home;
  2962. const unsigned int keyPAGEUP = XK_Page_Up;
  2963. const unsigned int keyTAB = XK_Tab;
  2964. const unsigned int keyQ = XK_q;
  2965. const unsigned int keyW = XK_w;
  2966. const unsigned int keyE = XK_e;
  2967. const unsigned int keyR = XK_r;
  2968. const unsigned int keyT = XK_t;
  2969. const unsigned int keyY = XK_y;
  2970. const unsigned int keyU = XK_u;
  2971. const unsigned int keyI = XK_i;
  2972. const unsigned int keyO = XK_o;
  2973. const unsigned int keyP = XK_p;
  2974. const unsigned int keyDELETE = XK_Delete;
  2975. const unsigned int keyEND = XK_End;
  2976. const unsigned int keyPAGEDOWN = XK_Page_Down;
  2977. const unsigned int keyCAPSLOCK = XK_Caps_Lock;
  2978. const unsigned int keyA = XK_a;
  2979. const unsigned int keyS = XK_s;
  2980. const unsigned int keyD = XK_d;
  2981. const unsigned int keyF = XK_f;
  2982. const unsigned int keyG = XK_g;
  2983. const unsigned int keyH = XK_h;
  2984. const unsigned int keyJ = XK_j;
  2985. const unsigned int keyK = XK_k;
  2986. const unsigned int keyL = XK_l;
  2987. const unsigned int keyENTER = XK_Return;
  2988. const unsigned int keySHIFTLEFT = XK_Shift_L;
  2989. const unsigned int keyZ = XK_z;
  2990. const unsigned int keyX = XK_x;
  2991. const unsigned int keyC = XK_c;
  2992. const unsigned int keyV = XK_v;
  2993. const unsigned int keyB = XK_b;
  2994. const unsigned int keyN = XK_n;
  2995. const unsigned int keyM = XK_m;
  2996. const unsigned int keySHIFTRIGHT = XK_Shift_R;
  2997. const unsigned int keyARROWUP = XK_Up;
  2998. const unsigned int keyCTRLLEFT = XK_Control_L;
  2999. const unsigned int keyAPPLEFT = XK_Super_L;
  3000. const unsigned int keyALT = XK_Alt_L;
  3001. const unsigned int keySPACE = XK_space;
  3002. const unsigned int keyALTGR = XK_Alt_R;
  3003. const unsigned int keyAPPRIGHT = XK_Super_R;
  3004. const unsigned int keyMENU = XK_Menu;
  3005. const unsigned int keyCTRLRIGHT = XK_Control_R;
  3006. const unsigned int keyARROWLEFT = XK_Left;
  3007. const unsigned int keyARROWDOWN = XK_Down;
  3008. const unsigned int keyARROWRIGHT = XK_Right;
  3009. const unsigned int keyPAD0 = XK_KP_0;
  3010. const unsigned int keyPAD1 = XK_KP_1;
  3011. const unsigned int keyPAD2 = XK_KP_2;
  3012. const unsigned int keyPAD3 = XK_KP_3;
  3013. const unsigned int keyPAD4 = XK_KP_4;
  3014. const unsigned int keyPAD5 = XK_KP_5;
  3015. const unsigned int keyPAD6 = XK_KP_6;
  3016. const unsigned int keyPAD7 = XK_KP_7;
  3017. const unsigned int keyPAD8 = XK_KP_8;
  3018. const unsigned int keyPAD9 = XK_KP_9;
  3019. const unsigned int keyPADADD = XK_KP_Add;
  3020. const unsigned int keyPADSUB = XK_KP_Subtract;
  3021. const unsigned int keyPADMUL = XK_KP_Multiply;
  3022. const unsigned int keyPADDIV = XK_KP_Divide;
  3023. #elif cimg_display==2
  3024. // Define keycodes for Windows.
  3025. const unsigned int keyESC = VK_ESCAPE;
  3026. const unsigned int keyF1 = VK_F1;
  3027. const unsigned int keyF2 = VK_F2;
  3028. const unsigned int keyF3 = VK_F3;
  3029. const unsigned int keyF4 = VK_F4;
  3030. const unsigned int keyF5 = VK_F5;
  3031. const unsigned int keyF6 = VK_F6;
  3032. const unsigned int keyF7 = VK_F7;
  3033. const unsigned int keyF8 = VK_F8;
  3034. const unsigned int keyF9 = VK_F9;
  3035. const unsigned int keyF10 = VK_F10;
  3036. const unsigned int keyF11 = VK_F11;
  3037. const unsigned int keyF12 = VK_F12;
  3038. const unsigned int keyPAUSE = VK_PAUSE;
  3039. const unsigned int key1 = '1';
  3040. const unsigned int key2 = '2';
  3041. const unsigned int key3 = '3';
  3042. const unsigned int key4 = '4';
  3043. const unsigned int key5 = '5';
  3044. const unsigned int key6 = '6';
  3045. const unsigned int key7 = '7';
  3046. const unsigned int key8 = '8';
  3047. const unsigned int key9 = '9';
  3048. const unsigned int key0 = '0';
  3049. const unsigned int keyBACKSPACE = VK_BACK;
  3050. const unsigned int keyINSERT = VK_INSERT;
  3051. const unsigned int keyHOME = VK_HOME;
  3052. const unsigned int keyPAGEUP = VK_PRIOR;
  3053. const unsigned int keyTAB = VK_TAB;
  3054. const unsigned int keyQ = 'Q';
  3055. const unsigned int keyW = 'W';
  3056. const unsigned int keyE = 'E';
  3057. const unsigned int keyR = 'R';
  3058. const unsigned int keyT = 'T';
  3059. const unsigned int keyY = 'Y';
  3060. const unsigned int keyU = 'U';
  3061. const unsigned int keyI = 'I';
  3062. const unsigned int keyO = 'O';
  3063. const unsigned int keyP = 'P';
  3064. const unsigned int keyDELETE = VK_DELETE;
  3065. const unsigned int keyEND = VK_END;
  3066. const unsigned int keyPAGEDOWN = VK_NEXT;
  3067. const unsigned int keyCAPSLOCK = VK_CAPITAL;
  3068. const unsigned int keyA = 'A';
  3069. const unsigned int keyS = 'S';
  3070. const unsigned int keyD = 'D';
  3071. const unsigned int keyF = 'F';
  3072. const unsigned int keyG = 'G';
  3073. const unsigned int keyH = 'H';
  3074. const unsigned int keyJ = 'J';
  3075. const unsigned int keyK = 'K';
  3076. const unsigned int keyL = 'L';
  3077. const unsigned int keyENTER = VK_RETURN;
  3078. const unsigned int keySHIFTLEFT = VK_SHIFT;
  3079. const unsigned int keyZ = 'Z';
  3080. const unsigned int keyX = 'X';
  3081. const unsigned int keyC = 'C';
  3082. const unsigned int keyV = 'V';
  3083. const unsigned int keyB = 'B';
  3084. const unsigned int keyN = 'N';
  3085. const unsigned int keyM = 'M';
  3086. const unsigned int keySHIFTRIGHT = VK_SHIFT;
  3087. const unsigned int keyARROWUP = VK_UP;
  3088. const unsigned int keyCTRLLEFT = VK_CONTROL;
  3089. const unsigned int keyAPPLEFT = VK_LWIN;
  3090. const unsigned int keyALT = VK_LMENU;
  3091. const unsigned int keySPACE = VK_SPACE;
  3092. const unsigned int keyALTGR = VK_CONTROL;
  3093. const unsigned int keyAPPRIGHT = VK_RWIN;
  3094. const unsigned int keyMENU = VK_APPS;
  3095. const unsigned int keyCTRLRIGHT = VK_CONTROL;
  3096. const unsigned int keyARROWLEFT = VK_LEFT;
  3097. const unsigned int keyARROWDOWN = VK_DOWN;
  3098. const unsigned int keyARROWRIGHT = VK_RIGHT;
  3099. const unsigned int keyPAD0 = 0x60;
  3100. const unsigned int keyPAD1 = 0x61;
  3101. const unsigned int keyPAD2 = 0x62;
  3102. const unsigned int keyPAD3 = 0x63;
  3103. const unsigned int keyPAD4 = 0x64;
  3104. const unsigned int keyPAD5 = 0x65;
  3105. const unsigned int keyPAD6 = 0x66;
  3106. const unsigned int keyPAD7 = 0x67;
  3107. const unsigned int keyPAD8 = 0x68;
  3108. const unsigned int keyPAD9 = 0x69;
  3109. const unsigned int keyPADADD = VK_ADD;
  3110. const unsigned int keyPADSUB = VK_SUBTRACT;
  3111. const unsigned int keyPADMUL = VK_MULTIPLY;
  3112. const unsigned int keyPADDIV = VK_DIVIDE;
  3113. #else
  3114. // Define random keycodes when no display is available.
  3115. // (should rarely be used then!).
  3116. const unsigned int keyESC = 1U; //!< Keycode for the \c ESC key (architecture-dependent).
  3117. const unsigned int keyF1 = 2U; //!< Keycode for the \c F1 key (architecture-dependent).
  3118. const unsigned int keyF2 = 3U; //!< Keycode for the \c F2 key (architecture-dependent).
  3119. const unsigned int keyF3 = 4U; //!< Keycode for the \c F3 key (architecture-dependent).
  3120. const unsigned int keyF4 = 5U; //!< Keycode for the \c F4 key (architecture-dependent).
  3121. const unsigned int keyF5 = 6U; //!< Keycode for the \c F5 key (architecture-dependent).
  3122. const unsigned int keyF6 = 7U; //!< Keycode for the \c F6 key (architecture-dependent).
  3123. const unsigned int keyF7 = 8U; //!< Keycode for the \c F7 key (architecture-dependent).
  3124. const unsigned int keyF8 = 9U; //!< Keycode for the \c F8 key (architecture-dependent).
  3125. const unsigned int keyF9 = 10U; //!< Keycode for the \c F9 key (architecture-dependent).
  3126. const unsigned int keyF10 = 11U; //!< Keycode for the \c F10 key (architecture-dependent).
  3127. const unsigned int keyF11 = 12U; //!< Keycode for the \c F11 key (architecture-dependent).
  3128. const unsigned int keyF12 = 13U; //!< Keycode for the \c F12 key (architecture-dependent).
  3129. const unsigned int keyPAUSE = 14U; //!< Keycode for the \c PAUSE key (architecture-dependent).
  3130. const unsigned int key1 = 15U; //!< Keycode for the \c 1 key (architecture-dependent).
  3131. const unsigned int key2 = 16U; //!< Keycode for the \c 2 key (architecture-dependent).
  3132. const unsigned int key3 = 17U; //!< Keycode for the \c 3 key (architecture-dependent).
  3133. const unsigned int key4 = 18U; //!< Keycode for the \c 4 key (architecture-dependent).
  3134. const unsigned int key5 = 19U; //!< Keycode for the \c 5 key (architecture-dependent).
  3135. const unsigned int key6 = 20U; //!< Keycode for the \c 6 key (architecture-dependent).
  3136. const unsigned int key7 = 21U; //!< Keycode for the \c 7 key (architecture-dependent).
  3137. const unsigned int key8 = 22U; //!< Keycode for the \c 8 key (architecture-dependent).
  3138. const unsigned int key9 = 23U; //!< Keycode for the \c 9 key (architecture-dependent).
  3139. const unsigned int key0 = 24U; //!< Keycode for the \c 0 key (architecture-dependent).
  3140. const unsigned int keyBACKSPACE = 25U; //!< Keycode for the \c BACKSPACE key (architecture-dependent).
  3141. const unsigned int keyINSERT = 26U; //!< Keycode for the \c INSERT key (architecture-dependent).
  3142. const unsigned int keyHOME = 27U; //!< Keycode for the \c HOME key (architecture-dependent).
  3143. const unsigned int keyPAGEUP = 28U; //!< Keycode for the \c PAGEUP key (architecture-dependent).
  3144. const unsigned int keyTAB = 29U; //!< Keycode for the \c TAB key (architecture-dependent).
  3145. const unsigned int keyQ = 30U; //!< Keycode for the \c Q key (architecture-dependent).
  3146. const unsigned int keyW = 31U; //!< Keycode for the \c W key (architecture-dependent).
  3147. const unsigned int keyE = 32U; //!< Keycode for the \c E key (architecture-dependent).
  3148. const unsigned int keyR = 33U; //!< Keycode for the \c R key (architecture-dependent).
  3149. const unsigned int keyT = 34U; //!< Keycode for the \c T key (architecture-dependent).
  3150. const unsigned int keyY = 35U; //!< Keycode for the \c Y key (architecture-dependent).
  3151. const unsigned int keyU = 36U; //!< Keycode for the \c U key (architecture-dependent).
  3152. const unsigned int keyI = 37U; //!< Keycode for the \c I key (architecture-dependent).
  3153. const unsigned int keyO = 38U; //!< Keycode for the \c O key (architecture-dependent).
  3154. const unsigned int keyP = 39U; //!< Keycode for the \c P key (architecture-dependent).
  3155. const unsigned int keyDELETE = 40U; //!< Keycode for the \c DELETE key (architecture-dependent).
  3156. const unsigned int keyEND = 41U; //!< Keycode for the \c END key (architecture-dependent).
  3157. const unsigned int keyPAGEDOWN = 42U; //!< Keycode for the \c PAGEDOWN key (architecture-dependent).
  3158. const unsigned int keyCAPSLOCK = 43U; //!< Keycode for the \c CAPSLOCK key (architecture-dependent).
  3159. const unsigned int keyA = 44U; //!< Keycode for the \c A key (architecture-dependent).
  3160. const unsigned int keyS = 45U; //!< Keycode for the \c S key (architecture-dependent).
  3161. const unsigned int keyD = 46U; //!< Keycode for the \c D key (architecture-dependent).
  3162. const unsigned int keyF = 47U; //!< Keycode for the \c F key (architecture-dependent).
  3163. const unsigned int keyG = 48U; //!< Keycode for the \c G key (architecture-dependent).
  3164. const unsigned int keyH = 49U; //!< Keycode for the \c H key (architecture-dependent).
  3165. const unsigned int keyJ = 50U; //!< Keycode for the \c J key (architecture-dependent).
  3166. const unsigned int keyK = 51U; //!< Keycode for the \c K key (architecture-dependent).
  3167. const unsigned int keyL = 52U; //!< Keycode for the \c L key (architecture-dependent).
  3168. const unsigned int keyENTER = 53U; //!< Keycode for the \c ENTER key (architecture-dependent).
  3169. const unsigned int keySHIFTLEFT = 54U; //!< Keycode for the \c SHIFTLEFT key (architecture-dependent).
  3170. const unsigned int keyZ = 55U; //!< Keycode for the \c Z key (architecture-dependent).
  3171. const unsigned int keyX = 56U; //!< Keycode for the \c X key (architecture-dependent).
  3172. const unsigned int keyC = 57U; //!< Keycode for the \c C key (architecture-dependent).
  3173. const unsigned int keyV = 58U; //!< Keycode for the \c V key (architecture-dependent).
  3174. const unsigned int keyB = 59U; //!< Keycode for the \c B key (architecture-dependent).
  3175. const unsigned int keyN = 60U; //!< Keycode for the \c N key (architecture-dependent).
  3176. const unsigned int keyM = 61U; //!< Keycode for the \c M key (architecture-dependent).
  3177. const unsigned int keySHIFTRIGHT = 62U; //!< Keycode for the \c SHIFTRIGHT key (architecture-dependent).
  3178. const unsigned int keyARROWUP = 63U; //!< Keycode for the \c ARROWUP key (architecture-dependent).
  3179. const unsigned int keyCTRLLEFT = 64U; //!< Keycode for the \c CTRLLEFT key (architecture-dependent).
  3180. const unsigned int keyAPPLEFT = 65U; //!< Keycode for the \c APPLEFT key (architecture-dependent).
  3181. const unsigned int keyALT = 66U; //!< Keycode for the \c ALT key (architecture-dependent).
  3182. const unsigned int keySPACE = 67U; //!< Keycode for the \c SPACE key (architecture-dependent).
  3183. const unsigned int keyALTGR = 68U; //!< Keycode for the \c ALTGR key (architecture-dependent).
  3184. const unsigned int keyAPPRIGHT = 69U; //!< Keycode for the \c APPRIGHT key (architecture-dependent).
  3185. const unsigned int keyMENU = 70U; //!< Keycode for the \c MENU key (architecture-dependent).
  3186. const unsigned int keyCTRLRIGHT = 71U; //!< Keycode for the \c CTRLRIGHT key (architecture-dependent).
  3187. const unsigned int keyARROWLEFT = 72U; //!< Keycode for the \c ARROWLEFT key (architecture-dependent).
  3188. const unsigned int keyARROWDOWN = 73U; //!< Keycode for the \c ARROWDOWN key (architecture-dependent).
  3189. const unsigned int keyARROWRIGHT = 74U; //!< Keycode for the \c ARROWRIGHT key (architecture-dependent).
  3190. const unsigned int keyPAD0 = 75U; //!< Keycode for the \c PAD0 key (architecture-dependent).
  3191. const unsigned int keyPAD1 = 76U; //!< Keycode for the \c PAD1 key (architecture-dependent).
  3192. const unsigned int keyPAD2 = 77U; //!< Keycode for the \c PAD2 key (architecture-dependent).
  3193. const unsigned int keyPAD3 = 78U; //!< Keycode for the \c PAD3 key (architecture-dependent).
  3194. const unsigned int keyPAD4 = 79U; //!< Keycode for the \c PAD4 key (architecture-dependent).
  3195. const unsigned int keyPAD5 = 80U; //!< Keycode for the \c PAD5 key (architecture-dependent).
  3196. const unsigned int keyPAD6 = 81U; //!< Keycode for the \c PAD6 key (architecture-dependent).
  3197. const unsigned int keyPAD7 = 82U; //!< Keycode for the \c PAD7 key (architecture-dependent).
  3198. const unsigned int keyPAD8 = 83U; //!< Keycode for the \c PAD8 key (architecture-dependent).
  3199. const unsigned int keyPAD9 = 84U; //!< Keycode for the \c PAD9 key (architecture-dependent).
  3200. const unsigned int keyPADADD = 85U; //!< Keycode for the \c PADADD key (architecture-dependent).
  3201. const unsigned int keyPADSUB = 86U; //!< Keycode for the \c PADSUB key (architecture-dependent).
  3202. const unsigned int keyPADMUL = 87U; //!< Keycode for the \c PADMUL key (architecture-dependent).
  3203. const unsigned int keyPADDIV = 88U; //!< Keycode for the \c PADDDIV key (architecture-dependent).
  3204. #endif
  3205. const double PI = 3.14159265358979323846; //!< Value of the mathematical constant PI
  3206. // Define a 12x13 font (small size).
  3207. static const char *const data_font12x13 =
  3208. " .wjwlwmyuw>wjwkwbwjwkwRxuwmwjwkwmyuwJwjwlx`w Fw "
  3209. " mwlwlwuwnwuynwuwmyTwlwkwuwmwuwnwlwkwuwmwuw_wuxlwlwkwuwnwuynwuwTwlwlwtwnwtwnw my Qw +wlw b"
  3210. "{ \\w Wx`xTw_w[wbxawSwkw nynwky<x1w `y ,w Xwuw CxlwiwlwmyuwbwuwUwiwlwbwiwrwqw^wuwmxuwnwiwlwmy"
  3211. "uwJwiwlw^wnwEymymymymy1w^wkxnxtxnw<| gybwkwuwjwtwowmxswnxnwkxlxkw:wlymxlymykwn{myo{nymy2ykwqwqwm{myo"
  3212. "zn{o{mzpwrwpwkwkwswowkwqwqxswnyozlyozmzp}pwrwqwqwqwswswsxsxqwqwp}qwlwiwjybw`w[wcw_wkwkwkwkw mw\"wlwiw"
  3213. "=wtw`xIw awuwlwm{o{mylwn|pwtwtwoy`w_w_wbwiwkxcwqwpwkznwuwjzpyGzqymyaxlylw_zWxkxaxrwqxrwqyswowkwkwkwk"
  3214. "wkwkwk}qyo{o{o{o{owkwkwkwkznxswnymymymymyayuwqwrwpwrwpwrwpwrwqwqwpwkwtwlwkwlwuwnwuynwuwmyTwkwlwuwmwu"
  3215. "wnwkwlwuwmwuwkxlwuxmwkwlwuwnwuynwuwTwkwlwuwmwuwlwmwkwtwUwuwuwowswowswowswowsw;wqwtw_ymzp~py>w bwswcw"
  3216. "kwuwjwuwozpwtwuwnwtwowkwjwmwuwuwkwIxmxuxowuwmwswowswmxnwjwhwowswowsw0wmwowswuwnwrwowswpwswowkwjwrwqw"
  3217. "rwpwkwkwtwnwkxsxqxswowswpwswnwswpwswowrwnwmwrwqwqwqwswswrwswowswjwpwlxjwkxuxLw[wcw_wSwkw mw\"wlwiw=wt"
  3218. "wmxlwFw cwswnwuwnwkwjwswo{pwrwpwtwtwpwswby`w`yUwlwtwpwqwpwswowlw\\wrwrxuwHwrwfwuwjwlwlwTyuwVwlwtwawsw"
  3219. "owswowswcwuwmwuwmwuwmwuwmwuwlwkwuwnwswpwkwkwkwkwkwkwkwkwswoxswowswowswowswowswowswowrwpwswpwrwpwrwpw"
  3220. "rwpwrwpwswoznwtw Ww (wGwtwtwqwqwqwuwuwuwqwswuwqwqw=wqxtw`{nzp~q{ozowrwnxmwtwow bzawkwuwl}rwuwnwtwuw"
  3221. "nwtwowkwjwlyjwIwlwswmwiwkwnwuwnwkwhwnwswowswowkwewewixnwsytwswuwnwrwpwkwrwpwkwkwkwrwpwkwkwuwmwkxsxqw"
  3222. "uwtwpwqwqwswowqwqwswowiwmwrwpwswowtwtwpwuwmwuwjwowkwjwlxsxXynzmymznyozlzoznwkwkwtwnwkzuyrzmynzmzowux"
  3223. "myozmwswpwrwowtwtwrwrwpwrwp{mwlwiwHyuwpwtwkwmxlynzoxswmwmwswnwswowtxq|owtwtwpym{p{owswnwuwmwlwkwqwqx"
  3224. "uwuxqwrwpwtwtwqwqwowlwuwuwkwmwlwtwowuwuwdwjznwl{nwuwnwkx_wtxtwswtwlwtwWwuytwgyjwmwjwawswoyuwVwlwtwnw"
  3225. "twmwtwnwtwmwuwmwlwuwmwuwmwuwmwuwmwuwmwuwmxuwowkwkwkwkwkwkwkwkwkwrwpwuwtwpwqwqwqwqwqwqwqwqwqwowtwpwsw"
  3226. "uwqwrwpwrwpwrwpwrwowuwnwswowuwlymymymymymymyuyqymymymymynwkwkwkwjynzmymymymymykwmzowswowswowswowswpw"
  3227. "rwozowrwW}q}qwtwtwqwtwtwqwtwtwA}rwuw_{p~r~r}pwtwowrwnxmwtwow aw_w]wtwpwuwmxuwmybwjwlyjwIwlwswmwiwnyn"
  3228. "wtwnznzkwmynwswTyp}pylwmwtwtwtwswuwn{owkwrwp{o{owk|pwkwkxlwkwuwuwuwqwuwtwpwqwqwswowqwqwswoykwmwrwpws"
  3229. "wowuwuwuwowkwjwnwkwjwDwowswowkwswowswowkwswowswowkwkwuwmwkwswswswswowswowswowswoxlwswowkwswpwrwowtwt"
  3230. "wqwtwowrwlwoxkwhxVxuxpwtypwuwjwnwtwnwkwswowtxnxmwswowqwqwtwuxqwtwnwtwtwqwswowswmwm{nwuwlxnwkwqwqwtwt"
  3231. "wqwrwpwtwtwqwuyuwpwiwhwnwmwrwnwbwkwuwlwlwswoxuxowlwtw`wuwrwszmwtwo}dwuwtwuw[}qymx`wswoyuwow_ylxlwtwo"
  3232. "yuwoyuwoyuwmwlwuwmwuwmwuwmwuwmwuwmwuwmwt{swk{o{o{o{owkwkwkwlztwpwuwtwpwqwqwqwqwqwqwqwqwqwnxowtwtwqwr"
  3233. "wpwrwpwrwpwrwnwmwswowuwiwkwkwkwkwkwkwswswkwswowswowswowswowkwkwkwkwswowswowswowswowswowswowswcwtxows"
  3234. "wowswowswowswpwrwowswpwrwWwtwtwqwqwqwuwuwuwqwuwswqwqw>wowuw`}q~q|q}qwrwpwrwowtwnwtwo~ izaw]wtwoykwux"
  3235. "qwtwswfwjwmwuwuwn}eyaxlwswmwjwjwpwswjwowswmwmwswnzWy]ypwlwtwtwuwswswowrwpwkwrwpwkwkwsyqwrwpwkwkwuwmw"
  3236. "kwuwuwuwqwtwuwpwqwqznwqwqzkynwmwrwowuwnwuwuwuwowkwjwnwkxkwGzowswowkwswo{owkwswowswowkwkxlwkwswswswsw"
  3237. "owswowswowswowjxmwkwswowtwnwuwuwuwpxmwtwlwlwlwiwlytwewtwtwqwswowtxoznwswnxmwswnwuwmwuwnwswowtwtwqwtw"
  3238. "twqwtwnwtwtwqwswowswmwmwswowswmwmwkwqwqwtwtwqwrwowuwuwpwuyuwq~own~own~owbwkwuwmznwswmwbwswawuwrwgwtw"
  3239. "hwdwuytwXwJwswnxuw=wtwmwswowtxowswqxmwswowswowswowswowswowswnwtwowkwkwkwkwkwkwkwkwkwrwpwtwuwpwqwqwqw"
  3240. "qwqwqwqwqwqwnxowtwtwqwrwpwrwpwrwpwrwnwmwswowtwmznznznznznzn~swk{o{o{o{owkwkwkwkwswowswowswowswowswow"
  3241. "swowswo}qwuwuwowswowswowswowswowtwnwswowtwUwuwuwowswowswowswowsw@}qx`}q~pzo{pwrwpwrwowtwnwtwow aw_w_"
  3242. "}owuwmwuwtwrwswuwewjwkwiwJwkwswmwkwiwp|kwowswmwmwswkwWym}mypwlwszr{owrwpwkwrwpwkwkwqwqwrwpwkwkwtwnwk"
  3243. "wtwtwqwtwuwpwqwqwkwqwqwtwiwnwmwrwowuwnwuwuwuwpwuwlwkwmwjwkwHwswowswowkwswowkwkwswowswowkwkwuwmwkwsws"
  3244. "wswswowswowswowswowhwnwkwswowtwnwuwuwuwpxmwtwmwkwlwiwmwtydwtwtwqwswowswowtwnwswowkwswnwuwnwtwnwswowt"
  3245. "wtwqwtwtwqwtwnwtwtwqwswowswmwmwswowswnwlwkwqwqxuwuxqwrwnyowqwpwiwhwpwuwuwowrwpwuwuwdwkwuwlwlwswo{owk"
  3246. "xuwawtxtwszmwtwiwdwuwtwuwXwJwswmwuwKzmwtwlwtxowrwpwtxrxl{o{o{o{o{o{o{owkwkwkwkwkwkwkwkwkwrwpwtwuwpwq"
  3247. "wqwqwqwqwqwqwqwqwowtwpwuwswqwrwpwrwpwrwpwrwnwmznwswowswowswowswowswowswowswowswowkwkwkwkwkwkwkwkwkws"
  3248. "wowswowswowswowswowswowswcwuwuwowswowswowswowswowtwnwswowtwTymymymymy=wmw^wuwuwmxlxmyowrwowtwnwtwmxm"
  3249. "w bwswIwuwmwuwmwuwtwrxswdwjw]wJwkxuxmwlwlwswlwjwowswmwmwswlwSycyawlwswowrwowswpwswowkwjwrwqwrwpwkwkw"
  3250. "swowkwqwqwsxowswpwjwswpwswowrwnwmxtxnwlwswpwswmwlwlwjwkwHwswowswowkwswowswowkwswowswowkwkwtwnwkwswsw"
  3251. "swswowswowswowswowkwswowkwswnxlwswpwtwmxmwjwlwiwTxuxpwtxowswowtwnwswowkwswnynwtwnwswowtwtwqxuwuxqwtw"
  3252. "nwtwtwqwswowswmwlwuwnwswowkwjwswo{pwrwmwmwswnwjwiwnymwtwnycwkwuwlwl{mwmwiw_wrwdwtwVwrw*wswmwuw?wtwlw"
  3253. "tzqwrwpwtzswkwswowswowswowswowswowswowswnwswpwkwkwkwkwkwkwkwkwswowsxowswowswowswowswowswowrwpwswpxtx"
  3254. "pxtxpxtxpxtxnwmwkwswowswowswowswowswowswowswowtxowkwswowswowswowswowkwkwkwkwswowswowswowswowswowswow"
  3255. "swlwnxtwowswowswowswowswnxmwswnx >wlw\\wkx`wnwrwoznwtwmxl| gybw^wtwozmwsxpzuxfxlx]wnw_wlxjyn{o{nykwnz"
  3256. "mymwkynymwkwewewjwjwrwswqwp{myozn{owizpwrwpwkwkwrwp{owqwqwsxnyowiyowrwozmwlzmwlwswqxsxnwm}qwjxlwGzoz"
  3257. "mymznynwjzowswowkwkwswowkwswswswswnynzmzowjymxlznxlwswqwrwnwm{mwlwiwHxuxpzmxlymynwswmwnwrwozmxuxo{pw"
  3258. "txn{pzmykwmyo}p{owkyuynwnwrwmwly`w_w_wbwjzo{pwqwnwmwhw_z>zY}M|nwuw2wqwqwryrwqwqyowqwqwqwqwqwqwqwqwqw"
  3259. "qwqwqwr{qyo{o{o{o{owkwkwkwkznwsxnymymymymycwuynznznznzmwmwkwuynznznznznznznyuzrymymymymynwkwkwkwjynw"
  3260. "swnymymymymybzmznznznznwlzmw hwHwlwSwTw <w8z ]x tx Zxjwmx RwWw/wgw pw_ynwky=wCwmwaw\\w_wnw 1wIwl"
  3261. "z 'wiwuwaw mw Pw swlwjw hw f| pyWx/wgw rxSw/wCwmwaw\\w_wnw 1w AwRx nw Pw txk"
  3262. "wlxm";
  3263. // Define a 20x23 font (normal size).
  3264. static const char *const data_font20x23 =
  3265. " 9q\\q^r_rnp`qnq`plp7q\\q^q_qmqbq\\q^q_qmqHqmp_q\\q^r_rnp"
  3266. "`qnq7q\\q^q_qmq_q \"r Mq^q^qnq`pnr`qnq`plp6q^q^p"
  3267. "mp`qmqaq^q^pmp`qmqIpmq]q^q^qnq`pnr`qnq6q^q^pmp`qmq`q \"plp 'q 5qmq Vq "
  3268. " Xq [plp 3qYq_p^rnpLplp8qYq_qNqYq_q4rmpaqYq_q_rmp%qYq^pGq Irc|!pKp]raqjq`p "
  3269. "HtNq_qmq\\plqbp_shpdscq[q^q[p [q]s_r`uau]rbv`tcxbuat LsZucrav_udwcxdw`udqiqeq]q]qjreq]sksgrjqbtcv_tcv"
  3270. "aud{eqiqgqfqgqjsjqlrjrhrirfzfs`q[sZqMqJqCqNsLq]q]q]q]q .scq]s \\sKt%r [s^raxdxat_qazgqlqlqctJqIqIq"
  3271. "LqHsOqiqOtaqmq\\uft nufu`sLs`t\\qKv<r\\rLrepirepitgpeq]r^r^r^r^r^r^{gudxdxdxdxdq]q]q]q]wcrjqbt`t`t`t`tL"
  3272. "tlpgqiqeqiqeqiqeqiqgrireq[s_q[q_pnp_pnr`qnq`plp7q[q_s`qmqcq[q_s`qmq]pkpbpmr`q[q_s`pmraqmq8q[q^pnp_qn"
  3273. "q^qaq\\qnq !pnqd{!pJp^tdunucr _y dvOq_qmq\\plpap_pmpipdudq[p\\p_plplp _q^ubtawcw^rbvavdxcwcw Ou]yerawb"
  3274. "xeyexdwbxeqiqeq]q]qkrdq]sksgrjqdxewbxewcwe{eqiqfqhqfqjsjqkqjqfqiqezfs`q[s[sMpJqCqOtLq]q]q]q]q q 1tc"
  3275. "q]t ^vaq_w&r \\u_raxdxcxcuczgqlqlqexMsJqJsMq[p^uPqiqdq]uaqmq]qkqcq!qkqguaqmqNpkp\\p]pKtmp:p]plpKpfpfp"
  3276. "fpcpipdq]r^r^r^r^r^r^{ixexdxdxdxdq]q]q]q]yerjqdxdxdxdxdxPwnpfqiqeqiqeqiqeqiqfqiqdq\\u_p[p^pnpKqnq_r5p"
  3277. "[p^pmp`qmqbp[p^pmp`qmq]tKp[p^pmpLqmq7p[p]pnp_qnq^p`q\\qnq5uauauauaucq`qhq4p]pKr_ueunucr `q \\rkpOq_qm"
  3278. "q\\plpctbqmqkqerlpdq\\q\\q_qnpnq\\q%q^qkqcqnqapjrdpjr`sbq]rkp^qcrkrerkq Oplr`sirgtbqkrdripeqjsfq]q]ripeq"
  3279. "iqeq]q]qlrcq]sksgskqerjrfqkrdrjrfqkrerjp`q`qiqfqhqeqkskqiqlqdqkq\\qeq]qZq\\qmqNqKqCqOqIq5q]q q 1q`qZq"
  3280. " _rlqbtaqjp$q ^qkqatbr^q]rjrewdqhqgqlqlqfrjrOuKqKu8p_rlpOqkqcq]qFpgpcp\"pgpTpkp\\q^p\\p^qLump:p^pjpLpg"
  3281. "pepgpbpjpPt`t`t`t`t`qnq_qnqcripeq]q]q]q]q]q]q]q]qjsfskqerjrfrjrfrjrfrjrfrjrRrjrfqiqeqiqeqiqeqiqeqkqc"
  3282. "vbrlq`q]q_plp Iq]q_qmqNq]q_qmqKtIq]q_qmq ^q]q^plpKq`q mqkqcqkqcqkqcqkqcqkqdq`qhq5q^qLt`ueunudtasbqip"
  3283. "`q`pipcq [qIq_qmq`{gvcqmqkpdq_q\\q\\q]rZq%q_rkraqZq]qaqnqbq]qXqcqiqeqiq1pSpXq`qfrhqnqbqjqdq]qhqfq]q]q"
  3284. "]qiqeq]q]qmrbq]qnqmqnqgskqeqhqfqjqdqhqfqjqeqYq`qiqfrjreqkskqirnrdrmr]qdq]qZq]qkq)qCqOqIq5q]q q 1q`q"
  3285. "Zq _qkq_qaq mq ^qkqaqnqar_q]qhqfrnqnreqhqgqlqlqfqhqPwLqLw9p_q_phqdqkqcq]qGplslpiu#pmtlpUpkp\\q_q_r8u"
  3286. "mp:p^pjpLpgpepgperipcq^qnq`qnq`qnq`qnq`qnq`qnq`qmqcq]q]q]q]q]q]q]q]q]qhqfskqeqhqfqhqfqhqfqhqfqhqdphp"
  3287. "fqirfqiqeqiqeqiqeqiqermrcwcqkq [q 3qZp Oq nqmqmqeqiqeqiqeqiqeqiqeq_piq4q^pLvatd|evdvcqipasaqkqdq "
  3288. " [qHq_qmq`{hrnpmpcqmqlpcq_q\\pZp]rZq%q_qiqaqZq]qapmqbq^qWqcqiqeqiqdq]qUsSs[qaqdqhqnqbqjqeq\\qgqgq]q^q\\"
  3289. "qiqeq]q]qnraq]qnqmqnqgqnqlqfqfqgqjqeqfqgqjqeqYq`qiqeqjqdqlqmqlqhqnqbqmq]rdq]qZq^pgp=taqns`s`snqatdv_"
  3290. "snqcqnsbq]q]qkqcq]qnsmshqns`saqnsasnqcqnr`tbvaqjqeqiqdqkqkqjrkreqiqdw`q`qZq#tnreqkq^qatauaqnsdqiq`ra"
  3291. "qjqdqiqdpmrcxdqmqmqatbxfyeqiqbqnq`r`q^qfqhrmqmrfqhqgqlqlqgqfqep[pnqnp[p`q`pipbpnqnpNq]taq^qnqnqbqmqb"
  3292. "q\\qIqmpkpmqkqkp$qmpkpmqVqmq\\q`q[pLqjqeump:p^pjpLphpdphpapkpbq^qnq`qnq`qnq`qnq`qnq`qnq`qmqdq\\q]q]q]q]"
  3293. "q]q]q]q]qgqgqnqlqfqfqhqfqhqfqhqfqhqfqfrjrhqiqnqgqiqeqiqeqiqeqiqdqmqbqkrdqmsbt`t`t`t`t`t`tlsfs_t`t`t`"
  3294. "tbq]q]q]q[tbqns`s_s_s_s_s\\q`smpdqjqdqjqdqjqdqjqeqiqdqnscqiq;qlqlqgqgqgqnqmqnqgqjqnqgqgqfq_qjq<{fpjpL"
  3295. "vatd|fxeqkqdqipasaqkqdp \\yNqGplqeqmp`qmqmqcrLqZq`qnpnq\\q%q_qiqaqZq^rbqmqbubqms^qaqkqdqiqdq]qXuf{fu_"
  3296. "q`qlrnqlqjqlqcqkreq\\qgqgq]q^q\\qiqeq]q]t`q]qnqmqnqgqnqlqfqfqgqkreqfqgqkres[q`qiqeqjqdqlqmqlqhs`s]rcq]"
  3297. "qZq#vbwcvbwcwev`wcwcq]q]qlqbq]vnthwcwcwcwcubwcvaqjqdqkqcqkqkqiqkqdqiqdw`q`qZq7smsfxdqlr^qavdvawdqkq_"
  3298. "raqjqdpgpeqntdxdqmqmqcwdyfyeqiqcqlq`raq^qfqhqlqlqfqhqgqlqlqgqfqfrZqZraqarkraqLq^vbq^wbqmqbq]tKpmpfpk"
  3299. "pjp_plp9plpkplpUs[qaqZpLqjqeump:p^pjpaplp_piqdpiqaplqbq_qlqbqlqbqlqbqlqbqlqbqlqbrmqdq\\q]q]q]q]q]q]q]"
  3300. "q]qgqgqnqlqfqfqhqfqhqfqhqfqhqfqerlrgqjqmqgqiqeqiqeqiqeqiqcsaqjqdqnq`vbvbvbvbvbvbvnuivbwcwcwcwcq]q]q]"
  3301. "q]wcwcwcwcwcwcwOwcqjqdqjqdqjqdqjqeqiqdwdqiq;pkqkpgpepgpmumpgpjrmpgpepfq_qkq;{hrkpLxdxf|fxepipdqipas`"
  3302. "pkpcp ZqHqGplpdt_pmplpmshsMqZqaplplp]q&q^qiqaq[qat`plqbvcx_q`ucrkr:uc{cucq`qlvlqjqlqcwdq\\qgqgxdvcqj"
  3303. "tfyeq]q]s_q]qmsmqgqmqmqfqfqgwdqfqgwcv_q`qiqdqlqbqmqmqmqfr`s]qbq\\q[q#pjqcrlrdqkpcrlrcqkrdq^rlrcrlrdq]"
  3304. "q]qmqaq]rlrlqirlrdqkqcrlrerlrcr_qjpbq]qjqdqkqcqlslqhqmqbqkq^q_q`qZq_tjpSqmsmpgrlsdqnsaqmqbqkqdq\\rlrd"
  3305. "qlq_raqjqeqgqgrnqnrdqlqcqmqmqcqkqerkq`qaycqlq_rbq^qfqhqlqlqfqhqgqlqlqgqnvnqgrYqYrbqbrirbqLq_rnpmpdwa"
  3306. "qmqcydq^qlqLpmpfpkpkq`plpa{RpltkpB{gpXpLqjqdtmpcqHp]plp_plp`pipjpipipmsfplpjphr_qlqbqlqbqlqbqlqbqlqb"
  3307. "qlqbqlxkq\\xdxdxdxdq]q]q]q_vjqgqmqmqfqfqhqfqhqfqhqfqhqfqdrnrfqkqlqgqiqeqiqeqiqeqiqcsaqjqdqnq`pjqcpjqc"
  3308. "pjqcpjqcpjqcpjqcpjrlrjqkpbqkrdqkrdqkrdqkrdq]q]q]q]qkrdrlrdqkqcqkqcqkqcqkqcqkqOqkqcqjqdqjqdqjqdqjqdqk"
  3309. "qcrlrdqkq:pnwnpgpnwnpgplslpgpkrlpgpkqkpfq^qlq6qaqlpMzfzfzfzgqipdqipbqmp`qmqc| fqHqHqlpcuasmplpmpiul"
  3310. "qSqZq]p^{+q^qiqaq\\q`ubqlqbpkrdrkrarawcx<tEteq`qlqlqlqjqlqcwdq\\qgqgxdvcqjtfyeq]q]t`q]qmsmqgqmqmqfqfqg"
  3311. "vcqfqgv_t`q`qiqdqlqbqmqmqmqgs_q]qaq\\q[q\"vcqjqeq]qjqdqiqdq^qjqcqjqdq]q]qnq`q]qkqkqiqjqeqiqdqjqeqjqcq^"
  3312. "s^q]qjqdqkqbqmsmqgqmqbqkq_qas_qYsc{Spkqkphqkrcqntcvcqiqeq\\qjqdqmr`tbqjqeqgqgqmqmqdqlqcqmqmqdqiqfqiqa"
  3313. "qaycqlq_qaq^qfqhqlqlqfqhqfqmqmqfqnvnqh}cqc}cqc}cqLq_qmpawbqkqasaq^qkqMpmpfpjsnpaplp`{RplpmqkpB{huatK"
  3314. "qjqbrmpcqJt^r]plpctlpjqktlpmpkpltlpjqhq^qlqbqlqbqlqbqlqbqlqcrlrcqlxkq\\xdxdxdxdq]q]q]q_vjqgqmqmqfqfqh"
  3315. "qfqhqfqhqfqhqfqcteqlqkqgqiqeqiqeqiqeqiqbq`qkrdqmravbvbvbvbvbvbvjqkq]qiqeqiqeqiqeqiqdq]q]q]q^qiqdqjqe"
  3316. "qiqeqiqeqiqeqiqeqiqd{hqkpnqdqjqdqjqdqjqdqjqdqkqcqjqdqkq:pnwnpgpnwnpgplslpgplrkpgpkqkpfq^qlq6qaqmqMzg"
  3317. "|fxdxfqipdqipbqmqaqmqcp \\wLqK{dt]qmqmqkrmrnrSqZqK{TtKq^qiqaq]r\\rdqkq\\qdqiqaqarkrcsmq<tEtfq_qlqlqlqk"
  3318. "qjqdqjqeq\\qgqgq]q^qgqfqiqeq]q]qnraq]qmsmqgqlqnqfqfqgq^qfqgqkq]raq`qiqdqlqbqnqkqnqgt`q^raq\\q[q#wcqjqe"
  3319. "q]qjqdydq^qjqcqjqdq]q]s_q]qkqkqiqjqeqiqdqjqeqjqcq]uaq]qjqcqmqaqmpmpmqfs`qmq_ras_qYscpjtRpkqkphqkrcqk"
  3320. "reqlrcqiqcr_qjqdqmq_qnqbqjqeqlqlqgqmqmqdqlqcqmqmqdqiqfqiqaqaqiqdqjqaq`q^qfqhqlqlqfqhqfrnqnrfqfqh}cqc"
  3321. "}cqc}cqLq_qmp_q^qkq`qMrlqMpmpfpWplpUqRplplqlp=q&qjq`pmp _plp]qkpnpdqhpeqkpnpiq^qjqdqjqdqjqdqjqdqjqdq"
  3322. "jqdqkqdq\\q]q]q]q]q]q]q]q]qgqgqlqnqfqfqhqfqhqfqhqfqhqfqbrdqmqjqgqiqeqiqeqiqeqiqbq`wcqlrcwcwcwcwcwcwc~"
  3323. "kq]yeyeyeydq]q]q]q^qiqdqjqeqiqeqiqeqiqeqiqeqiqd{hqlpmqdqjqdqjqdqjqdqjqcqmqbqjqcqmq9pkqkpgpepgpmumpgp"
  3324. "mrjpgpepfq]pmq:{epmpLzg|evbveqipdqipbqmqaqmpbq [qHqK{cpmq^plqmqkqktRqZqFqOtKq^qiqaq^rZqdy^qdqiqaqaq"
  3325. "iq]q:uc{cudq_qlqlqmqjxdqiqfq\\qgqgq]q^qgqfqiqeq]q]qmrbq]qlqlqgqlqnqfqfqgq^qfqgqkr]qaq`qiqcqnqaqnqkqnq"
  3326. "hrnq`q_r`q\\q[q$qjqcqjqeq]qjqdydq^qjqcqjqdq]q]s_q]qkqkqiqjqeqiqdqjqeqjqcqZsbq]qjqcqmqaqnqmqnqfs`qmq`r"
  3327. "^r`qZr9pkqkphqkrcqjqeqkqcqiqet_qjqcqnq`rnqbqjqeqlqlqgqmqmqdqlqcqmqmqdqiqfqiqaqaqiqdqjqbr`q]qhqgrmqmr"
  3328. "fqhqeweqfqgrYqYrdpnqnpdrirdpnqnpNq_qmp_q]qmqcyPrmqMqmpkpmqkvaplpVqRqmpkpmq=q&qjq`pmp(v_plp\\pkpmpdphq"
  3329. "epkpmpjq]xdxdxdxdxdxdwdq\\q]q]q]q]q]q]q]q]qgqgqlqnqfqfqhqfqhqfqhqfqhqfqcteqnqiqgqiqeqiqeqiqeqiqbq`vbq"
  3330. "jqeqjqdqjqdqjqdqjqdqjqdqjqdqjxkq]yeyeyeydq]q]q]q^qiqdqjqeqiqeqiqeqiqeqiqeqiqQqmplqdqjqdqjqdqjqdqjqcq"
  3331. "mqbqjqcqmq9qlqlqgqgqgqnqmqnqgqnqjqgqgqfq]qnq:{eqnpLzg|dt`tdqipcpipbpkp`sbq Zq plq`pmq_pkqmqkqjrQqZq"
  3332. "Fq'q]rkraq_rYqdy^qdqiqbq`qiq^q6uf{fuaq_qlyjzeqiqeq]qhqfq]q]qhqfqiqeq]q]qlrcq]qlqlqgqkseqhqfq]qhqfqjq"
  3333. "]qaq`qiqcqnq`skshrmraq_q_q[q\\q$qjqcqjqeq]qjqdq\\q^qjqcqjqdq]q]qnq`q]qkqkqiqjqeqiqdqjqeqjqcqXqbq]qjqcq"
  3334. "mqaqnqmqnqgqmq`s_q\\q`qZq7pmpnqmpgqkrcqjqeqkpbqiqeq\\qjqcs_qlqcqjqeqlqlqgqmqmqdqlqcqmqmqdqiqfqiqaq`qkq"
  3335. "drjrdr_q]riqfrnqnreqhqducqhqerZqZrdwdrkrdwOq_qmp_q^w`q`q[sKplslpTplpWqQpmpkqnp<q&qjq`pmp aplp\\pkplpe"
  3336. "phqepkplpjq^zfzfzfzfzfzfxcq]q]q]q]q]q]q]q]q]qhqfqkseqhqfqhqfqhqfqhqfqhqcrnreriqfqiqeqiqeqiqeqiqbq`q]"
  3337. "qjqeqjqdqjqdqjqdqjqdqjqdqjqdqjqdq]q]q]q]q\\q]q]q]q^qiqdqjqeqiqeqiqeqiqeqiqeqiqQqnpkqdqjqdqjqdqjqdqjqb"
  3338. "saqjqbs7qmqmqeqiqeqiqeqiqeqiqeq]qnp7q]rJrnpnresnpnsct_rcqipcqkqcqkqasaq [rkp&plpcplpnr`qkqmqkrltRqZ"
  3339. "qFq'q\\qkq`q`r_pjr^qcpjrcqkrbq`rkrdpkr3sSsLrlrnrhqhqeqjreripeqjsfq]q]riqfqiqeq]q]qkrdq]qgqgqkserjrfq]"
  3340. "rjrfqjrfpiraq_qkqbt`skshqkqaq`q^q[q\\q$qkrcrlrdqkpcrlrcqipdq^rlrcqjqdq]q]qmqaq]qkqkqiqjqdqkqcrlrerlrc"
  3341. "q^pjqbq]rlrbs_rkrfqmq`s`r\\q`qZq6qlrfrmscrlrepkqbrkqdqkpaqjqcs`rlqcrlrernsnrgrnqnrdqlqcrnqnrdrkqdqkra"
  3342. "q`qkqdqhqer^q\\rkqdwdqhqbqarjrdpYqYpbubpipbuNq_rnpmpbq^qnqnq`q`qZqIpgpRplp7pgp;q&rlr`pmp bplp[pkufpiq"
  3343. "dpkukrlpcqhqfqhqfqhqfqhqfqhqfqhqfqjqcripeq]q]q]q]q]q]q]q]qjsfqkserjrfrjrfrjrfrjrfrjrdrlrfrjreqkqcqkq"
  3344. "cqkqcqkqaq`q]qnplqeqkrdqkrdqkrdqkrdqkrdqkrdqksjpjqkpbqipdqipdqipdqipdq]q]q]q]qkqcqjqdqkqcqkqcqkqcqkq"
  3345. "cqkq^qbqkqcrlrdrlrdrlrdrlrbsarlrbs6qkqcqkqcqkqcqkqcqkqdq\\r7q\\qFp\\p]r^rcqipcvbqkqas`r \\vOqIqlpcw_pip"
  3346. "mpivnrRpZpEqbqIq^q[ubwdxdw]qcwbwaq_wcvbq]qRpSp[q^q^qhqexcxeyexdq\\xeqiqeq]q]qjrexdqgqgqjrdxeq\\xeqiqfx"
  3347. "`q_war_ririqiqbqazfq[q\\q$xcwcvbwcxdq]wcqjqdq]q]qlqbq]qkqkqiqjqdwcwcwcq^wbu`wbs_rkrgqkq`q`w`q`qZq$yew"
  3348. "dqmq`wdvaqjqbr`qkqcyeyewcqlsdwcxdw`sauczexdq^umteucqhqbq`xLqJsKsMq^vdxdpgpaq`qYqIqkq bqkq?{+yapmp Jp"
  3349. "fpfpipcpfpiucqhqfqhqfqhqfqhqfqhqfqhqfqjxixexdxdxdxdq]q]q]q]yeqjrdxdxdxdxdxdrjrgpnwdwcwcwcwaq`q]qnuex"
  3350. "dxdxdxdxdxdvnwjvbxdxdxdxdq]q]q]q]wcqjqdwcwcwcwcw^qbwbwcwcwcwaq`w`q4uauauauaucq\\r7p[qFp\\p\\p\\pbqipasap"
  3351. "ip`q^y ctNqIqmqbu_phsgslrSq\\qEqbqIq^qZsawdxcu\\qbt^taq]uataq]q q]qgpiqfqfw`udwcxdqZudqiqeq]q]qirfxdq"
  3352. "gqgqjrbtcqZtcqirfv_q]s_r_rirjrircqazfq[q\\q#tnqcqns`s`snqaucq\\snqcqjqdq]q]qkqcq]qkqkqiqjqbsaqnsasnqcq"
  3353. "]t_t_snqaq^rkrhrkraq`w`q`qZq#smrevbs^t`s`qjqbq`qiqdqnrmqdrmrcubqkrcubqntat^r`sc|fxdq^umtcqaqhqbq^tJq"
  3354. "IqIqLq]tcxLq`qYqHu `u>{+qnrmqapmp Kpepgpiuhpephscqfqhqfqhqfqhqfqhqfqhqfqhqixgudxdxdxdxdq]q]q]q]wcqjr"
  3355. "bt`t`t`t`taphpgplt`s_s_s_s_q`q]qmsctnqctnqctnqctnqctnqctnqbsktgs_uauauaucq]q]q]q[saqjqbs_s_s_s_sNpms"
  3356. "_snqbsnqbsnqbsnqaq`qns_q !p Zp jp#q\\q6q7q lq [sjq Qq -q OqZq]q Cq;q HqWq $rIq`qZq _q iqbqK"
  3357. "qFqIq`q hp$q]u JqYpmpLp .p jp ]p Xr`q[r !p Tp\"p\\p6q6q mq Yx Qr -r Ps\\q_s"
  3358. " Ipkq:q HqWq $qHq`qZq _q iqbqKqFqIq`q hp$q]t IqYpmpLq /q kq Fq_q[q #s Tp\"q"
  3359. "^q6p 1p Vu Rs YsJsMy &v<s HqWq &sHtcq]t _q iqbqKqFqIq`q hp$q 2q2q /q kq Hs_"
  3360. "q]s \"q (r Xy %t;r GqWq &rFscq]s ^q iqbqKqFqIq`q ,q4r 0r lr G"
  3361. "r^q *q "
  3362. " kr i";
  3363. // Define a 47x53 font (extra-large size).
  3364. static const char *const data_font47x53 =
  3365. " "
  3366. " 9])]2_2]T\\8^U^3] E])]"
  3367. "2`4^U^>])]2_4^U^ 6^T\\5])]1_2]T\\8^U^ K])]2`4^V^3] "
  3368. " "
  3369. " U]*\\2a4`V\\8^U^5a F]*\\1\\X\\4^U^=]*\\"
  3370. "2a5^U^ 7aV\\4]*\\1a4`V\\8^U^ J]*\\1\\X\\4^V^3\\ "
  3371. " "
  3372. " S],\\1\\W\\5g8^U^6c F],\\1\\V\\5^U^<],\\2]W]6^U^"
  3373. " 8h3],\\0\\W\\5g8^U^ I],\\1\\V\\5^V^4\\ ;] "
  3374. " "
  3375. " :\\-]2\\U\\6\\V`7^U^7]U] F\\-]2\\T\\6^U^;\\-]3]U]7^U^ 8\\"
  3376. "Va1\\-]1\\U\\6\\V`7^U^ H\\-]2\\T\\6^V^5] =a J] "
  3377. " "
  3378. " N\\/]2\\S\\7\\T]6^U^7\\S\\ E\\/]2\\R\\7^U^:\\/]3]S]8^U^"
  3379. " 8\\T^/\\/]1\\S\\7\\T]6^U^ G\\/]2\\R\\7^V^6] =c L^ "
  3380. " *^ U` "
  3381. " O^ )\\S\\ !^$^3\\ E]"
  3382. "U\\ K^$^4^ G^$^4] J^$^3\\ #^$^3\\ 4^ B[ "
  3383. " &^ Xe "
  3384. " S^ (\\S\\ )Z Q^&^3^2]S\\ A\\S\\ K^&^3^ F^&^4_ >]S"
  3385. "\\9^&^3^2]S\\ W^&^3^ 6^ Q] M[ ?` ![1^H]?` =]4](\\ %` >b4c Bb "
  3386. "?`2a .a Ib Pb Aa <a @b Fb =b F^ :] '] Da A].].].].] <_:]._ "
  3387. " Xh ?c W^ @` La Pa Sa Va5^U^ @` \"f4_ >`0`*^ $^.` <^F]F^F]G`G] "
  3388. " F\\S\\ ;b %a2a2a2a2a <bR\\ D`4^(^3`4`U\\8^V^6\\S\\ J^(^3`4^U^@^(^3_4^U^/^/`U\\8^(^3`"
  3389. "4`U\\8^V^ K^(^3`4^V^1^9]+^V^ ?` O\\ D\\6]M] We D]1]T] 9[3bJ\\@e<])]2])\\ "
  3390. " T]0d3_7h9i/_;k5f?n:f7e 3g :_8i3h@h9n?l5iB]H]C].].]J^B].`I`H_J]<g?g1g?g4hAuB]H]G]C]F]K"
  3391. "_K]S^J^F^G^CrBb7]*b'_ D] :] '] Fc A].].].].] >a:].a !^T_ Bg ` Dd2_8n?"
  3392. "m7g3]:rD]P]P]@g <] 8] 8] B] 3e J^K^ If7^U^+b@d Fb@f5a Ad4e-] :f Ra0d AaF\\HaF\\HeJ\\?]._0_"
  3393. "0_0_0_2\\U\\0tHh@n?n?n?n?].].].]-h:_J]<g8g8g8g8g BhV]G]H]C]H]C]H]C]H]G^G^B]*d5](]2\\X\\4aW]8^V"
  3394. "^6\\S\\ I](]3]X]5^U^?](]3\\W\\5^U^.^R[9aW]7](]2\\X\\4aW]8^V^ J](]2\\X\\4^V^1]8]+^V^ ?a>w "
  3395. "P[ 9[/a:aQa7[ Wl \"h E]1]T]+\\R\\;[4dL]Ag=])]2])\\ U^1f8c8k;j1`;k7h?n;h9g 5i*b:_"
  3396. "8k6kBl=n?l7mD]H]C].].]L_A].`I`H`K]>kAj6kAj9kBuB]H]F]E]E^L_L^R^L^D^I^BrBb7^+b(a D] ;] '] Gd"
  3397. " A].].].].] ;] (b:].b #^Q] Dj !a Ff3_8n?m8i4]:rD]P]P]Bk ?_ 9] 9_ C]&[0f "
  3398. "I]K]=]0g7^U^-fC\\S] IfBf6c B[S]5[S].] <i R\\W\\1]T] B\\W\\G]H\\W\\G]H[S]K]?]._0_0_0_0_2c1uIkBn"
  3399. "?n?n?n?].].].]-l>`K]>k<k<k<k<k EoF]H]C]H]C]H]C]H]F^I^A],h6]*]2\\V\\6]Wa7^V^6\\S\\ H]*]2\\V]6^U"
  3400. "^>]*]3]W]6^U^._V_;]Wa5]*]2\\V\\6]Wa7^V^ I]*]2\\V\\5^V^2]7]+^V^ @]W\\=v P[ 9\\1c<cSd:] "
  3401. "\"o #_S^ F]1]T],]S];[5^V^N]A_T]=]*]0]*\\ U]1^T^;e8`S_<^R_2`;k8^R]?n<_T_;^S^ 6^S_."
  3402. "i>_8m:`R`Cn?n?l9`QaE]H]C].].]M_@].aKaH`K]?`S`Bk8`S`Bk;_R_BuB]H]F]E]D]MaM]P]L]B^K^ArB]1]&])"
  3403. "c D] <] '] G] :].].].].] ;] (^6]*^ #]P^ E^P\\ V^ H^T^4_8n?m:`S`6]:rD]P]P"
  3404. "]C`S` Aa :] :a D]&[1^S\\ I^M^=]0^R[7^U^/^R^EZO\\ L^R^ N]U] :],\\0] <j M\\2]R] >\\H]B\\H]=\\M]>"
  3405. "]._0_0_0_0_0_/uK`R`Cn?n?n?n?].].].]-n@`K]?`S`>`S`>`S`>`S`>`S` H`ScE]H]C]H]C]H]C]H]E^K^@],^"
  3406. "T^5],]1\\V\\6\\U`7^V^6]U\\ F],]2\\T\\6^U^=],]2\\U\\6^U^-e9\\U`4],]1\\V\\6\\U`7^V^ H],]1\\V\\5^V^3]6]+^"
  3407. "V^ B`1`1`1`1`6]W]>u P[ 9]2e>eUf;^ %q $^O\\ F]1]T],]S];[5]T]N\\@]P[=]*]0]2ZR\\RZ $"
  3408. "]2]P]<_W]8]N]<ZL^4a;]+]MZ/]<^P^=^Q^ 7\\O]1nAa9]N_<_M]C]NaA].]+_L^E]H]C].].]N_?].aKaHaL]@"
  3409. "^M^C]P_:^M^C]P_=^M\\6]6]H]F^G^D]MaM]P^N^B^K^-^B]1]&]*e D] =] '] H] 9].].].].] ;] )"
  3410. "^5])^ %^O]8^3]LZ U] I^R^6a9_0]+^M^7]:]H]D]P]P]D^M^ Cc ;] ;c E]&[2^PZ H]M]<]1^-^U"
  3411. "^1]L];[ N]L] Q]S] :\\,\\1] <dU\\ M\\2\\P\\ >\\H\\A\\H\\<\\M\\=]/a2a2a2a2a1_/]V];_M]C].].].].].].].]"
  3412. "-]ObBaL]@^M^@^M^@^M^@^M^@^M^ J^N`D]H]C]H]C]H]C]H]E^K^@]-^Q]5].]1\\T\\7\\S]6^V^5c E].]2]S\\7^U"
  3413. "^<].]2\\S\\7^U^,a6\\S]2].]1\\T\\7\\S]6^V^ G].]1\\T\\6^V^4]5]+^V^ De6e6e6e6e9\\U\\>u P[ :_3f@gVf<"
  3414. "_ &r $]M[ F]1]T],\\R]>d<^T^P]A^OZ=]+].]4]T\\T] &^3^P^=[S]8[K].]4\\X];],]!]<]N]>^O^ "
  3415. " 8ZM^3`P`Ba9]M^=^J\\C]K_B].],^H\\E]H]C].].]O_>].aKaHaL]A^K^D]N^<^K^D]N^>]JZ6]6]H]E]G]C]MaM]"
  3416. "O^P^@^M^-^A]1]&]+_W_ D] >] '] H] 9] B].] ;] )]4](] %]N]:c6] G] J^P^7a8"
  3417. "_1],^K^;c=]H]D]P]P]E^K^ Ee <] <e F]&[2] =^O^<]1] 0\\H\\<\\ P\\H\\ R\\Q\\+]3\\,\\2] <eU\\ M\\3]P\\ >"
  3418. "\\I]A\\I]<\\N]=]/a2a2a2a2a2a1]U]<^J\\C].].].].].].].]-]K_CaL]A^K^B^K^B^K^B^K^B^K^ K]K^D]H]C]H]"
  3419. "C]H]C]H]D^M^?]-]P]4]0]1\\R\\ Ha C]0]2]R] E]0]2\\Q\\ 9c 9]0]1\\R\\ !]0]1\\R\\ ?]4] Di:i:i:i:i"
  3420. ";\\6]G] P\\ :`5g@gWh>a (_ J]KZ F]1]T],\\R\\?h>]R]P\\@]1]+].]3^V\\V^.] T]2]N]5]8ZJ]-]"
  3421. "6]X];]-]!^=]L]?]M] *]5_J_Ec:]L^>]H[C]I^C].],]F[E]H]C].].]P_=].]X]M]X]HbM]A]I]D]M]<]I]D]"
  3422. "M]?]%]6]H]E]G]C^NaN^N]Q^>^O^-^@]0]'],_U_ &] '] H] 9] B].] ;] )]4](] %]N]:d7] "
  3423. " F] K]N]8c8^1],]I]>i@]H]D]P]P]E]I] Fg =] =g G]&[2] <]O];]1] 1\\F\\=\\ Q\\F\\ S\\Q\\+]3\\."
  3424. "] IeU\\ M\\3\\N\\ ?\\I\\@\\I\\=]M\\<]0c4c4c4c4c3a1]U]<]H[C].].].].].].].]-]J_DbM]A]I]B]I]B]I]B]I]"
  3425. "B]I] L]J_E]H]C]H]C]H]C]H]C^O^>].]N] .] '`X_ I] FbWa=bWa=bWa=bWa=bWa<"
  3426. "\\6^I^ ?Z2[ :a5gAiXh?c *^ H] 7]1]T]-]S]Aj>]R]Q]@]1],],\\1^X\\X^,] T]3]L]6]'].]7]W]"
  3427. ";]-]!]<]L]?]M^ +]6^F^F]W]:]K]?]FZC]H^D].]-]DZE]H]C].].]Q_<].]X]M]X]H]X]M]B]G]E]M^>]G]E]"
  3428. "M^@]%]6]H]E^I^B]O^X]O]M^R^=]O^-^@]0]']-_S_ '] '] H] 9] B].] ;] )]4](] %]N]:e8"
  3429. "_ H] L]M]8]W]7^2]-]G]AmB]H]D]P]P]F]G] Hi >] >i J[3] ;^Q^;]1] 2\\RbT\\Ge R\\VdR\\ T\\"
  3430. "Q\\+]4\\2a IfU\\ M\\3\\N\\ ?\\J\\?\\J\\AaM\\ G]W]4]W]4]W]4]W]4]W]4c3^U]=]FZC].].].].].].].]-]H]D]X]"
  3431. "M]B]G]D]G]D]G]D]G]D]G]A[H[B]J`E]H]C]H]C]H]C]H]B]O^>g8]N] 1]T_ 3[ 9] "
  3432. "G_O^?_O^?_O^?_O^?_O^=\\5]I^ @\\3[ ;c6gAy?d7`8]L]7^7]L]>^ H] 6]1]T]-]S]B_W[U]>]R]R]?]1"
  3433. "],],]0d*] T]3]L]6]'].]7\\V];].] ]<]L]@]K] 7Z PZ X]7^D^G]W]:]K]?]/]G]D].]-]/]H]C].].]R_;]"
  3434. ".]X^O^X]H]X^N]B]G]E]L]>]G]E]L]@]%]6]H]D]I]A]O]W]O]L^T^<^Q^-^?]0]'].^O^ Sb7]U`2b4`U]8a8])`"
  3435. "7]T_ M].]%_O_@_2`0`3`/_3c9] )]4](] N_6]N]3^7a/c0_ <^ D[U^ Ga N]L]9]W]6^3]-]G]B`W"
  3436. "]W`C]H]D]P]P]F]G] I_X]X_ ?] ?_X]X_ Nb7]2ZFZ=]Q]:]0] 3[SfU[Ig R[UfS[ T\\Q\\+]5]2a IfU\\ M"
  3437. "\\3\\N\\ ?\\K]?\\K]AaN] G]W]4]W]4]W]4]W]4]W]4]W]3]T]=]/].].].].].].].]-]G]E]X^N]B]G]D]G]D]G]D]G"
  3438. "]D]G]B]J]C]KbF]H]C]H]C]H]C]H]B^Q^=j;]P_9b3b3b3b3b3b3bN`Bb3a2a2a2a V_2_2`1`1`1`1` ;aU] "
  3439. " :]U` S^T]U^A^L^A^L^A^L^A^L^?]5]I] @^5\\ <e7gAy@f;e:]L]8`8^N^?^ G] 6]1]T]-\\R\\A]U["
  3440. "RZ>]R]R\\>]1],],].`(] U^3]L]6]'].]8]V];].]!^<]L]@]K] :] P]#^8^A]I^W^;]K]@].]G^E].].].]H]"
  3441. "C].].]S_:].]W]O]W]H]W]N]C]E]F]L]?]E]F]L]@]%]6]H]D]J^A]O]W]O]L^U^:^S^-^>]0^(]/^M^ Wh:]Wd6f"
  3442. "8dW]:e>h2dW]?]Vd<].].]O_>].]WdScK]Vd8f;]Wd7dW]?]Wa6h>h6]L]B]I]A]P`P]K^L^B^K^@l4]4](] PdU"
  3443. "]A]N]2^8e5g;]Vd?^J^8]6]L] E]V`>pA]S]S]:e6kDo>]L]:^W^6^4].]E]D_U]U_D]H]D]P]P]G]E] K_W]W_ @]"
  3444. " @_W]W_ Qf9]3\\H\\>^S^:]0_ 6[ThT[K]Q\\ S[T\\R]S[ U]S]+]6],] ?]L]@fU\\ M\\3\\N\\ ?\\K\\>\\K\\;]O\\ G"
  3445. "^W^6^W^6^W^6^W^6^W^5]W]4^T]>].].].].].].].].]-]G^F]W]N]C]E]F]E]F]E]F]E]F]E]D_L_E]K]W]F]H]C"
  3446. "]H]C]H]C]H]A^S^<k<]Ra<h9h9h9h9h9h9hTeFf7e6e6e6e;].].].]\"^;]Vd8f7f7f7f7f/^6eX]@]L]?]L]?]L]?"
  3447. "]L]B^K^?]Wd>^K^ O]S]S]B]I]B]I]B]I]B]I]@]5^K^ @]4[ ;f8gAyAg<h<]L]8`7]N]>] F] 6]1]T]"
  3448. "-\\R\\B]T[6]R]S]>^2]-]*\\.`(] U]2]L]6]'].]9]U];].]!];]L]@]K] =` P`'^7]?\\I]U];]K]@].]F]E].]"
  3449. ".].]H]C].].]T_9].]W]O]W]H]W^O]C]E]F]L]?]E]F]L]@]%]6]H]C]K]@^P]W]P^K^V^9]S]-^=]/](]0^K^ Xi"
  3450. ";]Xf9h9fX]<h?h3fX]?]Xg=].].]P_=].]XfVfL]Xg:h<]Xf9fX]?]Xb7i>h6]L]A]K]@^Q`Q^J^N^@]K]?l4]4](]"
  3451. " QfW^A]O^1]6f9h;]Xg@_K]7]6]L]=]G]C^Wc@pA]S]S]<h9mDo>]L]:]U]5^5].]E]E^S]S^E]H]D]P]P]G]E]@"
  3452. "Z+]V]V^-Z4]5ZKZ:]V]V^ Sh9]4^J^>]S]9]._ 8[U_Q[T[L]P\\ S[T\\Q]T[ T]U]*]7]*] @]L]@fU\\ M\\3\\N"
  3453. "\\ ?\\L]>\\L]:]Q]:]1]U]6]U]6]U]6]U]6]U]6^W^5]S]>].].].].].].].].]-]F]F]W^O]C]E]F]E]F]E]F]E]F]"
  3454. "E]C_N_D]L^W]F]H]C]H]C]H]C]H]@]S];]P_=]S^8i:i:i:i:i:i:iVgIh9h9h9h9h<].].].]'d<]Xg:h9h9h9h9h"
  3455. "0^8k?]L]?]L]?]L]?]L]A]K]>]Xf>]K] O]R]R]D]G]D]VZOZV]D]KZV]D]G]A]4]K] @]3[ <g7fAyBi>j=]L]8"
  3456. "`7]N]?] F^ 6]1]T]5uI]T[6]R]S\\<^3]-]*]1d*] U]3]J]7]'].]9\\T];].\\Ua-^;]L]@]K^?].] Uc "
  3457. "Pc+_8]>]J]U];]K]@].]F]E].].].]H]C].].]U_8].]W^Q^W]H]V]O]C]E]F]L]?]E]F]L]@^&]6]H]C]K]?]Q^V]"
  3458. "Q]I^X^8^U^.^<]/](]1^I^ ]R_<aT_;_R\\:^Tb=_S^@h4_Ub?bT^=].].]Q_<].aT_X]T^LbT^;_T_=aT_;^Tb?aT"
  3459. "Z8_R]>h6]L]A]K]?]Q`Q]H^P^?]K]?l4]4](] R^U^W]@]O]0^7g;_S];bT^@`L]8_7]L]>]E]E^W]V]@pA]S]S]"
  3460. "=_T_<oDo?]K^;]U]5_6].\\D]E]R]R]E]H]D]P]P]G]E]A\\+[U]U\\,\\6]6\\L\\;[U]U\\ S_W[V\\9]3^V`V^=^U^9]/a"
  3461. " :[T]G[M\\O\\1ZQZ M[S\\P\\S[ Ud)]8](\\ @]L]@fU\\ M\\3\\N\\9ZQZ0\\L\\=\\L\\8\\Q\\9]1]U]6]U]6]U]6]U]6]U]6"
  3462. "]U]5]S]>].].].].].].].].]-]F]F]V]O]C]E]F]E]F]E]F]E]F]E]B_P_C]L]V^G]H]C]H]C]H]C]H]@^U^;]N^>"
  3463. "]T]6]R_;]R_;]R_;]R_;]R_;]R_;]R_X_T^K_R\\:_S^;_S^;_S^;_S^=].].].]*h=bT^;_T_;_T_;_T_;_T_;_T_1"
  3464. "^9_T`>]L]?]L]?]L]?]L]A]K]>aT_?]K] P]Q]R]E]F]E]V\\Q\\W]E]K\\W]E]F]A]4^L] A^@ZN\\ =i8e@yCk?^R^"
  3465. "=]L]9b8]O^?] Im B]1]T]5uI]T[6]S^T]<^3]-]*]3^X\\X^,] V^3]J]7](^/]9]T];e7]We/]9]N]?]K"
  3466. "^?].] Wd Nd._8]O`U\\T\\K]S]<]L^A]-]F^F].]/]-]H]C].].]V_7].]V]Q]V]H]V^P]D]C]G]L]@]C]G]L]?^']6"
  3467. "]H]C^M^?]Q]U]Q]Ic6^W^._<]/^)]2^G^ !ZM^=`Q^=^NZ;^Q`>^P^=].^Q`?`Q^>].].]R_;].`R^X\\R^M`Q^=^P^"
  3468. ">`Q^=^Q`?`1]MZ;].]L]A^M^?]Q`Q]G^R^>^M^1^4]4](] D]P^A]R^X]@]P^/]9^Vb=^NZ;`Q^AaN^8_7]L]>]E]"
  3469. "F^V]U]>]P]>]S]S]>^P^>`T`7]6]J]<]S]5^6]/]C]G]Q]Q]F]H]D]P]P]H]C]C^&]TZ,^7]7^N^6]TZ H]/^U[TZ9"
  3470. "]2n;]U]8]0d <[U]F[M\\P]2[R[ M[S\\P\\S[ Tb(]9]'\\ @]L]@fU\\ M\\3]P]9[R[1\\M\\<\\M\\7\\R\\8]2]S]8]S]8]"
  3471. "S]8]S]8]S]7]U]6]R]?]-].].].].].].].]-]F]F]V^P]D]C]H]C]H]C]H]C]H]C]B_R_C]L]T]G]H]C]H]C]H]C]"
  3472. "H]?^W^:]M]>]U^6ZM^<ZM^<ZM^<ZM^<ZM^<ZM^<ZMbP]M^NZ;^P^=^P^=^P^=^P^>].].].]+i=`Q^=^P^=^P^=^P^"
  3473. "=^P^=^P^2^:^P^>]L]?]L]?]L]?]L]A^M^>`Q^@^M^ P]Q]Q]F]E]F]W^S^W]F]L^W]F]E]B]3]M^ B^B^O[ =k8"
  3474. "d?xClA^P^>]L]9]X]8^P]>\\ Hl A] 9uI]T[5]T]T]:^ =]*]5^V\\V^.] V]2]J]7](]/^:]S];h:]Xg0]"
  3475. "9^P^?]K^?].]!e Je2_7\\PdW\\S\\L]S]<]M^@]-]E]F].]/]-]H]C].].]X_5].]V]Q]V]H]U^Q]D]C]G]L]@]C]G]M"
  3476. "^?`)]6]H]B]M]>]Q]U]Q]Hb5c-^;].])] B]=_O]=].]O_>]N^>].]O_?_O]>].].]S_:]._P`P]M_O]=]N]>_O]"
  3477. "=]O_?_1]-].]L]@]M]>]RbR]G^R^=]M]1^3]4](] FaSaD^Qa?]R_.]9]R`>]._O]>^N]8`7]L]>]E]G^U]U^?]P]"
  3478. ">]S]S]>]N]>^P^7]6]J]<]S]4^7]/]C]G]Q]Q]F]H]D]P]P]H]C]D_&]&_8]8_N_7] B]/]T[3]1l:^W^8]1]W` >\\"
  3479. "U\\E\\N\\P]3\\S\\ N\\S\\P\\S\\ S_']:]&\\ @]L]@fU\\ M\\2\\P\\8\\S\\2\\N]<\\N]7\\S]8]2]S]8]S]8]S]8]S]8]S]8]S]"
  3480. "7]R]?]-].].].].].].].]-]E]G]U^Q]D]C]H]C]H]C]H]C]H]C]A_T_B]M]S]G]H]C]H]C]H]C]H]>c9]M^?]U]']"
  3481. ".].].].].].`O^N].]N^>]N^>]N^>]N^?].].].],_R^>_O]=]N]=]N]=]N]=]N]=]N]2^:]O_?]L]?]L]?]L]?]L]"
  3482. "@]M]=_O]?]M] O\\P]Q]F\\D]F\\U^U^V]F\\L^V]F\\D]B]3]M] RuJ`O[ >m9c>wCmA]N]>]L]9]X]7]P]?] "
  3483. "Im A] 2\\R\\A]T[5^V^T\\:` ?](\\6]T\\T]/] V]2]J]7])^1_9]S];i;bS^2^8^S_>]K^?].]$e@u@e6_7]QfX\\S\\"
  3484. "M^S^=]N^?]-]E]F].]/]-]H]C].].c4].]U]S]U]H]T]Q]D]C]G]M^@]C]G]M]=c-]6]H]B]M]>^R]U]R^G`4c.^:]"
  3485. ".])] B]=^M]?^/]M^?]L]>]/]M^?^N^?].].]T_9].^O_O^N^N^?]M^?^M]?]M^?^0]-].]L]@]M]>^S]X]S^F^T"
  3486. "^<^O^2_3]4](] GcUcE]Pa?]Vb-]:]O_?].^N^>]O^8a8]L]?]C]H]T]T]?]P]>]S]S]?]L]@^N^8]6]J]=^S^4^8"
  3487. "]/]C]H^Q]Q^G]H]D]P]P]H]C]E_%]%_9]9_L_8] B]0^T[3]0_T_>cWc=]1]U_ ?[U\\C[N]R^4]T] N[R\\Q]R[ 'u"
  3488. "G]&] @]L]?eU\\ M\\2]R]8]T]3\\N\\;\\N\\7]S\\7]3^S^:^S^:^S^:^S^:^S^9]S]8^R]?]-].].].].].].].]-]E]G"
  3489. "]T]Q]D]C]H]C]H]C]H]C]H]C]@_V_A]N]R]G]H]C]H]C]H]C]H]>c9]L]?]U]'].].].].].]._M]O^/]L]?]L]?]L"
  3490. "]?]L]?].].].]-^O]>^N^?]M^?]M^?]M^?]M^?]M^ I]O`?]L]?]L]?]L]?]L]@^O^=^M]@^O^ P]P]P\\G]C\\G]T^"
  3491. "W^T\\G]M^T\\G]C\\B]3^O^ RuJ[X]P[ >o=\\XaX]BwDoC]L\\>]L]:^X^8]P]?] E] 5] 3]S]A^U[4dT];b @"
  3492. "](]6ZR\\RZ.] V]2]J]7]*^7d8]R];]R_<aQ^3]5f<^M_?].]'e=u=e:_6\\Q^S`S]N]Q]=l>]-]E]Fm>k=]-rC].]"
  3493. ".b3].]U]S]U]H]T^R]D]C]G]M]?]C]G]N^<f1]6]H]B^O^=]S^U^S]F_2a.^9].])] A]>^M]?].]M^?]L]>]/]M"
  3494. "^?^M]?].].]U_8].^N^N]N^M]?]L]?^M]?]M^?^0]-].]L]@^O^=]S]X]S]D^V^:]O]2_2]4](] H\\U^W]U\\E]Pa?"
  3495. "]Vb-];]M^?].^M]>^P]7a8]L]?]C]H]T]T]?]P]>]S]S]?]L]@]L]8]6p=]Q]3^9]/]C]H]P]P]G]H]C]Q]Q]G]ViV"
  3496. "]F_$]$_:]:_J_9] B]0]S[3]0]P]>o=]2]S_ @[U\\C[M]T_5^U^;u O[R\\R]Q[ 'uH]/ZQ] ?]L]?eU\\ M\\1]T]7^"
  3497. "U^4\\O]O]I\\O]T`MZQ]S]O]E]3]Q]:]Q]:]Q]:]Q]:]Q]:^S^9]QmO]-m>m>m>m>].].].]1hL]G]T^R]D]C]H]C]H]"
  3498. "C]H]C]H]C]?_X_@]O]Q]G]H]C]H]C]H]C]H]=a8]L]?]U]&].].].].].].^M]O].]L]?]L]?]L]?]L]?].].].].^"
  3499. "M]?^M]?]L]?]L]?]L]?]L]?]L] I]Pa?]L]?]L]?]L]?]L]?]O]<^M]?]O] O]P]P\\G]C\\G]ScS\\G]N^S\\G]P]P\\B"
  3500. "]2]O] QuF]Q[ >oAqDuDqD]L]?]L]:^X^8^R^?\\ D] 5] 3]S]@`X[3bS\\R^G]W^N] P](].\\&] W]1]J"
  3501. "]7]*^7c8]Q];ZM^=`O^4]4d:]M_?].])d:u:d=_5\\R]O^R\\N]Q]=j<]-]E]Fm>k=]-rC].].a2].]U^U^U]H]S]R]D"
  3502. "]C]G]N^?]C]G]P_:g3]6]H]A]O]<]S]S]S]E^1_.^8]-]*] A]>^M]?]/^M^?]K]?]0^M^?]L]?].].]V_7].]M]"
  3503. "M]N]L]@^L]?^M]@^M^?]/]-].]L]?]O]<]S]X]S]C^X^9]O]2^1]4](]0_IZ O[R\\X]S\\G^O_>]Vd9_U];]L]?].]L"
  3504. "]=]P]8]X^9]L]?]C]I^T]S]@]P]>]S]S]?]L]@]L^9]6p=]Q]3^9]/]C]H]P]P]G]H]C]Q]Q]G]ViV]G_#]#_;];_H"
  3505. "_:] B]0]S[3]0\\N\\>o=]2]Q^ A[U\\C[LcX\\6]T]9u O[RfP[ 'uIf7e >]L]>dU\\<] :f5d4]T]:fT\\O^NfT\\UdOeR"
  3506. "\\O^F^3]Q]:]Q]:]Q]:]Q]:]Q]:]Q]:^QmO]-m>m>m>m>].].].]1hL]G]S]R]D]C]H]C]H]C]H]C]H]C]>d?]P^Q]G"
  3507. "]H]C]H]C]H]C]H]<_7]L]?]U^'].].].].].].^L]P].]K]@]K]@]K]@]K]@].].].].]L]?]L]@^L]@^L]@^L]@^L"
  3508. "]@^L] I]Q]X^@]L]?]L]?]L]?]L]?]O]<^M]?]O] O\\WmX]H\\WmX]H\\QaR]H\\N^R]H\\O]P]C]2]O] QuF]R\\ ?qC"
  3509. "sDtDrE]L]?]L]:]V]7]R]>x '] 5] 3\\R\\?e3^R\\SbJ^V^O] P](].\\&] W]1]J]7]+^6e:]Q]-^>_M]5^6"
  3510. "h<^O` Qe8u8e@^5]R\\M]R\\O^Q^>m?]-]E]Fm>k=]KdFrC].].b3].]T]U]T]H]S^S]D]C]G]P_>]C]Gk6f5]6]H]A"
  3511. "^Q^<]S]S]S]F_1_/_8]-]*] A]>]K]A].]K]@]J]?]0]K]?]L]?].].]W_6].]M]M]N]L]@]J]@]K]A]K]?]/^.]"
  3512. ".]L]?]O]<]T^W]T]C^X^9^Q^3^1]3]']3dN\\ P\\R`Q[G]N_>]Q`;bW];\\K^?]/]L]=]Q^8]W]9]L]?]C]I]S]S]@]P"
  3513. "]>]S]S]@]J]B^L^9]6p>^Q^4^9]/]C]H]P]P]G]H]C]Q]Q]G]ViV]H_\"]\"_<]<_F_;] B]1]R[3]1]N]8a6]2]P^ B"
  3514. "[U\\C[K`V\\7]T]8u O[RdN[ 'uIf5a <]L]=cU\\<] :f3`1]T];fU\\N^NfU\\T[S]NaQ\\N^G^3^Q^<^Q^<^Q^<^Q^<^Q"
  3515. "^;]Q]:]PmO]-m>m>m>m>].].].]1hL]G]S^S]D]C]H]C]H]C]H]C]H]C]=b>]P]P]G]H]C]H]C]H]C]H]<_7]L]?]U"
  3516. "_(].].].].].].]K]Q].]J]A]J]A]J]A]J]@].].].].]L]?]L]@]J]A]J]A]J]A]J]A]J] K]P\\V]@]L]?]L]?]L]"
  3517. "?]L]?^Q^<]K]@^Q^ O\\WmX]H\\WmX]H\\P_Q]H\\O^Q]H\\O]P]C]2^Q^ D^<]R[ >qDuEsCqD]L]?]L]:]V]7]R]>x "
  3518. " '] 5] 3\\R\\=f+]TdL^T^P] P](].\\2u *]1]J]7],^-_=]P],]>_M]5]7_R^<^Qa Sd .dC^4\\R]M]R\\O]O"
  3519. "]>]N_@]-]E]F].]/]KdF]H]C].].]X^4].]T]U]T]H]R]S]D]C]Gk=]C]Gj1c6]6]H]@]Q];^T]S]T^Ga1].^7]-]*"
  3520. "] Lh>]K]A].]K]@]J]?]0]K]?]L]?].].]X_5].]M]M]N]L]@]J]@]K]A]K]?]._0].]L]>]Q];^U]V]U^Bb7]Q]"
  3521. "3^1^3]'^6iS^ P[P^P[G]N_>]N^=dX]<]J]>^1]L]=^R]8^W]9]L]@]A]J]S]S]@]P]>]S]S]@]J]B]J]9]6]J]>]O"
  3522. "]5^8]/]C]H]P]P]G]H]B]R]R]F]C]Iz<]<z=]=z<] B]1]R[7j:\\L\\7_5]2]P^ B[U\\C[ V]T]7u O[R\\U^O[ T] "
  3523. " ]L];aU\\<] I]T],]O[X\\>]K]@]O[X\\I`3]O]<]O]<]O]<]O]<]O]<]O];]P]?]-].].].].].].].]-]E]G]R]"
  3524. "S]D]C]H]C]H]C]H]C]H]C]<`=]Q]O]G]H]C]H]C]H]C]H];]6]L]?]T_4h9h9h9h9h9h9hK]Q].]J]A]J]A]J]A]J]"
  3525. "@].].].]/]J]@]L]@]J]A]J]A]J]A]J]A]J]?tG]Q\\U]@]L]?]L]?]L]?]L]>]Q];]K]?]Q] N\\WmX]H\\WmX]H\\P_"
  3526. "Q]H\\P^P]H\\O]P]C]1]Q] C]:]S[ ?sEvEqAoC]L]?]L];^V^8^T^>x '] 5] 4]S]<g-\\T^V^M]S_Q\\ O](]"
  3527. ".\\2u Se =^1]J]7]-^*^?]O],^?^K]7^7]N]<^Sb Sa (aC]3\\R\\K\\R\\P^O^?]L^A]-]E]F].]/]KdF]H]C].].]W"
  3528. "^5].]T^W^T]H]R^T]D]C]Gj<]C]Gj-`7]6]H]@]Q]:]U^S^U]Fb2]/^6]-^+] Nj>]K]A].]K]@p?]0]K]?]L]?]"
  3529. ".].b3].]M]M]N]L]@]J]@]K]A]K]?].c4].]L]>]Q]:]U]V]U]@`6^S^4^5b2]&b<u P[O]P\\H]N^=]M]>^Ua<]J]="
  3530. "c7]L]<]S^8]V^:]L]@]A]J]S]S]@]P]>]S]S]@]J]B]J]9]6]J]?^O^7^7]/]C]H]P]P]G]H]B]R]R]F]C]Iz<]<z="
  3531. "]=z<] B]1]R[7j:\\L\\7_ C^P] B[U\\C[ W]T] W] O[R\\T^P[ T] ]L]7]U\\<] H]T]-\\O\\X\\>\\I\\@\\O\\X\\J`"
  3532. "3^O^>^O^>^O^>^O^>^O^=]O]<^P]?]-].].].].].].].]-]E]G]R^T]D]C]H]C]H]C]H]C]H]C];^<]R]N]G]H]C]"
  3533. "H]C]H]C]H];]6]L]?]S`8j;j;j;j;j;j;|Q].pApApAp@].].].]/]J]@]L]@]J]A]J]A]J]A]J]A]J]?tG]R]U]@]"
  3534. "L]?]L]?]L]?]L]>^S^;]K]?^S^ N\\WmX]H\\WmX]H\\QaR]H\\Q^O]H\\O]P]C]1^S^ D]9]T\\ ?sFwDo?nC]L]?]L];"
  3535. "]T]7]T]=] Hj ?] 4]S]8d/]T]T]N^R_R\\ O](] =u Se =]0]J]7].^(]?]O]+]?^K]7]7]L]<gX] Sa ("
  3536. "aC]3\\R\\K\\R\\P]M]?]K]A]-]E]F].]/]D]F]H]C].].]V^6].]S]W]S]H]Q]T]D]C]Gg9]C]G]Q_,^7]6]H]@^S^:]U"
  3537. "]Q]U]G^X]2]0^5],]+] Pl>]K]A].]K]@p?]0]K]?]L]?].].a2].]M]M]N]L]@]J]@]K]A]K]?]-f8].]L]>^S^"
  3538. ":]U]V]U]?^4]S]4^4`0]$`<^Si O[O\\O\\H]N^=]M^@^S`<]J]=c7]L]<]S]8^U]:]L]@]O]O]J]S]S]@]P]>]S]S]@"
  3539. "]J]B]J]9]6]J]?]M]7]6]/^E^H]P]P]G]H]A]S]S]E]C]Iz<]<z=]=z<] B]1]R[7j:\\L\\6] A^Q] B[U\\C[Ni:]T]"
  3540. " V] O[R\\S]P[ T] ]L]6\\U\\<] Dh2]T]/]P\\W\\?]I\\A]P\\W\\K`2]M]>]M]>]M]>]M]>]M]>^O^=]O]?]-].].]"
  3541. ".].].].].]-]E]G]Q]T]D]C]H]C]H]C]H]C]H]C]<`=]S]M]G]H]C]H]C]H]C]H];]6]M^?]R`;l=l=l=l=l=l=~Q]"
  3542. ".pApApAp@].].].]/]J]@]L]@]J]A]J]A]J]A]J]A]J]?tG]S]T]@]L]?]L]?]L]?]L]=]S]:]K]>]S] M]P]P\\G]"
  3543. "C\\G]ScS\\G]S^N\\G]P]P\\B]0]S] D]7\\T[ >sFwCn?mB]L]?]L];]T]7]T]=] Hi >] 4]S]7[Xa1]T^T^O]"
  3544. "P_T] O](] =u Se =]0]J]7]/^'^A]N]+]?^K]7]8^L^<eW] Sd .dC]3\\R\\K\\R\\P]M]?]K]A]-]E]F].]/]D]F]H"
  3545. "]C].].]U^7].]ScS]H]Q^U]D]C]G]/]C]G]O^,^8]6]H]?]S]9]U]Q]U]H^W^3]1^4],]+] Q`P]>]K]A].]K]@p"
  3546. "?]0]K]?]L]?].].b3].]M]M]N]L]@]J]@]K]A]K]?]+e9].]L]=]S]9]V]T]V]@_4]S]5_4b2]&b<\\Nd M[O]P\\H]N"
  3547. "^=]L]@]Q_<]J]?e7]L];]T]8]T]:]L]@]O]O]J]S]S]@]P]>]S]S]@]J]B]J]9]6]J]?]M]8^6].]E]G]P]Q^G]H]A"
  3548. "^T]T^E]C]Iz<]<z=]=z<] B]1]R[3]1\\L\\6] A_R] B\\U\\E\\Ni:]T] V] O\\S\\R]R\\ T] ]L]6\\U\\<] Dh2]T]"
  3549. "/\\O[V\\?\\H\\A\\O[V\\L`1]M]>]M]>]M]>]M]>]M]>]M]>^O]?]-].].].].].].].]-]E]G]Q^U]D]C]H]C]H]C]H]C]"
  3550. "H]C]=b>]T]L]G]H]C]H]C]H]C]H];]6]M]>]Qa>`P]>`P]>`P]>`P]>`P]>`P]>`PoQ].pApApAp@].].].]/]J]@]"
  3551. "L]@]J]A]J]A]J]A]J]A]J]?tG]T]S]@]L]?]L]?]L]?]L]=]S]:]K]>]S] L\\P]P\\F\\C\\F\\T^W^T\\F\\T^M\\F\\C\\B]"
  3552. "0]S] E^7]U[ >sFwBl=kA]L]?]L]<^T^8^V^=] Ij >] <u=[U^1\\S]R]O]O_U\\ N](] 1] Ge =]0]J]7]"
  3553. "0_&]A]N]+]?^K]8^8]J]:aU\\ Pe 4eA]3\\R\\K\\R\\Qo@]J]A].]F^F].].]E]F]H]C].].]T^8].]RaR]H]P]U]C]E"
  3554. "]F].]E]F]N^,]8]6]H]?]S]9^V]Q]V^H^V^4]2_4],]+] Q]M]>]K]A].]K]@],]0]K]?]L]?].].c4].]M]M]N]"
  3555. "L]@]J]@]K]A]K]?](d;].]L]=]S]9^W]T]W^@`5^U^5^/_3]'_8ZJ` K[O]P\\H]N^=]L]@]P];]J]@_0]L];]U^9^T"
  3556. "^;]L]@]O]O]J]S]S]@]P]>]S]S]@]J]B]J]9]6]J]@^M^:^5].]E]F]Q]Q]F]H]@^U]U^C]E]G_\"]\"_BZT]TZB_F_;"
  3557. "] B]1]R[3]1\\L\\?o I_S] A[U]F[ V]T] W] N[S\\R]R[ S] ]L]6\\U\\ ']T]/\\O\\V\\@\\H\\A\\O\\V\\M_0o@o@o"
  3558. "@o@o?m>l>].].].].].].].].]-]F^G]P]U]C]E]F]E]F]E]F]E]F]E]=d?^V]L]F]H]C]H]C]H]C]H];]6]N^>]O`"
  3559. "?]M]>]M]>]M]>]M]>]M]>]M]>]M]?].].].].]-].].].]/]J]@]L]@]J]A]J]A]J]A]J]A]J] K]U]R]@]L]?]L]?"
  3560. "]L]?]L]=^U^:]K]>^U^ L\\P]Q]F\\D]F\\U^U^V]F\\U^M]F\\D]B\\/^U^ OuD]V[ =sFwBk;i@]L]?]L]<]R]7]V];]"
  3561. " F^ Nu=[T^3]S]R]O]N_V\\ N](] 1] ].]L]6]1_%]Aq0]>]K]8]7]J]/] Md:u:d>]3\\R\\K\\S\\Po@]"
  3562. "J]A].]F]E].].]E]F]H]C].].]S^9].]RaR]H]P^V]C]E]F].]E]F]M],]8]6]H]>]U^8]W^Q^W]H^U^4]2^3]+],]"
  3563. " R^M]>]K]A].]K]@],]0]K]?]L]?].].]X_5].]M]M]N]L]@]J]@]K]A]K]?]$`;].]L]=^U^8]W]T]W]@b5]U]5"
  3564. "^,]3]'] J\\Q_Q[G]N^=]L]A]O];]J]@].]L];]U]8]R];]L]@]O]O]J]S]S]@]P]>]S]S]@]J]B]J]9]5]L]?]K];"
  3565. "^4].^G^F]Q]Q]F]H]?_W]W_B]E]F_#]#_B\\U]U\\B_H_A\\U]U[ H]1]R[3]1]N]?o H`V] @[T]G[ U]T] X] N[S\\Q"
  3566. "]S[ S] ]L]6\\U\\ (]T]/]P\\U\\A]I]B]P\\U\\M^/o@o@o@o@o@o@m>].].].].].].].].]-]F]F]P^V]C]E]F]"
  3567. "E]F]E]F]E]F]E]>_X_?]W^L]F]H]C]H]C]H]C]H];]6]P_=]M^@^M]?^M]?^M]?^M]?^M]?^M]?^M]?].].].].]-]"
  3568. ".].].]/]J]@]L]@]J]A]J]A]J]A]J]A]J] K]U\\Q]@]L]?]L]?]L]?]L]<]U]9]K]=]U] K]Q]Q]F]E]F]W^S^W]F"
  3569. "]W^L]F]E]B\\.]U] NuC\\V[ =eXZXdFgXhAi9h@]L]?]L]<]R]7]V];] E] Nu=[S]3\\R]R]O]M_X\\ M]("
  3570. "] 1] ].]L]6]2_$]Aq0]>]K]8]7]J]/] Ke=u=e<]3\\R\\K\\S\\Po@]J]A].]F]E].].]E]F]H]C].].]R^:].]Ra"
  3571. "R]H]O^W]C]E]F].]E]F]M^-]8]6]H]>]U]7]W]O]W]I^S^5]3^2]+],] R]L]>]K]A].]K]@],]0]K]?]L]?].]."
  3572. "]W_6].]M]M]N]L]@]J]@]K]A]K]?]\"_<].]L]<]U]7]W]T]W]Ac5^W^6^+^4](] H[R\\X]S\\G]N^=]L]A]O];]J]A"
  3573. "^.]L]:]W^9^R];]L]@]O]O]J]S]S]@]P]>]S]S]@]J]B]J]9]5]L]?]K];^4]-]G]D]R]R]E]H]>kA]E]E_$]$_B^V"
  3574. "]V^B_J_A^V]V] I]1]R[3]0\\N\\>o G`X] ?\\U_Q[T\\ T]T] ] N\\T\\Q]T\\ S] ]L]6\\U\\ )]T].\\P\\T\\A\\I]A"
  3575. "\\P\\T\\N^.o@o@o@o@o@o@m>].].].].].].].].]-]F]F]O^W]C]E]F]E]F]E]F]E]F]E]?_V_@]W]K]F]H]C]H]C]H"
  3576. "]C]H];]6k<]L^A]L]?]L]?]L]?]L]?]L]?]L]?]L]?].].].].]-].].].]/]J]@]L]@]J]A]J]A]J]A]J]A]J] K]"
  3577. "V\\P]@]L]?]L]?]L]?]L]<^W^9]K]=^W^ J]R]R]D]G]D]W\\Q\\W]D]W\\L]D]G]A\\.^V] NuC]W[ <cWZXdEfXh@g8"
  3578. "g?]L]?]L]=^R^8^X^:] F] G\\R\\5[S]4]R]R]O]Lb M](\\ 0] ].]L]6]3_#]Aq0]>]K]9]6]J]/] H"
  3579. "e@u@e H\\R]M]T]Q^J]A]J]@]/]G^E].]-]F]F]H]C].].]Q^;].]Q_Q]H]N]W]B]G]E]-]G^F]L]-]8]6]I^>^W^7]"
  3580. "W]O]W]I^R^6]4^1]+],] R]M^>^M^@]/^M^?]-]0^M^?]L]?].].]V_7].]M]M]N]L]@^L]?^M^A^M^?] ]<].]L"
  3581. "]<]U]7]X]R]X]B^W^5]W]6^)]4](] H\\T]W]U\\F]O_=]L]A]P^;^L^A]-]L]:]W]8]P]<]L]@]O]O]J^T]T]?]P]>"
  3582. "]S]S]@^L]A^L]8]5]L]@^J]=^3]-^I^D^S]S^E]H]<g>]G]C_%]%_A_W]W_A_L_@_W]W_ J]0]S[3]0]P]5]4],b ="
  3583. "[ThT[ R]T]!] M[T\\P]U[ R] ]L]6\\U\\ *]T].]P[S\\B]J]A]P[S\\N].^J]B^J]B^J]B^J]B^J]B^K^A]M]=]"
  3584. "/].].].].].].].]-]G^F]N]W]B]G]D]G]D]G]D]G]D]G]?_T_AbK]E]I^C]I^C]I^C]I^;]6j;]K]A]M^?]M^?]M^"
  3585. "?]M^?]M^?]M^?]M_?].].].].].].].].]/]J]@]L]@^L]@^L]@^L]@^L]@^L] J^X]Q]?]L]?]L]?]L]?]L];]W]8"
  3586. "^M^<]W] I]R]S]C]H]C]VZOZW]C]VZL]C]H]@\\-]W] MuC]X[ ;cWZWbDeWZXe>e6e>]L]?]L]=]P]8^X^:] "
  3587. " F^ H\\R\\5[S]5]Q]R]O^L` K]*] 0] !^.]L]6]4_\"]2],^>^M]8]6]J]0] DeCuCe E]R\\M]T\\P]I]A]J]@"
  3588. "]/]G]D].]-]F]F]H]C].].]P^<].]Q_Q]H]N^X]B]G]E]-]G]E]L^.]8]5]J]<]W]6^X]O]X^J^Q^6]5^0]+^-] "
  3589. "R]M^>^M]?].]M^?]-]/]M^?]L]?].].]U_8].]M]M]N]L]?]L]?^M]?]M^?] ]<].]M^<^W^6aRbB^V^6]W]7^(]4]"
  3590. "(] GcUcE]P_=]L]A]P]9]L]@]-]L]:^X]9^P]<]M^@]P^O]I]T]T]?]P]>]S]S]@^L]@]L]8]5]M]?]I]>^2],]I]"
  3591. "B_U]U_D]H]:c<]G]B_&]&_?_X]X_?_N_>_X]X_ I]0]S[3]0_T_5]4]+` ;[SfU[ P^U^#] L[U\\P]V[ Q] ]M^"
  3592. "6\\U\\ ,^U^-\\P\\S\\B\\J]@\\P\\S\\N].]I]B]I]B]I]B]I]B]I]B]I]B^M]=]/].].].].].].].]-]G]E]N^X]B]G]D"
  3593. "]G]D]G]D]G]D]G]@_R_A`J]D]J]A]J]A]J]A]J]:]6g8]K]A]M^?]M^?]M^?]M^?]M^?]M^?]M_?].].].].].].]."
  3594. "].].]L]?]L]?]L]?]L]?]L]?]L]?]L]3^;aP]?]M^?]M^?]M^?]M^;]W]8^M];]W] H]S]T^B]J^B]J^B]J^B]J^@"
  3595. "\\-]W] G^1_ :aW[V`BcW[Wc<d5c=]L]>]N]<]P]7]X]8] F]KZ X]S]5[S]5\\P]R]N]K_ K]*] 0] !]"
  3596. ",]N]5]5_\"]1],]<]M]9^6^L^0] Ad Nd A\\R]O^U\\P^I^B]K^?]H[C]H^D].],]G]F]H]C].].]O^=].]P^Q]H]M]"
  3597. "X]A]I]D],]I^E]K]AZH^8]5]J]<]W]5bObJ^O^7]6_0]*]-] R]M^>^M]?^/]M^?^.]/]M^?]L]?].].]T_9].]M"
  3598. "]M]N]L]?]L]?^M]?]M^?] ]<].]M^;]W]5aRaB^U^6c8_(]4](] FaSaD]P_=]M]@]P]9]L]@]-]L]9b9]O^=^N^?"
  3599. "\\P_Q]H]T]T]?]P]=]T]T]?^L]@]L]8]4]N]@^I^?]1],^K^A`W]W`C]H]7]8]I]@^&]&^=i=^N^<i H]0^T[3]1l6]"
  3600. "4])_ <\\RbT\\ O]T]#] L\\V\\O]X\\ M^N^6\\U\\ ,]T]-\\OhF\\J]@\\OhQ]/^I^D^I^D^I^D^I^D^I^C]I]B]L]<"
  3601. "]H[C].].].].].].].]-]H]D]M]X]A]I]B]I]B]I]B]I]B]I]@_P_B_J]C]J]A]J]A]J]A]J]:]6].]K]A]M^?]M^?"
  3602. "]M^?]M^?]M^?]M^?]M_?^/^/^/^/^/].].].].]L]?]L]?]L]?]L]?]L]?]L]?]L]3^;`O]?]M^?]M^?]M^?]M^;c8"
  3603. "^M];c G^U]U^@^M^@^M^@^M^@^M^?\\-c H^0_ 9^U[U^@aV[Va:b3a<]L]>^P^=^P]7]X]8_ H^M[ F] 6"
  3604. "]S]>ZQ[T^6]P]S^N^K^ K]*] 0]:] 8]0],]O^5]6_2ZI]1]-^<^O^9]4]L]0]<].] Uc Pc1]2\\Q^S`W^P]G]B]K]"
  3605. ">^J\\C]I^C].],^H]F]H]C].].]N^>].]C]H]MbA^K^D],^K^D]K^B[I]7]5^L^<c5aMaJ^N]7]6^/]*]-] R^O_>"
  3606. "_O]=].]O_>].].]O_?]L]?].].]S_:].]M]M]N]L]>]N]>_O]=]O_?] ]<]-]O_;]X^5aRaC^S^6a8_']4](] D]P"
  3607. "^B^Ra>^N]@]Q]7]N]?^.]L]9a8]N]=^N^?]Q_Q]G]U]U]>]P]=]T]T]?_N]>]N]7]4^P^@]G]@^1]+^M^?mB]H]7]8"
  3608. "^K^?\\%]%\\;g;\\L\\:g G]/]T[3]2n7]4]'^ <\\F\\ M\\S\\ J\\F\\ L^N^6\\U\\ ,\\S\\-]OhG]K]@]OhQ]LZ=]G]"
  3609. "D]G]D]G]D]G]D]G]D]G]D^L]<^J\\C].].].].].].].]-]J_D]MbA^K^B^K^B^K^B^K^B^K^A_N_B^K]B^L^A^L^A^"
  3610. "L^A^L^:]6].]K]A^O_?^O_?^O_?^O_?^O_?^O_?^Oa?].].].].]/].].].]-]N]>]L]>]N]=]N]=]N]=]N]=]N]2^"
  3611. ";_O]=]O_>]O_>]O_>]O_:a7_O]9a E^P_>^P_>^P_>^P_>^P_>\\,a H^.] /[5]T[S\\8a1`<]L]=^R^<]O^8b7_ "
  3612. " H^O\\ F] 6\\R\\=[R[U^5\\N]T]L^M` L]*] 0]:] 8]1^+]P]4]7_1[L_1]<ZL^:^Q^8]4^N^>ZM];].] R` P"
  3613. "`.]2]QfXaN]G]B]L^=^L]C]K_B].]+_J]F]H]C].].]M^?].]C]H]La@^M^C]+^M^C]J]B]L^7]4^N^:a4aMaK^M^8"
  3614. "]7^.]*^.] Q]P`>`Q^=^NZ;^Q`>_LZ>].^Q`?]L]?].].]Q^;].]M]M]N]L]>^P^>`Q^=^Q`?]/ZL];]-^Q`:a4`"
  3615. "P`D^Q^7a8^&]4](] S]Sb>_P^@]R^7^P^>^MZ<]L]9a9]M]=_P`XZB]Q_Q]G^V]V^>]P]=^U]U^?`P^>^P^6]4]Q"
  3616. "^?]G]A^0]*^O^<i@]H]7]7^M^=Z$]%Z8e9ZKZ7e F]/^U[TZ9]3^V`V^8]4]&^ <\\H\\ K[R[ I\\H\\ K_P`XZ9"
  3617. "\\U\\ ,[R[,\\E\\D\\K]?\\E\\M]O\\=]G]D]G]D]G]D]G]D]G]D]G]D]K];^L]C].].].].].].].]-]K_C]La@^M^@^M^"
  3618. "@^M^@^M^@^M^A_L_C`N^A^N^?^N^?^N^?^N^9]6].]L]?]P`>]P`>]P`>]P`>]P`>]P`>]P]X^LZN^NZ;_LZ>_LZ>_"
  3619. "LZ>_LZ?].].].]-^P^>]L]>^P^=^P^=^P^=^P^=^P^2^:^P^=^Q`>^Q`>^Q`>^Q`:a7`Q^9a Dk<k<k<k<k>],a "
  3620. "H]-] /[,[._0_;]L]=j<]N]7`5a J_S^ F] 6\\R\\=^U[W_5]N^V^K_Rd L],] /]:] 8]1])^T^3]8_0^Q`0"
  3621. "]<]Q_8^S^8^3_R_=]R^:].] O] P]+]1\\PdW`N^G^C]N_;`R`C]NaA].]*`O`F]H]C].].]L^@].]C]H]La?`S`B]*"
  3622. "`S`B]J]B`Q_6]3_R_9a4aMaL^K^9]8^-])].] Q_Tb>aS^;_R\\:^Sa=`Q]>]-^Sa?]L]?].].]P^<].]M]M]N]L]"
  3623. "=_T_=aS^;^Sa?]/^R_:]-^Sa:a3_P_C^P^7_8^%]4](] S_V^X^?aS^>]T^5_T_=`R]<]L]8_8]M^>`SdA]SaS]E"
  3624. "^W]W^=]P^=_W]W_>]X]T_<_T_5^4^T^?^G^C^/])^Q^8c=]H]7]6`S` ?] ;c >c E]._W[V\\9]4^J^9]4]%] ;]L]"
  3625. " IZQZ H]L] !u ,`Sd9\\U\\ ,ZQZ,]E\\E]L]?]E\\M_S^>^G^F^G^F^G^F^G^F^G^F^G^F^K]:`R`C].].].].]."
  3626. "].].]-]ObB]La?`S`>`S`>`S`>`S`>`S`?]J]CcS`?_R_=_R_=_R_=_R_8]6].]V[R^?_Tb>_Tb>_Tb>_Tb>_Tb>_T"
  3627. "b>_T^V_Q]M_R\\:`Q]=`Q]=`Q]=`Q]?].].].],_T_=]L]=_T_;_T_;_T_;_T_;_T_1^:`T_;^Sa=^Sa=^Sa=^Sa9_6"
  3628. "aS^7_ Bi:i:i:i:i=]+` I],] /[,[-].]:]L]<h;]N]7`3q \"h E] 7]S]=k5]LdIjW^ M],] /]:] 8]1"
  3629. "](f9k?n?l/]<j6g7]1j<h9].] LZ PZ(]1]O`U]K]E]Cm8kBn?n?](nE]H]C].].]K^Am>]C]H]K`>kA])kA]J^Cm5"
  3630. "]2j7_2`M`K^J]9]8tC])].] PgX]>]Xf9h9fX]<k>],fX]?]L]?].].]O^=].]M]M]N]L]<h<]Xf9fX]?]/j9d4g"
  3631. "X]:a3_P_D^O^7_8m4]4](] RfXaBk=^V^3h;j<]L]8_9^L]>qA^U]W]U^Di<]O`?k=]Xg:h3a7f>uCn?]/eSe;]:"
  3632. "]H]7]5k >] :a <a D]-h>n?\\H\\8]4]%] 9^R^ *^R^ Xu ,q9\\U\\ /]D\\F]LfH]D\\Li>]E]F]E]F]E]F]E"
  3633. "]F]E]F]E]F]JnIkBn?n?n?n?].].].]-n@]K`>k<k<k<k<k=[H[Co<j;j;j;j7]6].]Vf=gX]=gX]=gX]=gX]=gX]="
  3634. "gX]=gTjLh9k<k<k<k?].].].]+h<]L]<h9h9h9h9h Fk:gX]=gX]=gX]=gX]9_6]Xf6_ @e6e6e6e6e;]+_ G\\+["
  3635. " /].]-[,[9]L];e:^N^8`2p e D] 7]S]<i4\\JbGgT^ M\\,\\ .]:] 8]1]'d8k?n>i-]<i4e6]0h;g8].] "
  3636. " I]0]3]E]Cl6h@l=n?]&jC]H]C].].]J^Bm>]C]H]K`<g?]'g?]I]Bj3]1h6_2_K_L^I^:]8tC])].] OdV]>]Wd"
  3637. "6f8dW]:i>]+dW]?]L]?].].]N^>].]M]M]N]L];f;]Wd7dW]?]/i7c3dV]9_2_P_E^M^8_8m4]4](] QdV`B]Xe;"
  3638. "d1f8h<]L]8_9]K]>]XdW_@eWeBg;]O`=g;]Vd8f1`6d=uCn?]/eSe;]:]H]7]3g <] 9_ :_ C]+f>n>ZFZ7]4]%] "
  3639. "7f &f Vu ,]XdW_9\\U\\ /\\C\\F\\KfH\\C\\Kg=]E]F]E]F]E]F]E]F]E]F]E]F]JnHh@n?n?n?n?].].].]-l>"
  3640. "]K`<g8g8g8g8g J]Vh:h9h9h9h6]6].]Ve;dV]<dV]<dV]<dV]<dV]<dV]<eRiJf7i:i:i:i?].].].]*f;]L];f7f"
  3641. "7f7f7f F]Xe7dV]<dV]<dV]<dV]9_6]Wd5_ <\\-\\-\\-\\-\\6]+_ FZ*[ /].],Z+Z9]L]8`8]L]7^.m W` "
  3642. "A] 7\\R\\7b2]H^BaP_ O].] .]:\\ 7]2^%`6k?n:b*]9c/a5],b6b5].\\ H]/\\4]C]Di0b=h9n?]#c?]H]C].].]I"
  3643. "_Dm>]C]H]J_9a<]$d?]I^?c0].b3_2_K_M^G^;]8tC](]/] M`T]>]U`2b4`U]7c;])`U]?]L]?].].]M^?].]M]"
  3644. "M]N]L]8`8]U`3`U]?],c2a0_T]9_2^N^F^K^8]7m4]4](] O`R^B]Va8b-`3d:]L]7]9^J]?]V`T]>cUc?c9]N_:"
  3645. "a8]T`3`-_4`<wDn?]/eSe;]:]H]7]0a 9] 8] 8] B])b<n @]4]&^ 5b \"b Tu ,]V`T]8\\U\\ 0].].]0b"
  3646. ";]C]H]C]H]C]H]C]H]C]H^E^H^JnEb=n?n?n?n?].].].]-h:]J_9a2a2a2a2a G\\Rb4b3b3b3b3]6].]Vc7`T]:`T"
  3647. "]:`T]:`T]:`T]:`T]:aMcEb2c4c4c4c<].].].]'`8]L]8`1`1`1`1` D]Ua2_T]9_T]9_T]9_T]8]5]U`2] "
  3648. "=] &[ O].] E] E] '] S] R] ^ (](]/] "
  3649. " C] S] '] V] F^ 7]4](] %])[ 4]7] @])_Q_:] 9]6] 6[ S]0[R"
  3650. "^ H]%\\U\\ A\\ @\\ /Z <\\ ,[ M^5](^ "
  3651. " =] &[ N]0] D\\ D] '\\ Q^DZ 1] _ )"
  3652. "](]/] D^ S] '] V] F] 6]4](] %] ;]7] @] /] 9]6] 6[ S]0"
  3653. "g H]%\\U\\ @\\ @\\ J\\ X]4](] "
  3654. " <] &[ N]0] D\\ E^ '\\ P^G] 2] X^ )]"
  3655. "(^0] D] R] '] V] G^ 6]4](] %] ;]7] @] /] 9]6] 6[ S]0e"
  3656. " F]%\\U\\ ?[ ?[ I[ ^4])^ "
  3657. " @ZV] &[ M]2] D] E] '] O_K_ 3] V^ *b"
  3658. ",]5b E^ R] '] V] G^ 6^5])^ %] ;]7] @] /] 9]6] 6[ S].a"
  3659. " D]%\\U\\ ?\\ @\\ J\\ !^4])^ "
  3660. " B\\V] &[ M]2] D\\ G\\ L`P` 2] U^ +b "
  3661. "=b RZN^ R^ '] V] H^ 4^6]*^ $] ;]7] @] /] 9]6] 6[ S] "
  3662. " J] :\\ @\\ J\\ \"^3]*^ A\\V"
  3663. "\\ %[ L]4] Vm 2^ S^ ,b =b "
  3664. " R\\Q_ R] &] V] I^ 3b:].b $] ;]7] @] /] 9]6] 6[ S] "
  3665. " J] @ZU] FZU] PZU] #^2]+^ @b "
  3666. " %[ Si 4b %i Ua &]"
  3667. " V] Mb 2a:].a #] ;]7] @] /] 9]6] .] J] @b "
  3668. " Fb Pb 'b2] E` "
  3669. " Qb 1a $g S` %] V] Ma /_:]._ !] "
  3670. " ;]7] @] /] 9]6] .] J] @a Ea "
  3671. " Oa &a1] D^ "
  3672. " X^ Ip Fc Q^ #] V] M_ A] )] ;]7] @] /] 9]6] "
  3673. " T] @` D` N` %_/"
  3674. "] BZ Ap "
  3675. " 6] "
  3676. " "
  3677. " p 6] "
  3678. " "
  3679. " "
  3680. " F]']2] +]']2^ D]']3_ E]']1] \"]']2^ "
  3681. "8] H";
  3682. // Define a 90x103 font (huge size).
  3683. static const char *const _data_font90x103[] = {
  3684. // Defined as an array to avoid MS compiler limit about constant string (65Kb).
  3685. // First string:
  3686. " "
  3687. " "
  3688. " "
  3689. " "
  3690. " HX 4V >X IX *W FW "
  3691. " "
  3692. " "
  3693. " "
  3694. " "
  3695. " HX W 4Z 3VCT <Z >X W 4Z "
  3696. " HX W 4Z 'VCT ;X W 3Y 2UCT KX W 3Y 0W "
  3697. " "
  3698. " "
  3699. " "
  3700. " "
  3701. " @W !W 4\\ 5YET ?XHX 8] >W !W 4\\ 7XGX KW !W 4\\ 7XHX "
  3702. " +YET :W !W 3[ 5ZFT ?XGX EW !W 3[ 7XGX 5W "
  3703. " "
  3704. " "
  3705. " "
  3706. " "
  3707. " >W \"V 3\\ 7]HU ?XHX 9` ?W \"V 3\\ 7XGX JW \"V 3\\ 7XHX -]HU"
  3708. " 9W \"V 3] 7]HT ?XGX DW \"V 3] 8XGX 5V "
  3709. " "
  3710. " "
  3711. " "
  3712. " "
  3713. " <W $V 3VNV 8_KV ?XHX 9` >W $V 3VNV 8XGX IW $V 3VNV 8XHX -_KV"
  3714. " 8W $V 2] 7_KU ?XGX CW $V 2] 8XGX 6V "
  3715. " "
  3716. " "
  3717. " "
  3718. " "
  3719. " :W &W 4VLV :j >XHX :VJV >W &W 4VLV 9XGX HW &W 4VLV 9XHX .j 6"
  3720. "W &W 3VMV 9i >XGX BW &W 3VMV 9XGX 7W MW "
  3721. " "
  3722. " "
  3723. " "
  3724. " "
  3725. " CV 'W 4VJV ;j >XHX ;UGV >V 'W 4VJV :XGX GV 'W 4VJV :XHX .j"
  3726. " 5V 'W 3VKV :i >XGX AV 'W 3VKV :XGX 8W N[ "
  3727. " "
  3728. " "
  3729. " "
  3730. " "
  3731. " DV )W 4VHU <VK_ =XHX ;TEU =V )W 4VHU :XGX FV )W 4VHU :XHX "
  3732. " /VK_ 3V )W 3VIV <UK_ =XGX @V )W 3VIV ;XGX 9W N] "
  3733. " "
  3734. " "
  3735. " "
  3736. " "
  3737. " DV *V 3UFU =UH\\ <XHX <UDT <V *V 3UFU ;XGX EV *V 3U"
  3738. "FU ;XHX /UH\\ 1V *V 2UGU <TH] =XGX ?V *V 2UGU ;XGX 9V a "
  3739. " "
  3740. " "
  3741. " "
  3742. " "
  3743. " EV ,V 3UDU >TEY ;XHX <TBT <V ,V 3UDU <XGX D"
  3744. "V ,V 3UDU <XHX /TEY /V ,V 2UEU =TFZ <XGX >V ,V 2UEU <XGX :V Na "
  3745. " "
  3746. " "
  3747. " "
  3748. " "
  3749. " DU -V 3VDV ?TCV :XHX <TBT ;U -V 3VD"
  3750. "V =XGX CU -V 3VDV =XHX /TCV -U -V 2UCU >TCU :XGX =U -V 2UCU =XGX ;V NV"
  3751. "IV \"W "
  3752. " "
  3753. " "
  3754. " "
  3755. " JU /V 3VBV ETBT :U /"
  3756. "V 3VBV FU /V 3VBV (U /V 2UAU DU /V 2UAU @V NVGV "
  3757. " $X "
  3758. " "
  3759. " *X "
  3760. " "
  3761. " JX GTBT "
  3762. " MX GX 7V :UEU DX GX 7V JX GX 7W 4X GX 6V "
  3763. " GX GX 5V (X &X "
  3764. " "
  3765. " )X 8V "
  3766. " "
  3767. " ;X FTBT "
  3768. " LX IX 7X <UCU DX IX 7X JX IX 6W 3X IX 6X GX IX 5X "
  3769. " *X &Y "
  3770. " "
  3771. " (X 9V "
  3772. " <X "
  3773. " ETBT KX "
  3774. "KX 6X 1TBT BTAT CX KX 6Y JX KX 6Y (TBT BX KX 5X 1TBT LX KX 4X +X "
  3775. " %T #W 9W "
  3776. " "
  3777. "3a :a <W 2W >W E\\ AW ,W ,W ,W ,W HY GV +Y "
  3778. " 4Z NX @X "
  3779. " %W DUDU "
  3780. " =Y 7W KW 6Z 4XDT BTAT BW KW 6Z IW KW 6[ ,Y )XDT AW KW 5Z 4XDT "
  3781. " KW KW 4Z ,W BW 8V (S "
  3782. " <S 9V 7V "
  3783. " 3a :a ;W 3W >W H_ AW ,W ,W ,W ,W "
  3784. " L] GV +] ;a #[ F^ "
  3785. " 8XGX +W BTEU "
  3786. " *R 9a :W MW 6\\ 6ZET ?XHX <TAT AW MW 6\\ 7XGX LW MW "
  3787. "5[ 7XGX .Y +ZET @W MW 5\\ 6ZET ?XHX DW MW 4\\ 7XHX 0W AW &XHX MZ "
  3788. " +T $Y BS 1W,V MY 8W 7W T 9X 5Z /"
  3789. "[ 0Z 8Z /Y GY .\\ <\\ [ 4[ :\\ -a "
  3790. " :a :W 4W >W Ja AW ,W ,W ,W ,W N_ GV +_ "
  3791. "?e 8] J] Jb 8[ <[ $Y FY 7XGX "
  3792. "=Z Di 5W 8Z .Y !W FW *Y 4W)V*W)V-Y(V <UFU 3\\ "
  3793. " +[ 0[ 0[ 0[ 0[ 4[=T <e ;W W 5\\ 7\\FT ?XHX <TAT @W W 6^ 8XGX KW W"
  3794. " 5] 8XGX .Z@R ?\\FT ?W W 4\\ 7\\FT ?XHX CW W 3\\ 7XHX 1W @W &XHX N\\ "
  3795. " ,T :U :U5U ` EX 2VFV .S 4]0W\"b DV V 5V T 7W"
  3796. " .` 3[ 7c 8d )Z Dq 8b Hy Bb 7` Na /Z @k .d Kj ?x Mt 7f MX/X'X -X -X2Z&X -]0]0["
  3797. "3X Dc Ii -c Ij 4f N~W$X/X.X&X.X4Y4XDY/Y/Y,Y'~S%a >W $a MY EW 5W >W Kb AW ,W ,W"
  3798. " ,W ,W !a GV +a Ch =f ^ Mf 2Z @"
  3799. "x Mx <c 3X C~Q)X?X?X Kc 2T .V .T CX $a !W.W N` ;XGX ![ Lb &Z Mi 7[ >a"
  3800. " 5a &W 0g #\\ -_ <\\*V.\\*V0a-V\"X )Z /Z /Z /Z /Z 4WJV 1~U+d Kx Mx Mx Mx MX -X -X -X ,j"
  3801. " @[3X Dc 8c 8c 8c 8c <cBV.X/X'X/X'X/X'X/X/Y,Y$X &h ;W \"W 5VNV 8]HU ?XHX <TAT ?W \"W 5"
  3802. "VNV 8XGX JW \"W 5VMV 9XGX -ZDV @]HU >W \"W 4VNV 8]HU ?XHX BW \"W 3VNV 8XHX 2W ?W &XHX "
  3803. " ^ K~\\ >S 3Q +[ @[;[ ;Q ;e HX 2VFV #VBV FS 6`"
  3804. "1V#g GV !V 3V !T 7W 0d :` ;j ?k -[ Dq :g Ky Df ;d $f 1Z @o 5j Np Ex Mt "
  3805. ":m\"X/X'X -X -X3Z%X -]0]0\\4X Gi Lm 4i Ln ;m#~W$X/X-X(X-X4Y4XCY1Y-Y.Y&~S%a >W $a N[ EV "
  3806. "5W >W Lc AW ,W ,W ,W ,W \"b GV +a Dk Aj \"_"
  3807. " h 3Z @x Mx ?i 6X C~Q)X?X?X Ni 6V /V /V DX &f #W0W e >XGX %c#"
  3808. "e +b\"i 9_ Be 9d 'V 3k %^ /c @^*V0^*V2d.V\"X )Z /Z /Z /Z /Z 3b 1~U.j Nx Mx Mx"
  3809. " Mx MX -X -X -X ,p F\\4X Gi >i >i >i >i BiEV.X/X'X/X'X/X'X/X.Y.Y#X 'j ;V \"V 5VLV :_IT >XH"
  3810. "X <TAT >V \"V 5VLV 9XGX IV \"V 4VMV 9XGX ,ZHY A_IT <V \"V 4VLV :_IT >XHX AV \"V 3VLV 9"
  3811. "XHX 2V >W &XHX !_ K~[ >T 4R -_ D_?_ >S =t Fh "
  3812. " IX 2VFV #VBV FS 7c4V#i HV \"W 3V !T 7V 0f @e >o Co 0\\ Dq <j Ly Fj ?h (i "
  3813. "\\ ?Z @r :o\"s Hx Mt <q$X/X'X -X -X4Z$X -]0]0\\4X Im Np 9m Np ?q%~W$X/X-X(X,W5[6XAX1X+X.X%~S%"
  3814. "a =V $a ] EV 6W >W Md AW ,W ,W ,W ,W HW 1b GV +b "
  3815. " Fm Dm #` \"j 4Z @x Mx Am 8X C~Q)X?X?X!m 9X 0V 0X EX 'h"
  3816. " $W0W \"h ?XGX 'g%g 0h%i :a Cf :f *V 4m %^ 0e A^+V/^+V1f1V!X )Z /Z /Z /Z /"
  3817. "Z 2` 1~V0o\"x Mx Mx Mx MX -X -X -X ,t J\\4X Im Bm Bm Bm Bm FmHV-X/X'X/X'X/X'X/X-X.X\"X (l ;"
  3818. "V $V 4UJU :ULXLU >XHX <UCU =V $V 5VJV :XGX HV $V 4VKV :XGX +ZL\\ AULXLU ;V $V 3UJU :ULX"
  3819. "LU >XHX @V $V 2UJU 9XHX 3V =W &XHX !` K~Z >T 4S /a FaAa @T "
  3820. " @w Hl KX 2VFV $WCV ES 8e5V$j HV \"V 1V \"T 7V 2j Eh ?q Dp 1\\ Dq >"
  3821. "l Ly Hn Bj +l %e E\\ At >s$v Kx Mt >u&X/X'X -X -X5Z#X -^2^0]5X Jo q ;o r Br%~W$X/X"
  3822. "-X(X,X6[6XAY3Y+Y0Y%~S%W 3V IW !_ FW 7W >W Md AW ,W ,W ,W ,W HW "
  3823. " 2[ ?V #[ Hn En #` #l 6\\ Ax Mx Cp 9X C~Q)X?X?X\"o "
  3824. " ;Z 1V 1Z FX KS 0i #W2W LV ,i ?XGX *l'h 3l'i ;c Dg ;g ,W 6o %^ 1g B"
  3825. "^,V.^,V0g3V X *\\ 1\\ 1\\ 1\\ 1\\ 2^ 0~V2s$x Mx Mx Mx MX -X -X -X ,v L]5X Jo Do Do Do Do HpKW"
  3826. "-X/X'X/X'X/X'X/X-Y0Y\"X )n <W &W 5VJV ;TI_ >XHX ;UEU <W &W 5VIV ;XGX HW &W 5VIV ;XGX *g"
  3827. " ?TI_ ;W &W 4VJV ;TI_ >XHX @W &W 3VJV :XHX 4W =W &XHX 1\\ 1\\ 1\\ 1\\ 1\\ =XMV K~Y "
  3828. " =S 4U 1c IdCc AU Dz In LX 2VFV $VBV ES 9g7V$k HV #W 1W #T "
  3829. " 8W 3l Fh ?r Eq 3] Dq ?m Ly Ip Em -n )k H\\ Au Av%x Mx Mt ?x(X/X'X -X -X6Z\"X -"
  3830. "^2^0]5X Ls\"s ?s\"s Et%~W$X/X,X*X+X6[6X@Y5Y)Y2Y$~S%W 3W JW \"a FW 8W >W NZ 6W ,W "
  3831. ",W ,W ,W HW 2X <V X H[G[ Go KZ %"
  3832. "[H[ 7\\ Ax Mx Ds ;X C~Q)X?X?X$s >\\ 2V 2\\ GX KS 1j #W2W LV -j ?XGX +ZEZ)VGY "
  3833. "5ZDZ)i <e EUFY <UFX -W 7q %VMU 2YIY CVMU,V.VMU,V0UFX3V X *\\ 1\\ 1\\ 1\\ 1\\ 1\\ 0~W4v%"
  3834. "x Mx Mx Mx MX -X -X -X ,x N]5X Ls Hs Hs Hs Hs LsMW,X/X'X/X'X/X'X/X,Y2Y!X *\\G[ <W (W 4UHU"
  3835. " <UH] =XHX ;VGV ;W (W 5VHV ;XGX GW (W 4UGU ;XGX )c =UH] 9W (W 3UHU <UH] =XHX ?W (W"
  3836. " 2UHU :XHX 5W <W &XHX 5c 8c 8c 8c 8c @WKU J~X >T 5V 2e KfEe CW G| "
  3837. " Jp MX 2VFV $VBV ES 9XIX8V$l HV #V /V #T 8V 3n Gh ?s Fr 5^ Dq @n Lx Ir"
  3838. " Go .o -q L^ Bv Cx&z x Mt A{)X/X'X -X -X7Z!X -^2^0^6X Mu#t Au#t Gu%~W$X/X,X*X+X6["
  3839. "6X?X5X'X2X#~S%W 2V JW #c FW 9W >W NX 4W ,W ,W ,W ,W HW "
  3840. " 2W ;V NW IZCY Hp JY &ZDZ 9^ Bx Mx Eu <X C~Q)X?X?X%u @"
  3841. "^ 3V 3^ HX KS 2k \"W4W KV -ZGW ?XGX -X=X+R@W 8X<X .XIX FQ@W <Q@W /W 7dGU"
  3842. " %QHU 3XEX DQHU-V-QHU-V/Q@W5V NX +^ 3^ 3^ 3^ 3^ 2\\ 0~W5x&x Mx Mx Mx MX -X -X -X ,z!^6"
  3843. "X Mu Ju Ju Ju Ju N}+X/X'X/X'X/X'X/X+X2X X +ZBY ;W *W 4UFU =TF\\ =XHX :VIV 9W *W 5VFV "
  3844. "<XGX FW *W 4VGV <XGX (_ :TF\\ 8W *W 3UFU =TF\\ =XHX >W *W 2UFU ;XHX 6W ;W &XHX 7h =h"
  3845. " =h =h =h DWJV K~X >T 5W 4g MgFg EY J~ K]FZ MX 2VFV $VBV "
  3846. "ES :XGX9V%\\GX HV $W /W 3PATAP GV 3[H[ Gh ?]F] GZE^ 6^ Dq A]FX Lx I\\F\\ G\\G[ "
  3847. " /[H] 0u N^ Bw E_D^&{!x Mt B`C_)X/X'X -X -X8Z X -_4_0_7X N^E^$u C^E^$u H^E\\%~W$X/X,Y,Y*W7"
  3848. "]8X>Y7Y'Y4Y#~S%W 2V JW $e FV 9W >W NW 3W ,W ,W ,W ,W HW "
  3849. " 2W ;V NW IY@X >X 4[AV IX &X@X 9^ Bx Mx F^E^ =X C~Q)X?X?X"
  3850. "&^E^ B` 4V 4` IX KS 3\\GW \"W4W KV .YBT ?XGX .V7V,P=W :W8W /VEV 3V +V /V "
  3851. " 7eGU KU 3WCW ;U-V$U-V LV5V NX +^ 3^ 3^ 3^ 3^ 3^ 1~W6_D^&x Mx Mx Mx MX -X -X -X ,{\""
  3852. "_7X N^E^ L^E^ L^E^ L^E^ L^E^ !^Ed*X/X'X/X'X/X'X/X+Y4Y X +Y?X ;V *V 4UDU >TEZ <XHX 9a "
  3853. "7V *V 4UDV =XGX EV *V 4VEV =XGX )] 7TEZ 6V *V 3UDU >TEZ <XHX =V *V 2UDU <XHX 6V :W &XH"
  3854. "X 9k @k @k @k @k EWJV K~W >T 5Y 5g MhHi G[ M~Q L\\AW M"
  3855. "X 2VFV $VCV DS :WEW:V%ZAU HV $V -V 3RCTCR HW 4ZDZ H\\LX ?Y?[ HV>\\ 8_ DX )[?T -Y J[B"
  3856. "[ I[CZ 0WAZ 2x ^ BX>^ G]=Z&X=b#X -X '];[)X/X'X -X -X:[ NX -_4_0_7X \\?\\%X@^ E\\?\\%X"
  3857. "?] J[=X =X <X/X+X,X)X8]8X=Y9Y%Y6Y )Y$W 2W KW %ZMZ FV :W >W X 3W 4W ,W "
  3858. " HW 3X ;V NX KY?X Ca 9Y:R HX (X>X :VNV "
  3859. "BZ /X '\\?\\ A^ FX0X)X?X?X'\\?\\ Db 5V 5b JX KS 3ZBT !W6W JV .X?R 4V4U HV ;V"
  3860. "4V 1VCV 4V *U 0V 7fGU KU 4WAW <U.V#U.V JU6V MX +^ 3^ 3^ 3^ 3^ 3^ 2XIX F]=Z&X -X"
  3861. " -X -X -X -X -X -X ,X=b$_7X \\?\\ N\\?\\ N\\?\\ N\\?\\ N\\?\\ #\\?`)X/X'X/X'X/X'X/X*Y6Y NX ,Y=W :V ,"
  3862. "V 3UDU >TDX ;a 6V ,V 4UBU GV ,V 3UCU 0` 6TDX 4V ,V 2UDU >TDX >V ,V 1UDU "
  3863. ":V 9W (o Do Do Do Do GWIU J~V >T 6Z 6i jIj I\\ N~R M[="
  3864. "U MX 2VFV %VBV H] AWCW;V%Y=R HV %W -V 4UETEU IV 4ZBZ IWGX ?V;[ IS9Z 9VNX DX *Z;R"
  3865. " -X JZ>Y JZ?Y 1U>Z 5`C_#` CX;[ H[7W&X9_$X -X (\\6X)X/X'X -X -X;[ MX -_4_0`8X![;[&X"
  3866. "=[ F[;[&X<[ LZ8U =X <X/X+X,X)X8]8X<X9X#X6X )Z$W 1V KW &ZKZ FV ;W >W W 2W 4"
  3867. "W ,W HW 3W :V MW KX=W Cc ;X7P HX ("
  3868. "W<W ;WNW BY /X ([;[ Gg JX0X)X?X?X([;[ Fd 6V 6d KX KS 4Y>R !X8X JV /X<P 6V1U IV"
  3869. " <U0U 2UAU 3U *U 1V 6fGU KU 4V?V <U/V\"U/V IU7V LX ,` 5` 5` 5` 5` 5` 3XIX "
  3870. "G[7W&X -X -X -X -X -X -X -X ,X9_%`8X![;[![;[![;[![;[![;[ %[;](X/X'X/X'X/X'X/X)X6X MX ,X;W"
  3871. " :V .V 3UBU ?TBT 7] 3V .V 4VAU GV .V 3UAU 4d 7TBT 1V .V 2UBU ?TBT ;V .V 1U"
  3872. "BU <V 8W )r Gr Gr Gr Gr IVHR GX+W =S 5[ 7i!kJk I] !^ "
  3873. " )Y:T MX 2VFV %VBV Le EVAV<V$X:P HV %W -W 6WFTFV IV 4X?Y IRBX ?T7Y IP5Z :VNX DX "
  3874. "+Z8P .Y JY<Y KY=X 1S;Y 6];\\$WNW CX9Z J[4U&X6]%X -X )[2V)X/X'X -X -X<[ LX -XNV6VNX"
  3875. "0`8X\"Z7Z'X;Z HZ7Z'X;Z LY4R =X <X/X*X.X(X8]8X<Y;Y#Y8Y *Z#W 1V KW 'ZIZ FV <W >W W "
  3876. " 2W 4W ,W HW 3W :V MW KW<X Dd <W -W "
  3877. " )W;X <WNW AY 0X )Z7Z Jl MX0X)X?X?X)Z7Z Hf 7V 7f LX KS 4X;P W8W IV /W \""
  3878. "V.U JV >U.U 4VAV &V 5U *U 2V 6gGU KU 5W?W =U/V\"U/V IU7V LX ,WNW 5WNW 5WNW 5"
  3879. "WNW 5WNW 5WNW 4XHX H[4U&X -X -X -X -X -X -X -X ,X6]&`8X\"Z7Z#Z7Z#Z7Z#Z7Z#Z7Z 'Z8['X/X'X/X'"
  3880. "X/X'X/X)Y8Y MX ,W:W 9V 0V 3U@U ?[ 1V 0V 3U@V GV 0V 3U?U 8h 1V 0V 2U@U "
  3881. " CV 0V 1U@U >V 7W *`L` I`L` I`L` I`L` I`L` JV =X,X >T 6] 9k\"lKl K_ "
  3882. " #\\ 'Y8S MX 2VFV %VBV Nk IVAV=V$X 1V %V +V 6YHTHY -V EW 5Y>Y :X ?R5"
  3883. "Z .Y ;VMX DX +Y DX IY<Y LY;X 2Q8Y 8[5[&WNW CX8Y KZ1T&X4\\&X -X *Z.T)X/X'X -X -X=["
  3884. " KX -XNV6VNX0a9X#Z5Z(X:Y IZ5Z(X:Z NY1P =X <X/X*X.X'W9WNV:X:Y=Y!Y:Y *Z\"W 1W LW (ZGZ -"
  3885. "W >W W 2W 4W ,W HW 3W :V MW KW;W De =W "
  3886. " -X *W:W <VLV @Y 1X *Z5Z Mp X0X)X?X?X*Z5Z Jh 8V 8h MX KS 5Y :X:X"
  3887. " IV /W #U+T JV ?U+T 5U?U &V 5U +V AgGU KU 5V=V =U0V!U0V IV8V KX ,WNW 5W"
  3888. "NW 5WNW 5WNW 5WNW 5WNW 4XHX IZ1T&X -X -X -X -X -X -X -X ,X4\\'a9X#Z5Z%Z5Z%Z5Z%Z5Z%Z5Z )Z5Z"
  3889. "(X/X'X/X'X/X'X/X(Y:Y LX -X:W !W 2\\LZ "
  3890. "EW +[@[ K[@[ K[@[ K[@[ K[@[ KV <X-X /P 0T 7^ 9k\"lLm La %Z "
  3891. " %Z6Q MX 2VFV %VCV n KWAW>V$X 1V &W +W 5XITIX +V EV 4X<X :X ?P2Y -X <WMX DX ,Y C"
  3892. "X JY:Y MX9W 2P7Y :Z0Z(WLW DX7X KY.R&X2Z&X -X *Y+R)X/X'X -X -X>[ JX -XNW8WNX0a9X#Y"
  3893. "3Y(X9Y JY3Y(X9Y NX LX <X/X*X.X'X:VMV:X9X=X NX:X *Z!W 0V LW )ZEZ .W >W W 2W "
  3894. " 4W ,W HW 3W :V MW LX;W Df >W ,W "
  3895. " +W8W >WLW @Y 2X +Z3Z!t\"X0X)X?X?X*Y3Y Kj 9V 9j AS 5X 8W:W HV /W #T)T KV "
  3896. " @T(T 6U?U &V 5T +V AhGU KU 5V=V =U0V!U0V JV7V WLW 7WLW 7WLW 7WLW 7WLW 7XNX "
  3897. "6XGX IY.R&X -X -X -X -X -X -X -X ,X2Z'a9X#Y3Y%Y3Y%Y3Y%Y3Y%Y3Y )Y3Z)X/X'X/X'X/X'X/X'X:X Ki"
  3898. " >W8V *XHZ FW ,Z<Z MZ<Z MZ<Z "
  3899. "MZ<Z MZ<Z LV <X.X .R 2S 7` :k#nMm Mb &Z $Y4P MX 2VFV &VBV!o "
  3900. "KV?V?V#W 0V &V )V 3XKTKX )V EV 5X:X ;X X -Y =VLX DX -Y CY JY:Y NY9X HX ;"
  3901. "Z-Y)WLW DX7Y MY,Q&X1Z'X -X +Y)Q)X/X'X -X -X?[ IX -XMV8VMX0XNX:X$Y1Y)X9Y KY1Y)X8X NX LX <X"
  3902. "/X)X0X&X:VMV:X9Y?Y NY<Y *Y W 0V LW *ZCZ /W >W W 2W 4W ,W HW"
  3903. " 3W :V MW LW:W Dg ?W ,X ,W8W >WLW ?Y 3X +Y1Y\"v#X"
  3904. "0X)X?X?X+Y1Y MYNVNY :V :YNVNY BS 5X 8X<X HV /W $T?ZBT*c AT&T 7U?U &V "
  3905. "6U -W @hGU KU 6V;V >U1V U1V KW7V NWLW 7WLW 7WLW 7WLW 7WLW 7WLW 6XGX JY,Q&X -X "
  3906. "-X -X -X -X -X -X ,X1Z(XNX:X$Y1Y'Y1Y'Y1Y'Y1Y'Y1Y P)P$Y3[)X/X'X/X'X/X'X/X'Y<Y Km BW8W "
  3907. " +UDZ 7P 1W -Y8Y Y8Y Y8Y Y8Y Y8Y MV ;"
  3908. "W.X /T 4T 7a ;k#nMn Nc 6P :W4W ?Z ?X6X KY #Y 0X 2VFV &VBV\"p KV?V?V#"
  3909. "W 0V 'W )W 2XMTMX 'V FW 5X:X ;X Y -X >VKX DX -X BX IX8X NX7W KP 1P =X <Y)X+"
  3910. "XLX EX6X NY*P&X0Z(X -X ,Y'P)X/X'X -X -X@Z GX -XMV8VMX0XNX:X%Y/Y*X8X LY/Y*X8Y!X KX <X/X)X0"
  3911. "X&X:VMV:X8YAY LY>Y *Z W 0W MW +ZAZ 0W >W W 2W 4W ,W HW "
  3912. " 3W :V MW LW:W DSF[ @X -X -X8W ?WJW ?Y 4X ,Y/Y%z%X0X)"
  3913. "X?X?X,Y/Y YMVMY ;V ;YMVMY CS 5X 5P*Q JW<W GV /W %TBbET/g BTGb?T 8U?U &V"
  3914. " 7U 5_ ?hGU KU 6V;V >U2V NU2V$_7V NXLX 9XLX 9XLX 9XLX 9XLX 8WLW 6XGX KY*P&X -X"
  3915. " -X -X -X -X -X -X ,X0Z)XNX:X%Y/Y)Y/Y)Y/Y)Y/Y)Y/Y\"R+R&Y3]*X/X'X/X'X/X'X/X&Y>Y Jp EW:Y "
  3916. " +R@Y 7Q 2W .XEVFY\"X5Y\"X5Y\"X5Y\"X5Y N"
  3917. "V ;X/X 0V 5T 8c <k#nNo e >^ AW4W ?Z >W6W KY \"Y 0X 2VFV &VCW#[LSKZ K"
  3918. "V?V@V\"W 0V 'W )W 1XNTNX &V FW 6Y:Y <X NX -X ?WKX DX .Y CY IX8X NX7W NS 1S @"
  3919. "X =X&X,WJW EX6X NY /X/Y(X -X ,Y /X/X'X -X -XAZ FX -XMW:WMX0XMX;X%Y/Y*X8Y MY/Y*X8Y!X KX <X"
  3920. "/X)Y1X%W;WMW;W6XAX JX>X *Z NW 0W MW ,Z?Z 1W >W W 2W 4W ,W H"
  3921. "W 3W :V MW LW:W DPAY ?Y .W -W6W @WJW >Y 5X ,X-X&"
  3922. "_MXM_&X0X)X?X?X,Y/Y !YLVLY <V <YLVLY DS 6Y 6R,R JX>W FV /X 'TCfFT2i CUGfB"
  3923. "T 9U?U &V 7U 5] >iGU KU 6V;V >U2V NU2V$]5V NWJW 9WJW 9WJW 9WJW 9WJW 9WJW 8XFX"
  3924. " KY /X -X -X -X -X -X -X -X ,X/Y)XMX;X%Y/Y)Y/Y)Y/Y)Y/Y)Y/Y#T-T'Y3]*X/X'X/X'X/X'X/X%X>X Ir "
  3925. "GW=\\ GY 9S 3W /XDVDX$X2X$X2X$X"
  3926. "2X$X2X V ;X0X 0X 7T 8d <k#~`!g Bd DW4W ?[ ?X7W LY !X /X 2VFV &VCV#Z"
  3927. "JSGV KV?VAV!W 0V 'V 'V /d $V FV 5X8X <X NX -X ?VJX DX .X BX HX8X Y7X #V 1V C"
  3928. "X >X$X-WJW EX6X Y .X.Y)X -X -Y .X/X'X -X -XBZ EX -XLV:VLX0XMX;X&Y-Y+X7X NY-Y+X7X!X KX <X/"
  3929. "X(X2X$X<VKV<X6YCY JY@Y +Z MW /V MW -Y;Y \"Z ;WDX 0Z 2XDW >Z <W !X :WDY IW ,W HX8X "
  3930. "MY 3Z *X 3X &X 7] <W 3W :V MW ;X :W:W 4Y @[ )\\ (Y 6X 8QEV :[ "
  3931. " JW6W @VIW =Y 6X -Y-Y(]JXJ]'X0X)X?X?X-Y-Y #YKVKY =V =YKVKY IZ 9X 6T.T JW>W FV "
  3932. ".X (TDgFT3j CTFhDT 9U?U &V 8U 4\\ =iGU KU 6V;V >U3V MU3V#\\5V MWJW 9WJW"
  3933. " 9WJW 9WJW 9WJW 9WJW 8XFX LY .X -X -X -X -X -X -X -X ,X.Y*XMX;X&Y-Y+Y-Y+Y-Y+Y-Y+Y-Y%V/V)Y3"
  3934. "_+X/X'X/X'X/X'X/X%Y@Y Is HW?^ ?Z /Z /Z /Z /Z /Z /Z6Y NZ 0Z /Z /Z /Z 8Y 1Y 3Z /Z /Z"
  3935. " /Z /Z 3ZCV 5WDX DXCVCW%X0W%X0W%X0W%X0W V :X1X 0X 7T 9f =k#~`\"h Cf "
  3936. "EW4W @\\ ?X8X LX !Y /X 2VFV 'VBV#XHSET KV?VAV!W 0V (W 'W .` \"V GW 5X"
  3937. "8X <X NX -X @VIX DX .X BX HX8X X5W &Y 1Y FX >W\"W.XJX FX6X X -X.Y)X -X -X -X/X'X -X"
  3938. " -XCZ DX -XLV:VLX0XLX<X&X+X+X7X NX+X+X7X!X KX <X/X(X2X$X<VKV<X5YEY HYBY +Z LW /W NW .Y9Y"
  3939. " 'b ?WG^ 7b 9^GW A` Gl 2_GW MWG_ DW ,W ,W8Y MW ,WG^>^4WG_ 9` @WG^ 9^GW MWG\\ ;f Gm <W6W#"
  3940. "X2X#W;X;W5Y7Y#W1X\"u 6W :V MW >^BV\"W:W 3X ?^ 0e AWG_ KV.X ?X <W6W HTG[ K}!WCWCW Ca"
  3941. " 7p&{ NW6W AWHW >Z 7X -X+X)\\HXH\\(X0X)X?X?X-X+X $YJVJY >V >YJVJY Ma =X 7V0V JW@W E"
  3942. "V .Y *TEiET5k DTEiDT :VAV &V 9U 3_ ;W6W NiGU KU 6V;V >U3V MU3V#_8V NXJX"
  3943. " ;XJX ;XJX ;XJX ;XJX ;XJX :XEX LX -X -X -X -X -X -X -X -X ,X.Y*XLX<X&X+X+X+X+X+X+X+X+X+X&X"
  3944. "1X*X3`+X/X'X/X'X/X'X/X$YBY Ht IW@_ Cb 7b 7b 7b 7b 7b 7b>a'b 7` 5` 5` 5` AW ,W ,W ,W DY EW"
  3945. "G_ 9` 5` 5` 5` 5` (Z <`GV W6W MW6W MW6W MW6W#W1X NWG^ HW1X NWBVBW&W.W&WJP:PJW&W4PJW&W."
  3946. "W!V :X2X 0X 6S 8g >k#~`#j Fj GW4W @\\ >W8W LX X .X 2VFV 'VBV$XGSCR "
  3947. "KV?VBV X 1V (W 'W ,\\ V GW 5X8X <X NX -X AWIX DX /X BY HX8X X5W ([ 1[ HX ?W "
  3948. "W/WHW FX6X!Y -X-Y*X -X .Y -X/X'X -X -XDZ CX -XLW<WLX0XKW<X'Y+X+X7X Y+X+X7X!X KX <X/X'X4X#"
  3949. "X<VKV<X4XFY FXBX *Y KW /W NW /Y7Y +g AWIb ;f =bIW De Il 3bIW MWIc FW ,W ,W9Y LW ,WIbBb"
  3950. "6WIc >f CWIb =bIW MWI^ =j Im <W6W\"W2W\"W<Z<W4X7X!W2W!u 6W :V MW @bEW\"W:W 2X @c 8j CW"
  3951. "Ic MX0W =W <W6W IW/W\"VI^ L}!WCWCW Ee =t&{ W4W BWHW =Y 7X .X*Y*ZFXFZ(X0X)X?X?X.Y+X #WIVIW "
  3952. " =V =WIVIW f ?X 8X2X KW@W EV .Z +SE[GVDS6ZDV DSDVDXDS 9UAU %V :U 2` <W6W"
  3953. " NiGU KU 6V;V >U4V LU4V\"`:V GX /WHW ;WHW ;WHW ;WHW ;WHW ;WHW :XEX MY -X -X -X -X -X "
  3954. "-X -X -X ,X-Y+XKW<X'Y+X,Y+X,Y+X,Y+X,Y+X'Z3Z,Y4WNY,X/X'X/X'X/X'X/X#XBX Gu JWB\\ Ag <g <g <g "
  3955. "<g <g <gBe+f <e :e :e :e CW ,W ,W ,W Mc FWIc >f ;f ;f ;f ;f +Z >eJU NW6W MW6W MW6W MW6W\"W"
  3956. "2W MWIb IW2W NWAVAW(W,W(WJR<RJW(W4RJW(W,W\"V 9W2X 1X 6T 9i ?k#~`#k Hl HW4W @] ?X9"
  3957. "W LW NX .X 2VFV 'VCW$WFSAP KV?VBV NW 1V (V &W *X MV GV 5X6X =X N"
  3958. "X -X AVHX DX /X BX GX8X X5X ,^ 1^ LX ?W MW0WHW FX6X!X ,X-Y*X -X .X ,X/X'X -X -XEZ B"
  3959. "X -XKV<VKX0XKX=X'Y+Y,X7X Y+Y,X7X!X KX <X/X'X4X\"W=WKV<W3YGY FYDY +Z KW .V NW 0Y5Y /l C"
  3960. "WJe ?j AeJW Eh Kl 5eJW MWJe GW ,W ,W:Y KW ,WJdDd7WJe @h DWJe AeJW MWJ_ ?l Im <W6W\"W2W!W=Z="
  3961. "W2X9X W2W!u 6W :V MW BeFV!W;X 1W ?f =k CWJe NY2X =X =W6W JW-W$WI` N}!WCWCW Gi Av&{ "
  3962. "W4W BVGW <Y 8X .X)X+ZEXEZ)X0X)X?X?X.Y+Y #UHVHU <V <UHVHU !j AX 9Z4Z KWBW DV -Z -"
  3963. "TFY@RDT8XAV ETDVBWET :VCV %V ;V )X =W6W NiGU KU 6V;V >U5V KU5V GX<V FX /WHW"
  3964. " ;WHW ;WHW ;WHW ;WHW ;WHW :WDX MX ,X -X -X -X -X -X -X -X ,X-Y+XKX=X'Y+Y-Y+Y-Y+Y-Y+Y-Y+Y'Z"
  3965. "5Z+Y5WMY,X/X'X/X'X/X'X/X#YDY GX@^ KWCZ Al Al Al Al Al Al AlFh.j ?h =h =h =h EW ,W ,W ,W !g"
  3966. " GWJe @h =h =h =h =h ,Z @hLV NW6W MW6W MW6W MW6W\"W2W MWJe KW2W W@VAW)W+W)WJT>TKW)W4TKW"
  3967. ")W+W\"V 9X3X 2X 5T :k ?i\"~`$m Jn IW4W A^ ?X:X MW NY .X 2VFV 7~X2XFS"
  3968. " <V?VCV MX 2V )W %W +X MV GV 5X6X =X NX -X BVGX DX /X BX GX8X X5X LX -X 7a 1a "
  3969. "X @W KW2XHX GX6X!X ,X,X*X -X .X ,X/X'X -X -XFZ AX -XKV<VKX0XJW=X'X)X,X7X X)X,X7X!X KX <X/"
  3970. "X'X4X\"X>VIV>X2YIY DYFY +Z JW .V NW 1Y3Y 1n DWLh Bm ChLW Gk Ll 6hLW MWKg HW ,W ,W;Y JW "
  3971. ",WKfGg8WKg Cl FWLh ChLW MWK` @m Im <W6W\"X4X!W=Z=W1X;X NW3X!u 6W :V MW CgGV!W;W 0X ?"
  3972. "g Am CWKg [4X >Y =W6W JW-W&YJb }!WCWCW Hk Dx&{ W4W CWFW <Y 9X /Y)X,ZDXDZ*X0X)X?X?X.X)X P #"
  3973. "SGVGS %P 7V 9P0P CSGVGS !l BX 8ZGWFZ JWCX DV ,Z .SEW<PCS8V?V .P>P JSCVAVDS :WEV "
  3974. "$V <V &W >W6W NiGU KU 6V;V BP>P /U5V KU5V EW=V FX 0XHX =XHX =XHX =XHX =XHX =XHX <XDX"
  3975. " MX ,X -X -X -X -X -X -X -X ,X,X+XJW=X'X)X-X)X-X)X-X)X-X)X&Z7Z*X5WKX,X/X'X/X'X/X'X/X\"YFY F"
  3976. "X=[ KWDY @n Cn Cn Cn Cn Cn CnHj1m Bk @k @k @k FW ,W ,W ,W $j GWKg Cl Al Al Al Al .Z Bs MW6"
  3977. "W MW6W MW6W MW6W\"W3X MWLh LW3X V?V@W*V)W*VJV@VKW*V4VKW*V)W#V 9X4X 2X 4S :l ?i\"~`"
  3978. "%o Lp JW4W A^ >W:X MW NX -X 2VFV 7~X2WES <V?VDV LX 2V )W %W -\\ V "
  3979. "HW 5X6X =X NX .X BWGX DX 0X BY FX:X NX5X LX -X :d 1d $Y @V IV2WFW GX6X\"Y ,X,Y+X -X /Y "
  3980. ",X/X'X -X -XH[ @X -XKW>WKX0XJX>X(Y)X,X7X!Y)X,X7X!Y LX <X/X&X6X!X>VIV>X1YKY BXFX +Z IW .W "
  3981. " W 2Y1Y 2o EWMj Dn DjMW Hn Nl 7jMW MWLi IW ,W ,W<Y IW ,WLhIi9WLi En GWMj EjMW MWLa An I"
  3982. "m <W6W!W4W W=Z=W1Y=Y MW4W u 6W :V MW DiIV W;W /W =g Cm CWLi![4W =Y =W6W KW+W(ZKd!}!"
  3983. "WCWCW Im Fy&{ W4W CWFW ;Y :X /X'X-YCXCY*X0X)X?X?X/Y)X!R #QFVFQ $R 9V :R1R DQFVFQ \"n BX "
  3984. "7ZJ\\JZ HWDW CV +[ 1TFW.T:W?V /Q?Q KTCVAWET :XIX $V =V #U >W6W NiGU KU 6V;V BQ"
  3985. "?Q 0U6V JU6V BU>V EX 0WFW =WFW =WFW =WFW =WFW =WFW <XDX NY ,X -X -X -X -X -X -X -X ,X,Y,XJ"
  3986. "X>X(Y)X.Y)X.Y)X.Y)X.Y)X%Z9Z*Y6WJX,X/X'X/X'X/X'X/X!XFX EX;Z LWDX ?o Do Do Do Do Do DoKn4n C"
  3987. "n Cn Cn Cn HW ,W ,W ,W %l HWLi En Cn Cn Cn Cn /Z Cs LW6W MW6W MW6W MW6W!W4W LWMj LW4W "
  3988. "W?V?V+W(V+WKXBXKV+W5XKV+W(V$W 8W4X 2X 5T ;n ?g!~_%p LZDZ JW4W A^ >W:W MW "
  3989. " MX -X 2VFV 7~X2WES <WAWDV KX 3V )W %W /` \"V HV 4X6X =X Y .X BVFX DX 0X BX E"
  3990. "X:X NX5X LX -X <e /e 'Y @V GV4XFX HX7X!X +X+X+X -X /X +X/X'X -X -XI[ ?X -XJV>VJX0XIW>X(X"
  3991. "'X-X7X!X'X-X7X!Y LX <X/X&X6X!X>VIV>X1YKY AXHX +Z HW -V W 3Y/Y 3p FWMk Fo EkMW Io Nl 8"
  3992. "kMW MWMk JW ,W ,W=Y HW ,WMjJj:WMk Gp HWMk GkMW MWMb Bo Im <W6W!W4W W>\\>W0X=X LW5X u 6W :V "
  3993. " MW EkJV W<X /W >j Fn CWMk\"\\6X =Z >W6W KW+W)[Ke\"}!WCWCW Jo Hz&{ W4W DWDW ;Y ;X /X'X."
  3994. "YBXBY+X0X)X?X?X/X'X#T HV IT :V ;T3T :V CV +o BX 6ZM`MZ GXFX CV *\\ 3SFW,S:V>V 0R@R "
  3995. " KSBV@VDS 9e #V ?W \"V ?W6W NiGU KU 6V;V BR@R 1U6V JU6V BV?V EX 1XFX ?XFX ?XFX ?XFX"
  3996. " ?XFX ?XFW =XCX NX +X -X -X -X -X -X -X -X ,X+X,XIW>X(X'X/X'X/X'X/X'X/X'X%Z;Z)X5VHX-X/X'X/"
  3997. "X'X/X'X/X XHX DX:Y LWEX >p Ep Ep Ep Ep Ep EpMp6o Do Do Do Do HW ,W ,W ,W 'o IWMk Gp Ep Ep "
  3998. "Ep Ep 0Z Ds KW6W MW6W MW6W MW6W!W5X LWMk MW5X V>V?W,V'W,VKZDYKW,V5YKW,V'W%W 8X5W 2"
  3999. "X 4T ;o @g ~^%q NY@Y KW4W B` ?X<X MV LX -X 2VFV 7~X2WES ;VAVDV JY 4V )"
  4000. "V $W 1d $V HV 4X6X =X X .Y CWFX DXLY =XEX 'Y EY<X MX5X LX -X ?e )e +Y ?V:X6V4WDW "
  4001. "HX7X!X +X+X+X -X /X +X/X'X -X -XJ[ >X -XJW@WJX0XIX?X(X'X-X7X!X'X-X8Y Y MX <X/X%W6X W?WIV>"
  4002. "W/YMY @YJY +Y GW -V W 4X+X 4YE\\ FWNXG\\ H]EX F\\GXNW J\\F[ GW ,\\GXNW MWNXG[ JW ,W ,W?Z GW"
  4003. " ,WNXH[KXH[:WNXG[ H]H] IWNXG\\ I\\GXNW MWNXFQ C\\CW CW ,W6W!X6X NW?\\?W.X?X JW6W 1X 6W :V MW "
  4004. " 9X=X\"[IZKW W=Y /W @m H]DV CWNXG[\"\\6W =[ >W6W LW)W*ZJWKY\"}!WCWCW K\\H] J{&{ V3W DWDW :Y "
  4005. "<X /X'X.XAXAX+X0X)X?X?X/X'X$V IV JV ;V <V5V ;V CV ,^MSKW BX 5x EWFW BV ,_ 5TFW,S:V?W"
  4006. " 1SAS LTBV@VDS 9d \"V @W U ?W6W NiGU KU 5V=V ASAS 2U7V IU7V @U@V DX 1WDW ?WDW ?"
  4007. "WDW ?WDW ?WDW ?XFX >XCX NX +X -X -X -X -X -X -X -X ,X+X,XIX?X(X'X/X'X/X'X/X'X/X'X$Z=Z(X6WH"
  4008. "X-X/X'X/X'X/X'X/X YJY DX9Y MWEW =YE\\ EYE\\ EYE\\ EYE\\ EYE\\ EYE\\ EYE]N\\G[7]EX E\\F[ F\\F[ F\\F[ "
  4009. "F\\F[ IW ,W ,W ,W (p IWNXG[ H]H] G]H] G]H] G]H] G]H] 1Z E]H^ JW6W MW6W MW6W MW6W W6W KWNXG\\"
  4010. " MW6W NV>V>V,V&V,VJZFYIV,V6YIV,V&V%W 7W6X 3X LR:T ;q @e N~^&s!Y>Y LW4W B` >W<X N"
  4011. "W $x FX 2VFV 7~X2WES ;VAVEW IY 5V *W #W 4XNTNX &V IW 5X5X =X X .X "
  4012. "CWEX Di AXH_ +X CX<X MX5X LX -X Be #e /Z @V<^IUDV5WDW HX8Y!X +X+X+X -X /X +X/X'X -X -XK["
  4013. " =X -XIV@VIX0XHW?X(X'X-X7X!X'X-X8X NZ NX <X/X%X8X NX@VGV@X.c >XJX +Z GW -W !W 5X)X 5U>"
  4014. "Z G_CZ I[>T FZC_ KZAZ HW -ZB_ M^BZ KW ,W ,W@Z FW ,^CZMVCZ;^BZ IZBZ I_CZ IZC_ M^ 5Y<S CW ,W"
  4015. "6W W6W MW?\\?W.YAY JW6W 2Y 6W :V MW ;\\A\\%YDYLV NW>Y .W AXJa IZ<Q C^BZ MX8X =\\ ?W6W LW)"
  4016. "W+YIXJY LW=W JWCWCW LZBZ K]F] ;W >W2W EWDW 9Y =X /X'X/YAXAY,X0X)X?X?X/X'X%X JV KX <V =X7"
  4017. "X <V CV -\\JSHT BX 4v DXHX BV -b 7SEV*S;V?W 2TBT LSAV@VCS 9b !V AV MU ?W6W MhGU"
  4018. " KU 5V=V ATBT 3U8V HU8V ?UAV CX 1WDW ?WDW ?WDW ?WDW ?WDW ?WDW ?XBX NX +X -X -X -X -X -"
  4019. "X -X -X ,X+X,XHW?X(X'X/X'X/X'X/X'X/X'X#Z?Z'X7WGX-X/X'X/X'X/X'X/X NXJX CX9Y MWFW <U>Z FU>Z "
  4020. "FU>Z FU>Z FU>Z FU>Z FU>eBZ9[>T FZAZ HZAZ HZAZ HZAZ JW ,W ,W ,W )r J^BZ IZBZ GZBZ GZBZ GZBZ"
  4021. " GZBZ 1Z EZB[ JW6W MW6W MW6W MW6W W6W K_CZ MW6W V=V>V-V%V-VHZHYHV-V6YHV-V%V%W 7X7X "
  4022. " 4X NU:T <s Ae N~^'u\"X<X LW4W BWNW >W<W MW $w EX 2~X2WES ;WCWEV GY "
  4023. "9W #W 5XMTMX 'V IV 4X4X >X !Y 0Y BVDX Dk CXJc -X BX>X LX5Y MX -X Ee Le 3Z ?U=bKUC"
  4024. "U6XDX IX9Y X +X+X+X -X /X +X/X'X -X -XL[ <X -XIV@VIX0XHX@X(X'X-X8Y!X'X-X8X N[ X <X/X%X8X "
  4025. "NX@VGV@X.c =XLX +Z FW ,V !W AR9Y H]?Y KZ:R GY?] LY=Y IW -Y?] M]@Y KW ,W ,WAY DW ,]@X"
  4026. "NV@X;]@Y JY>Y J]?Y KY?] M] 4X8P CW ,W6W X8X MW?\\?W-XAX IW7X 3Y 5W :V MW =_C_(YBXLV NW"
  4027. "?Z -W CXC\\ KY ,]@Y LW8X >] ?W6W LW)W,YHWHY MW=W JWCWCW MY>Y L[B[ ;W >W2W FWBW 9Y >X 0X%X0X"
  4028. "@X@X,X0X)X?X?X/X'X&Y JV KY =V >Y7Y =V CV .[HSFR BX 3t BWHW AV .WN\\ 9SFV)S;V?W 3UCU "
  4029. " LSAV@VCS 7_ V BV LU ?W6W MhGU KU 5W?W AUCU 4U8V HU8V ?UAV CX 2XDX AXDX AXDX AX"
  4030. "DX AXDX AXDX @XBX NX +X -X -X -X -X -X -X -X ,X+X,XHX@X(X'X/X'X/X'X/X'X/X'X\"ZAZ&X8WFX-X/X'"
  4031. "X/X'X/X'X/X MXLX BX8X MWFW <R9Y GR9Y GR9Y GR9Y GR9Y GR9Y GR9a>Y;Z:R GY=Y JY=Y JY=Y JY=Y KW"
  4032. " ,W ,W ,W *]E[ J]@Y JY>Y IY>Y IY>Y IY>Y IY>Y 2Z FY>Y JW6W MW6W MW6W MW6W W7X K]?Y NW7X "
  4033. " V=V=U-V$U-VGZJYFU-V7YFU-V$U%W 7X8X &~X/X:T =t @c L~\\'v\"W:W LW4W CXNX ?X>X MV "
  4034. " $x EX 2~X2WES :VDWEV FZ :W #W 7XKTKX )V IV 4X4X >X !X 0Y BWDX Dm FXKf "
  4035. "/Y AYBY KX5Y MX -X Gd ~X d 5Y ?V>dLUCU6WBW IX;Z Y +X+Y,X -X 0Y +X/X'X -X -XM[ ;X -XIWBWIX"
  4036. "0XGW@X)Y'Y.X8X!Y'Y.X9Y M] #X <X/X$X:X MX@VGV@X-a <YNY ,Z EW ,V !W AP6X H\\=Y LY7P HY="
  4037. "\\ LX;X IW .Y=\\ M[=X KW ,W ,WBY CW ,[=]=W;[=X KY<Y K\\=Y MY=\\ M\\ 4X *W ,W6W NW8X MW@VNV@W,XC"
  4038. "X GW8W 3Y 4W :V MW >aEa)X@XNW NWA[ ,W DW?[ LX +[=X KW:X =] ?W6W MW'W-XGWGX MW=W JWCWC"
  4039. "W MX<Y NZ>Z <W >W2W FWBW 9Z ?X 0X%X0X@X@X,X0X(X@X@X/Y'Y(Y IV JY >V ?Y5Y >V CV .YFSDP B"
  4040. "X 2q @XJX AV /WK[ :SFV)S;V@X 4VDV LSAV@VCS 6\\ MV CV KU ?W6W MhGU KU 4V?V @V"
  4041. "DV 5U9V GU9V >UBV BX 2WBW AWBW AWBW AWBW AWBW AXDX @XBX Y +X -X -X -X -X -X -X -X ,X+Y-XGW"
  4042. "@X)Y'Y1Y'Y1Y'Y1Y'Y1Y'Y\"ZCZ&Y9WEY.X/X'X/X'X/X'X/X MYNY BX8Y NWFW <P6X GP6X GP6X GP6X GP6X G"
  4043. "P6X GP6_<X;Y7P GX;X JX;X JX;X JX;X KW ,W ,W ,W *Z?Y K[=X KY<Y KY<Y KY<Y KY<Y KY<Y 3Z GY<Y "
  4044. "KW6W MW6W MW6W MW6W NW8W J\\=Y NW8W NV=V=V.V$V.VFZLYEV.V8YEV.V$V&W 6W8X &~X2\\<T =v"
  4045. " Ab K~\\(x$W8W MW4W CXNX ?X>X NW $w DX $VBV#XFS :WFXEV H] ;W #W 9XITIX"
  4046. " +V JW 4X4X >X \"Y 3[ BWCX Dn GXLi 1X ?ZFZ JY7Z MX -X Je M~X Me 9Y >U?gMUCV7WBW IX>\\"
  4047. " NX *X*X,X -X 0X *X/X'X -X -XNZ 9X -XHVBVHX0XGXAX)X%X.X9Y!X%X.X:Y La 'X <X/X$X:X LWAWGV@W+"
  4048. "_ :XNX ,Z DW ,W \"W &W H[;X MY .X;[ MX9X JW .X;[ M[<X LW ,W ,WCY BW ,Z<\\<X<[<X LX:X K"
  4049. "[;X MX;[ M[ 3W )W ,W6W NW8W KWAVNVAW*XEX FW9X 4Y 3W :V MW ?cGc+Y?WNV MWD] +W DV=Z LX "
  4050. "+Z;X LW:X >_ @W6W MW'W.YGWFX NW=W JWCWCW NX:X NY<Y <W >W2W FWBW 8Z @X 0X%X0X@X@X,X0X(X@X@X"
  4051. "/X%X)Y HV IY ?V @Y3Y ?V CV /YES 6X 1\\H[ JcJc LV 0WI\\ =TFV)S;WAX 5WEW MTAVAWCS 3"
  4052. "W 4~W.W KV ?W6W LgGU KU 4WAW @WEW 6U9V GU9V ?VBV BX 2WBW AWBW AWBW AWBW AWBW AWBW A"
  4053. "XAX X *X -X -X -X -X -X -X -X ,X*X-XGXAX)X%X1X%X1X%X1X%X1X%X!ZEZ%X9WCX.X/X'X/X'X/X'X/X LXN"
  4054. "X AX7X NWFW !W ,W ,W ,W ,W ,W ,]:X=Y .X9X LX9X LX9X LX9X LW ,W ,W ,W +Z=X K[<X LX:X KX:X K"
  4055. "X:X KX:X KX:X 3Z GX<Z KW6W MW6W MW6W MW6W NW9X J[;X NW9X NU<V=V.U#V.UDZNYDV.U8YDV.U#V&"
  4056. "V 5X9W %~X3]<T >x A` J~\\(y%W8W MW4W CXMW >W>W MV $x DX $VCV\"XFS 9X"
  4057. "IXEV H_ <W #W ;YHTHY -V JV 3X4X >X #Y ?g AVBX Do HXMk 3Y >l HX7Z MX -X Me J~X Je "
  4058. "=Y >V?hNUBU8XBX Ju MX *X*X,w Lq IX *~R'X -X -c 8X -XHVBVHX0XFWAX)X%X.X9Y!X%X.X;Z Ke ,X <X/"
  4059. "X$X:X LXBVEVBX+_ 9` +Y CW +V \"W %W IZ9X NX .X9Z MW7W JW /X9Z MZ;X LW ,W ,WDY AW ,Z;["
  4060. ";W<Z;X MY:Y LZ9X X9Z MZ 2W )W ,W6W NX:X KWAVNVAW*YGY EW:W 4Z 3W :V MW ?XMYIe,X>WNV MW"
  4061. "Ib +W EW;Y MW *Z;X KV:W =_ @W6W NW%W/XFWFX NW=W JWCWCW NW8X!Y:Y =W >| GW@W 8Y @X 0X%X1Y@X@"
  4062. "Y-X0X(X@X@X/XImIX*Y GV HY @V AY1Y @V CV /XDS 6X 0YDY JdLd LV 1WF[ >SFV'S<WBY 6XFX "
  4063. " MS@VAVAS @~W/W JU >W6W LgGU KU 3WCW ?XFX 7U:V FU:V >UBV AX 3XBX CXBX CXBX CXBX"
  4064. " CXBX CXBX BXAw?X *w Lw Lw Lw LX -X -X -X ,X*X-XFWAX)X%X1X%X1X%X1X%X1X%X ZGZ$X:WBX.X/X'X/X"
  4065. "'X/X'X/X K` @X7X NWFW W ,W ,W ,W ,W ,W ,[8W=X -W7W LW7W LW7W LW7W LW ,W ,W ,W ,Y:X LZ;X M"
  4066. "Y:Y MY:Y MY:Y MY:Y MY:Y \"Y=\\ LW6W MW6W MW6W MW6W MW:W IZ9X NW:W NV<V=V/V#V/VCcCV/V9YC"
  4067. "V/V=X>V&V 4W:X %~X2TNV<S =y KWM^LW$~Z({&W7V MW4W CWLX ?X?W MV KX ,X"
  4068. " %VBV!XGS 9gFV Ha >W \"W ;WFTFW -V JV 3X4X >X #Y ?f AWBX Dp IXNm 4X <j GX7Z MX -X"
  4069. " !e G~X Ge AY =U?ZH^BU8W@W Jt LX *X*X,w Lq IX *~R'X -X -b 7X -XHWDWHX0XFXBX)X%X.X:Y X%X.X<"
  4070. "Z Ih 0X <X/X#X<X KXBVEVBX*] 8` ,Z CW +V \"W %W IZ9X X -X9Z NX7X KW /X9Z MY9W LW ,W ,W"
  4071. "EY @W ,Y:Z:W<Y9W MX8X LZ9X X9Z MY 1W )W ,W6W MW:W JWAVNVAW)XGX DW:W 4Y 3X :V MW @VHXK"
  4072. "WGV,W<^ MWIa *W FW9Y NW *Y9W KW<X >` @W6W NW%W/WEWEW NW=W JWCWCW X8X!X8X =W >| GW@W 7Y AX "
  4073. "0X%X1X?X?X-X0X(X@X@X/XImIX+Y FV GY AV BY/Y AV DX 1XCS 6X 0W@X KdLd LV 1VCZ ?SFV'S;WE"
  4074. "[ 7XFX G~X .S@VBWAS @~W0W .P>W >W6W KfGU KU 3XEX >XFX 8U;V:W3U;VCZ9P>WCV:W/Y 3W@"
  4075. "W CW@W CW@W CW@W CW@W CXBX CX@w?X *w Lw Lw Lw LX -X -X -X 5p9X-XFXBX)X%X1X%X1X%X1X%X1X%X N"
  4076. "ZIZ#X:VAX.X/X'X/X'X/X'X/X K` @X7X NWFW W ,W ,W ,W ,W ,W ,[8X?X -X7X NX7X NX7X NX7X MW ,W "
  4077. ",W ,W ,X9X LY9W MX8X MX8X MX8X MX8X MX8X \"X=] LW6W MW6W MW6W MW6W MW:W IZ9X NW:W NVLu"
  4078. "KU/VLuKU/VBaAU/V:YAU/V=X=U&V 4X;X %~X2RLW>T >{!z'~Z)}(W6W NW4W DXLX ?X@X MV "
  4079. " KX ,X %VBV!YHS 8eEV Ic ?W !W ;UETEU ,V KW 3X4X >X $Y >c ?WAX DWD^ JbG] "
  4080. "5X 9d DY9[ MX -X #d D~X Dd DY <U@YD\\BU9X@X Kq IX *X*X,w Lq IX *~R'X -X -a 6X -XGVDVGX0XEWB"
  4081. "X)X%X.X;Z X%X.X?\\ Gk 4X <X/X#X<X KXBVEVBX)[ 6^ ,Z BW +W #W %W IY7W X -W7Y NW5W KW 0X"
  4082. "7Y MY9W LW ,W ,WFY ?W ,Y:Z:W<Y9W MW6W LY7W W7Y MY 1W )W ,W6W MW:W JWBVLVBW(XIX CW;X 5Y 2X "
  4083. ":V MX BUDVKVDU.X<] LWI_ :WEW FV7X NW *Y9W JV<X >a AW6W NW%W0XEWEX W=W JWCWCW W6W!X8X "
  4084. "=W >| HX@X 7Y BX 0X%X1X?X?X-X0X(X@X@X/XImIX,Y EV FY BV CY-Y BV DX 1XCS 6X 1W>W KeNe LV"
  4085. " 1VB[ ASFV'S;YI] 9YGY F~X .S@VDX@S @~W1V ,TEZ >W6W JeGU IX +U 2YIY <YGY :U;V:W3U"
  4086. ";VGa<TEZCV:W/X 3X@X EX@X EX@X EX@X EX@X EX@X DX@w?X *w Lw Lw Lw LX -X -X -X 5p9X-XEWBX)X%X"
  4087. "1X%X1X%X1X%X1X%X MZKZ\"X;WAX.X/X'X/X'X/X'X/X J^ ?X7X NWFX !W ,W ,W ,W ,W ,W ,Z6W?X -W5W NW5"
  4088. "W NW5W NW5W MW ,W ,W ,W -X7W LY9W MW6W MW6W MW6W MW6W MW6W \"W=^ LW6W MW6W MW6W MW6W MW;X "
  4089. "IY7W NW;X NVLuKU/VLuKU/VA_@U/V;Y@U/V=X=U&V 4X<X $~X,W>T ?|\"}(~X)~(W6W NW4W DXKW >"
  4090. "W@X MV KX ,X %VBV!ZIS 7cEV IYNZ8W 0W !W :RCTCR +V KW 3X4X >X %Y"
  4091. " =b >V@X DS=\\ K`C[ 6Y 8b BX9[ Nd A~X Ad HY <VAX@ZBV:X?W Kq IX *X*X,w Lq IX *~R'X -X -a"
  4092. " 6X -XGVDVGX0XEXCX)X%X.X=[ NX%X.u Fl 6X <X/X\"W<W IWCWEVBW([ 5\\ ,Z AW +W #W $V IY7X\"X"
  4093. " -X7Y NW5W KW 0X7Y MX8X MW ,W ,WHZ >W ,X8X8W=X8X X6X MY7X\"X7Y MX 0W )W ,W6W MX<X IWCVLVCW&"
  4094. "XKX AW<W 5Y 1W 9V LW 4P /TBVMVBT.X;\\ LWI` =\\HW GW7X NW *X8X KV=X >XMW AW6W NW%W0XEWDW W"
  4095. "=W JWCWCW!X6X#X6X >W >| HW>W 6Y CX 0X%X1X?X?X-X0X'XAXAX.XImIX-Y DV EY CV DY+Y CV DX 2X"
  4096. "BS 6X 1V<V KeNe LV 2V?Y ASFV'S:dNV :XFY E~X .S@i?S @~W2i >h =W6W JeGU IX 4g :g :"
  4097. "YFX DgEV:X<gEVHe>hCV:X/X 3X?W EX?W EX?W EX?W EX?W EX@X EX?w?X *w Lw Lw Lw LX -X -X -X 5p9X"
  4098. "-XEXCX)X%X1X%X1X%X1X%X1X%X LZMZ!X<W@X.X/X'X/X'X/X'X/X I\\ >X7X NWFY !V +V +V +V +V +V +Y6W@"
  4099. "X ,W5W NW5W NW5W NW5W MW ,W ,W ,W -X7X MX8X X6X X6X X6X X6X X6X $X=_ MW6W MW6W MW6W MW6W "
  4100. "LW<W HY7X NW<W MVLuKU/VLuKU/V@]?U/V<Y?U/V=X=U&V 3W<X $~X+V>S >}%~R)~V(~P)W6W NW4W"
  4101. " DWJX ?XAW L~^ $X ,X %VCV N\\LS 6aDVAW0XLZ9W 0W !W :PATAP +V KV 2X"
  4102. "4X >X &Z =e BW@X DP8[ L^?Z 7X :h EY;\\ \"d >~X ?e LY ;U@W>YAU:W>W Ks KX *X*X,w Lq IX6f+~R"
  4103. "'X -X -b 7X -XGWFWGX0XDWCX)X%X.X@^ NX%X.s Bl 8X <X/X\"X>X IXDVCVDX)[ 4\\ -Z @W *V #W $"
  4104. "W JX5W\"X -W5X W4W KW 0W5X MX7W MW ,W ,WIZ =W ,X8X8W=X7W W4W MX5W\"W5X MX 0X *W ,W6W LW<W HW"
  4105. "CVLVCW&YMY AW=X 6Y 1X 9V LX 1X.Q /TA]AU/W:\\ LWIb A`JW GV5X NW +X7W KW>X >XMX BW6W W#W1WD"
  4106. "WDW W=W JWCWCW!W4W#X6X >W >| HW>W 7Y BX 0X%X1X?X?X-X0X'XAXAX.XImIX.Y CV DY DV EY)Y DV "
  4107. "DX 2XBS 6X 2W<W =^ =V 2V>Y BSFV'S9bMV ;XFY D~X .S@h>S @~W2i >g <W6W HcGU IX 4g 9"
  4108. "e 8YFX EgEV;Y<gEVHf?gBV;Y0Y 3W>W EW>W EW>W EW>W EW>W EW>W EX?w?X *w Lw Lw Lw LX -X -X -X 5"
  4109. "p9X-XDWCX)X%X1X%X1X%X1X%X1X%X Ke X=W?X.X/X'X/X'X/X'X/X I\\ >X7X NWEY \"W ,W ,W ,W ,W ,W ,X5W"
  4110. "@X -W4W W4W W4W W4W MW ,W ,W ,W -W6X MX7W W4W W4W W4W W4W W4W $W=VMW MW6W MW6W MW6W MW6W "
  4111. "LW=X HX5W NW=X MVLuKU/VLuKU/V?[>U/V=Y>U/V=X=U&V 3X=W 7X FW@T ?~&~T*~V)~R*W5V NW4"
  4112. "W EXJX ?XBX L~^ $X ,X &VBV Mb 4]CVC]4XJZ:W 0W !W +T KV KV 2X4X >"
  4113. "X 'Z <g EW?X +Z L]=Z 9Y <l GZ=] %e e!Y :UAW<XAU;X>X Lu MX *X*X,w Lq IX6f+~R'X -X -c "
  4114. "8X -XFVFVFX0XDXDX)X%X.u MX%X.r ?l :X <X/X\"X>X IXDVCVDX)\\ 4Z ,Y ?W *V #W $W JX5W\"W ,W"
  4115. "5X W3W LW 0W5X MX7W MW ,W ,WJY ;W ,X8X8W=X7W W4W MX5W\"W5X MX 0X *W ,W6W LW<W HWCVKUCW%XMX "
  4116. "?W>W 6Y 0X 9V LX 5`3R 0T?[?T/W:[ KWId DbKW HW5X NW +X7W JV>W =WLX BW6W W#W1WDWDW W=W JWC"
  4117. "WCW!W4W#W4W >W >| IX>X 9Y AX 0X%X1X?X?X-X0X'XAXAX.XImIX/Y BV CY EV FY'Y EV DX 2WAS ?r "
  4118. "CV:V =^ =V 2V=Y CSFV'S8`LV <XFX B~X .S@e;S @~W2i >e :W6W GbGU IX 4g 8c 5XFX FgFV"
  4119. ":Y<gFVGg@eAV:Y1Y 3X>X GX>X GX>X GX>X GX>X GX>X FX?w?X *w Lw Lw Lw LX -X -X -X 5p9X-XDXDX)X"
  4120. "%X1X%X1X%X1X%X1X%X Jc NX>W>X.X/X'X/X'X/X'X/X HZ =X7X NWEZ #W ,W ,W ,W ,W ,W ,X4WAW ,W3W!W3"
  4121. "W!W3W!W3W NW ,W ,W ,W .X5W MX7W W4W W4W W4W W4W W4W $W>VLW MW6W MW6W MW6W MW6W KW>W GX5W "
  4122. "MW>W LVLuKU/VLuKU/V>Z>U/V>Y=U/V=X=U&V 2W>X 8Y FW@T ?~P(~V*~T(~Q)V4V NW4W EXJX >W"
  4123. "BX L~^ $X ,X &VBV Ld 4WAVD`6XHZ;W 0W !W +T KV LW 2X4X >X 'Y ;i G"
  4124. "V>X *Z M\\;Y 9X =p HZ?^ 'd Id$Y 9UAW<XAU;W<W Lw X *X*X,w Lq IX6f+~R'X -X -d 9X -XFVFV"
  4125. "FX0XCWDX)X%X.t LX%X.p ;k ;X <X/X!X@X HXDVCVDX*^ 4X ,Z ?W *W $W $W JX5W\"W ,W5X W3W LW"
  4126. " 0W5X MW6W MW ,W ,WKY :W ,W7W7W=W6W W4W MX5W\"W5X MX /Y ,W ,W6W LX>X GWEVJVEW#a >W>W 7Y 1Y "
  4127. "8V KY 9e8T 0T?Z>T0X:[ KWIf GdLW HW4W MW ,W6W JV?X >XKW BW6W W#W2XDWDX!W=W JWCWCW!W4W#W4W"
  4128. " >W >| IW<W :Y @X 0X%X1X?X?X-X0X&XBXBX-XImIX0Y AV BY FV GY%Y FV DX 2WAS ?r DW:W =\\ <V "
  4129. "2V;W CSFV'S7]JV =XFX A~X .S@d:S (V Ii <a 8W6W FaGU IX 4g 6_ 2XFX GgGV:Z<gGVFUFY?"
  4130. "a@V:Z2Y 2W<W GW<W GW<W GW<W GW<W GX>X GX>w?X *w Lw Lw Lw LX -X -X -X 5p9X-XCWDX)X%X1X%X1X%",
  4131. // Second string:
  4132. "X1X%X1X%X Ia MX?W=X.X/X'X/X'X/X'X/X GX <X7X NWDZ $W ,W ,W ,W ,W ,W ,X4WAW ,W3W!W3W!W3W!W3W"
  4133. " NW ,W ,W ,W .W4W MW6W W4W W4W W4W W4W W4W $W?VKW MW6W MW6W MW6W MW6W KW>W GX5W MW>W "
  4134. "LVLuKU/VLuKU/V?\\?U/V?Y<U/V=X=U&V 2W>X 8X DWBT ?~Q)~W)~R&~(V4V NW4W EWHW >WBW K~^ "
  4135. " $X ,X &VBV Kg \"VEc8WFZ=W /W !W +T 4~W 5V 1X4X >X (Y -] IW>X )Y M[9X 9"
  4136. "X >\\F\\ H[C` 'a Ca$Y 9UAV:WAU;W<W LX<\\!X *X*X,X -X 0X6f+X/X'X -X -XN[ :X -XEVHVEX0XCX"
  4137. "EX)X%X.s KX%X.o 6h <X <X/X!X@X GWDVCVDW*_ 4X -Z >W )V $W 6i JX5X$X -X5X V2W LW 1W3W "
  4138. "MW6W MW ,W ,WLY 9W ,W7W7W=W6W!X4X NX5X$X5X MW .[ .W ,W6W KW>W FWEVJVEW#a >W?X 8Z 4\\ 8V K["
  4139. " =i<V 0S=Y=S0X:[ KW@^ IfMW HW4W MY .W6W JW@W =XKX CW6W W#W2WCWCW!W=W JWCWCW\"X4X%X4X ?W >W"
  4140. "2W IW<W :Y @X 0X%X1X?X?X-X0X&XBXBX-X%X1~` GV H~` GV H~` GV DX 3XAS ?r DV8V =\\ <V 2V;X "
  4141. "DSFV'S4W /XFX @~X .S@VIX;S (V Ii 8Z 5W6W D_GU IX 4g 3Y .XFX HgGV;TNU<gGVFQ@W;Z=V;T"
  4142. "NU3Y 1W<W GW<W GW<W GW<W GW<W GW<W GX>X X *X -X -X -X -X -X -X -X ,X*X-XCXEX)X%X1X%X1X%X1X"
  4143. "%X1X%X H_ LX@W<X.X/X'X/X'X/X'X/X GX <X7X NWD\\ 8i >i >i >i >i >i >i3WBX ,V2W!V2W!V2W!V2W NW"
  4144. " ,W ,W ,W .W4W MW6W!X4X\"X4X\"X4X\"X4X\"X4X M~Y2X@VIW NW6W MW6W MW6W MW6W KW?X GX5X NW?X L"
  4145. "VLuKU/VLuKU/V@^@U/V@Y;U/V=X=U&V 2X?W 8X CWBT ?~R*~X)~Q%}(V4W W4W FXHX ?XDX K~^ "
  4146. " $X ,X 'WCV Ii &VEe:XEZ>W /W !W +T 4~W 5V 1X4X >X )Y )[ KW=X (Y N[9Y ;Y "
  4147. "?Z@Z I]Gb '^ =^$X 9U@V:WAU<X<X MX9Z\"X *X*X,X -X 0X6f+X/X'X -X -XM[ ;X -XEVHVEX0XBWEX"
  4148. ")X%X.r JX%X.q 4e =X <X/X!X@X GXFVAVFX*` 5X .Z =W )V $W :m JW3W$W ,W3W!W2W LW 1W3W MW"
  4149. "6W MW ,W ,WMY 8W ,W7W7W=W6W!W2W NW3W$W3W MW -^ 2W ,W6W KX@X FWEVJVEW\"_ <W@W 7Y :b 7V Jb F"
  4150. "mAX 0S<W<S0W8Y JW<[ KYHVMV GV3X MZ 0W6W IVAX >XIW CW6W!W!W3WCWCW!W=W JWCWCW\"W2W%W3X ?W >W"
  4151. "2W JW;X <Y ?X 0X&Y1X?X?X-X0X&YCXCY-X%X2~a GV H~a HV I~b HV DX 3W@S ?r DV8V <Z ;V 2W;W "
  4152. "DSFV'S <XFX =V .S@VGW<S (V \"W6W A\\GU IX 2XFX *V;TMU LV2V V;TMU4Z 2X<X IX<"
  4153. "X IX<X IX<X IX<X IX<X IX=X X *X -X -X -X -X -X -X -X ,X*X-XBWEX)X%X1X%X1X%X1X%X1X%X G] KX@"
  4154. "V;X.X/X'X/X'X/X'X/X GX <X8Y NWC\\ =m Bm Bm Bm Bm Bm Bm3WBW ,W2W\"W2W\"W2W\"W2W NW ,W ,W ,W /X4"
  4155. "X NW6W!W2W\"W2W\"W2W\"W2W\"W2W M~Y2W@VHW NW6W MW6W MW6W MW6W JW@W FW3W MW@W KVLuKU/VLuKU/V"
  4156. "A`AU/VAY:U/V=X=U&V 1W@X 9X BWBS >~R+~Z*~P#{'V4W W4W FXHX ?XDX K~^ $X "
  4157. " ,X 'VBV Gi (VFg;WCZ?W /W !W +T 4~W 6W 1X4X >X *Y &Z LW=X (Y NZ7X ;X ?Z>Z ImNX "
  4158. "'[ 8\\%Y 9UAW:WAU<W:W MX7Y#X *X*X,X -X 0X6f+X/X'X -X -XL[ <X -XEWJWEX0XBXFX)X%X.p HX%X.r"
  4159. " 0a >X <X/X XBX FXFVAVFX+b 6X /Z <W )W %W =p JW3W$W ,W3W!| LW 1W3W MW6W MW ,W ,WNY 7"
  4160. "W ,W7W7W=W6W!W2W NW3W$W3W MW -b 6W ,W6W JW@W EWFVHVFW!] ;WAX 8Y 9` 5V H` HrG[ 0S<W<S0W8Y"
  4161. " JW:Y KXF^ HW2W Kc ;W6W IVAX >XIW CW6W!W!W3WCWCW!W=W JWCWCW\"W2W%W2W ?W >W2W JW:W =Y >X 0Y'"
  4162. "X0X?X?X-X0X%XCXCX,X%X2~a GV H~a HV I~b HV DX 3W@S ?r DV8V <Z FW;W DSFV'S =XFX <V "
  4163. ".S@VFW=S (V \"W6W <WGU IX 1XFX +V;SLU LV2V V;SLU5Z 1W:W IW:W IW:W IW:W IW:W I"
  4164. "X<X IX=X X *X -X -X -X -X -X -X -X ,X*X-XBXFX)X%X1X%X1X%X1X%X1X%X F[ JXAW;X.X/X'X/X'X/X'X/"
  4165. "X GX <X8X MWB] Bp Ep Ep Ep Ep Ep E~eBW ,|\"|\"|\"| NW ,W ,W ,W /W2W NW6W!W2W\"W2W\"W2W\"W2W\"W2W "
  4166. "M~Y2WAWHW NW6W MW6W MW6W MW6W JWAX FW3W MWAX KV<V=V/V#V/VBbCV/VBY:V/V=X>V&V 1XAW 9"
  4167. "X @WDT ?~S+~Z)}!y'W4W W4W FWFW >WDW J~^ *r ?V &VBV Eh *VEXIX<XBZ@W /W"
  4168. " !W +T 4~W 5f 8V 0X4X >X +Y $Z NW<X 'X NZ7X ;X ?X:X HkMX '[ 7[%X 8UAV8VAU=X:X NX6"
  4169. "X#X *X*X,X -X 0X6f+X/X'X -X -XK[ =X -XDVJVDX0XAWFX)X%X.m EX%X.XA\\ -^ ?X <X/X XBX FXFVAVFX,"
  4170. "c 6X /Y ;W (V %W ?r JW3W$W ,W3W!| LW 1W3W MW6W MW ,W ,a 6W ,W7W7W=W6W!W2W NW3W$W3W M"
  4171. "W ,e :W ,W6W JW@W DWGVHVGW N[ 9WBW 8Y 8^ 3V F^ I~X 0S;U;T1W8Y JW8X MXC\\ HW2W Ia ;W6W IWB"
  4172. "W >XHX DW6W!W<W<W3WCWCW!W=W JWCWCW\"W2W%W2W ?W >W2W KX:X ?Y =X /X'X0Y@X@Y-X0X%YDXDY,X%X2~a "
  4173. "GV H~a HV I~b HV DX 3W@S ?r DV8V ;X DW;V DSFV'S >XFX ;V .S@VFW=S (V \"W6W "
  4174. ":UGU IX 0XFX -V;TLU MV0U!V;TLU6Y 0X:X KX:X KX:X KX:X KX:X KX:X JW<X X *X -X -X -X -X"
  4175. " -X -X -X ,X*X-XAWFX)X%X1X%X1X%X1X%X1X%X F[ JXBW:X.X/X'X/X'X/X'X/X GX <X9Y MWA] Er Gr Gr G"
  4176. "r Gr Gr G~gBW ,|\"|\"|\"| NW ,W ,W ,W /W2W NW6W!W2W\"W2W\"W2W\"W2W\"W2W M~Y2WBWGW NW6W MW6W MW6W "
  4177. "MW6W IWBW EW3W LWBW IU<V=V.U#V.UCdDV.UCY9V.U=X>V&V 1XBX :X ?WDT ?~S,~[({ x&W4W W"
  4178. "4W FWFX ?XFX JV \"q >V &VBV Af -VEXGX=W@ZBW .W !W +T 4~W 5f 8V 0X4X "
  4179. ">X ,Y \"Y W;X 'X NZ7X <Y @Y:Y HiLX '^ =^%X 8UAV8VAU=X:X NX5X$X *X*X,X -X 0X(X+X/X'X -"
  4180. "X -XJ[ >X -XDVJVDX0XAXGX)X%X.i AX%X.X>Z ,\\ ?X <X/X NWBW DWFVAVFW+XMY 7X 0Z ;W (V %W "
  4181. "@s JW3W$W ,W3W!| LW 1W3W MW6W MW ,W ,` 5W ,W7W7W=W6W!W2W NW3W$W3W MW +g =W ,W6W JXBX DWGVH"
  4182. "VGW N[ 9WBW 9Y 7^ 3V F^ I[Gr /S;U;T1W8X IW7X NWA[ HW2W F^ ;W6W HVCX >XGW DW6W!W<W<W3WCWC"
  4183. "W!W=W JWCWCW\"W2W%W2W ?W >W2W KW9X ?Y =X /X'X/X@X@X,X0X$YEXEY+X%X2~a GV H~a HV I~b HV DX "
  4184. "3W@S 6X 3V8V ;X DX<V DTFV)T >WEW :V .TAVEW?T (V \"W6W :UGU IX /WEW .V;"
  4185. "TKU NV/U\"V;TKU7Y /X:X KX:X KX:X KX:X KX:X KX:X KX<X X *X -X -X -X -X -X -X -X ,X*X-XAXGX)X"
  4186. "%X1X%X1X%X1X%X1X%X G] KXCW9X.X/X'X/X'X/X'X/X GX <X9Y MW?] Hs Hs Hs Hs Hs Hs H~hBW ,|\"|\"|\"|"
  4187. " NW ,W ,W ,W /W2W NW6W!W2W\"W2W\"W2W\"W2W\"W2W M~Y2WBVFW NW6W MW6W MW6W MW6W IWBW EW3W LWBW "
  4188. " IU<V=V.U#V.UDYMZEV.UDY8V.U#V&V 0WBX ;X >WDS >~T-~\\(y Mw&W4W W4W GXFX ?XFX JV "
  4189. " #r >V 'WCV <c .VEWEW=W?ZCW .W !W :~W 5f 9W 0X4X >X -Y Y!W;X 'Y Y5X =X"
  4190. " @Y8Y HgKX 'a Ca%X 8UAV8VAU=W8W NX4X%X *X+Y,X -X 0X(X+X/X'X -X -XI[ ?X -XDWLWDX0X@WG"
  4191. "X)X&Y.X 0X&Y.X=Y *[ @X <X/X NXDX DXHW@VHX,YMZ 8X 1Z :W (W &W At JW3W$W ,W3W!| LW 1W3"
  4192. "W MW6W MW ,W ,` 5W ,W7W7W=W6W!W2W NW3W$W3W MW )g ?W ,W6W IWBW CWGVHVGW MY 8WCX :Y 6` 5V H"
  4193. "` IW@m -S;V<T1W8X IW7X W@[ HW2W Ia ;W6W HVCW >XFX EW6W!W<W<W3WCWCW!W=W JWCWCW\"W2W%W2W ?W "
  4194. ">W2W KW8W @Y <X /X'X/X@X@X,X0X#YFXFY*X&Y2~a GV H~a HV I~b HV DX 3W@S 6X 3V8V ;X CX=V "
  4195. " CSFV)S =WEW :V -SAVDW@S 'V \"W6W :UGU IX /WEW .V<TJU NV/U\"V<TJU8Z /W8W KW"
  4196. "8W KW8W KW8W KW8W KX:X KX<X X *X -X -X -X -X -X -X -X ,X+Y-X@WGX)X&Y1X&Y1X&Y1X&Y1X&Y H_ LX"
  4197. "DW9Y.X/X'X/X'X/X'X/X GX <X:Y LW>] Jt It It It It It I~iBW ,|\"|\"|\"| NW ,W ,W ,W /W2W NW6W!W"
  4198. "2W\"W2W\"W2W\"W2W\"W2W M~Y2WCVEW NW6W MW6W MW6W MW6W IWCX EW3W LWCX IV=V=V.V$V.VFYKZFV.VFY"
  4199. "7V.V$V&V 0XCW ;Y =WFT >~T-~\\'w Ku%W4W W4W GXEW >WFW IV #q =V 6~X "
  4200. "JSN^ /VEWCW?W=ZDW .W !W :~W 5f 9V /X4X >X .Y MX\"W:X &X Y5X >Y @X6X FcJX &d Id"
  4201. "%X 8UAV8VAU>X8X X4X$X +X+X+X -X /X)X+X/X'X -X -XH[ @X -XCVLVCX0X@XHX(X'X-X /X'X-X<Y *Z @X "
  4202. "<X/X NXDX DXHV?VHX-YKY 8X 2Z 9W 'V &W B]?W JW3W$W ,W3W!| LW 1W3W MW6W MW ,W ,a 6W ,W"
  4203. "7W7W=W6W!W2W NW3W$W3W MW 'g AW ,W6W IWBW CWHVFVHW NZ 7WDW :Z 6a 6V Jb IU;i ,S;V<S0W7W IW"
  4204. "6W W?Z HW2W Kc ;W6W HWEX >XFX EW6W!W<W<W3WCWCW!W=W JWCWCW\"W2W%W2W ?W =V2V KX8X BY ;X /Y)Y/"
  4205. "X@X@X,X0X#YFXGZ)X'X0~` GV H~` GV H~` GV DX 3W@S 6X 3V8V M| &Z?V CSFV)S:m AXFX ;V -S"
  4206. "AVDW@S 'V \"W6W :UGU *m 5XFX /V;SIU V.T\"V;SIU9Z /X8X MX8X MX8X MX8X MX8X MX8X "
  4207. "MX;X NX +X -X -X -X -X -X -X -X ,X+X,X@XHX(X'X/X'X/X'X/X'X/X'X Ha LXFW8X-X/X'X/X'X/X'X/X G"
  4208. "X <X;Z LW<\\ L]?W J]?W J]?W J]?W J]?W J]?W J]?{BW ,|\"|\"|\"| NW ,W ,W ,W /W2W NW6W!W2W\"W2W\"W2"
  4209. "W\"W2W\"W2W M~Y2WDVDW NW6W MW6W MW6W MW6W HWDW DW3W KWDW HV=V>V-V%V-VGYIZHV-VGY7V-V%V%V "
  4210. "/WDX ;X <WFT >~T-~\\'v Is$W4W W4W GWDX ?XGW HV %r =V 6~X JSJ[ 0VEV"
  4211. "AV?W<ZFW -W !W \"V Lf 9V /X5X =X /Z MX\"V9X &X NX5X >X ?X6X D`IX $d Ne#X 8UAV8"
  4212. "VBU=x X4X$X +X+X+X -X /X)X+X/X'X -X -XG[ AX -XCVLVCX0X?WHX(X'X-X /X'X-X;Y *Y @X <X/X MXFX "
  4213. "CXHV?VHX-XIY 9X 3Z 8W 'V &W CZ;W JW3W$W ,W3W!| LW 1W3W MW6W MW ,W ,b 7W ,W7W7W=W6W!W"
  4214. "2W NW3W$W3W MW %f BW ,W6W IXDX BWIVFVIW N\\ 8WEX :Y .[ 7V K\\ BT8e *S<X=S0W7V HW6X\"W=X GW2"
  4215. "W Me ;W6W GVEX >WDW EW6W!W<W<W3WCWCW!W=W JWCWCW\"W2W%W2W ?W =W4W KW6W CY :X .X)X.YAXAY,X0X\""
  4216. "ZHXHZ(X'X/Y AV BY FV GY%Y FV DX 3W@S 6X 2V:V L| %ZAV BSEV*S:m @XFX <V -SAVCWAS "
  4217. " 'V \"W6W :UGU *m 6XFX .V<TIU V/U\"V<TIU9Y .x Mx Mx Mx Mx Mx Mu NX +X -X -X -X -X "
  4218. "-X -X -X ,X+X,X?WHX(X'X/X'X/X'X/X'X/X'X Ic MXGW7X-X/X'X/X'X/X'X/X GX <X=[ KW:[ NZ;W KZ;W K"
  4219. "Z;W KZ;W KZ;W KZ;W KZ;{BW ,|\"|\"|\"| NW ,W ,W ,W /W2W NW6W!W2W\"W2W\"W2W\"W2W\"W2W &WEVCW NW6W "
  4220. "MW6W MW6W MW6W HWEX DW3W KWEX GV>V>V,V&V,VIYGZIV,VIY6V,V&V&W /XEW N~X'VGT =~T-~\\"
  4221. "&u Ir#W4W NV4W HXDX ?XHX HV KX ,V 6~X JSHZ 2VDVAV?W;ZGW -W !W \"V "
  4222. "Lf :W .X6X =X 0Z LY#~ /X NX5X >X @X5Y AYFX !d >~X >d X 8UAV8VBU>z!X3X%X +X+X+X -X /X"
  4223. ")X+X/X'X -X -XF[ BX -XCWNWCX0X?XIX(X'X-X /X'X-X:X )Y AX <X/X MXFX BWHV?VHW-YIY 9X 3Y 7W 'W"
  4224. " 'W CX9W JW3W$W ,W3W!W 'W 1W3W MW6W MW ,W ,WNZ 8W ,W7W7W=W6W!W2W NW3W$W3W MW !c CW ,"
  4225. "W6W HWDW AWIVFVIW N] 8WFW :Y *Y 8V KY ?R3` (S<X=S0W7V HW5W\"W=X GW2W N[ 0W6W GWFW >XDX FW"
  4226. "6W!W<W<W3WCWCW!W=W JWCWCW\"W2W%W2W ?W =W4W LX6X DY :X .X)X-XAXAX+X0X!ZIXIZ'X'X.Y BV CY EV"
  4227. " FY'Y EV DX 3W@S 6X 2V:V L| $[CV BTFW,T:m ?XFX =V -TBVBVBT 'V \"W6W :UGU "
  4228. " *m 7XFX .V<THU!V/U\"V<THU:Y .z z z z z Nx Nv NX +X -X -X -X -X -X -X -X ,X+X,X?XIX(X'X/X'"
  4229. "X/X'X/X'X/X'X Je NXGV6X-X/X'X/X'X/X'X/X GX <X@^ KW9[ X9W KX9W KX9W KX9W KX9W KX9W KX9W MW "
  4230. ",W ,W ,W ,W )W ,W ,W ,W /W2W NW6W!W2W\"W2W\"W2W\"W2W\"W2W &WFVBW NW6W MW6W MW6W MW6W GWFW CW3"
  4231. "W JWFW FV>V?W,V'W,VJYEZKW,VJY6W,V'W&W /XFX N~X'WHT =~T-~\\%s Gp\"W4W NV4V GXCW >WH"
  4232. "X HW LX ,V 6~X JSGY 3VDWAW@W:ZIW ,W !W \"V Lf :W .X6X =X 1Z JX#"
  4233. "~ /X NX5X ?Y @X4X .X Md A~X Ad LX 8UAV8VBU>z!X3X%X +X+X+X -X /X)X+X/X'X -X -XE[ CX -XB"
  4234. "VNVBX0X>WIX(X'X-X /X'X-X9X *Y AX <X/X MXFX BXJW?WJX.YGY :X 4Z 7W 'W 'W DX8W JW3W$W ,"
  4235. "W3W!W 'W 1W3W MW6W MW ,W ,WLY 9W ,W7W7W=W6W!W2W NW3W$W3W MW K_ DW ,W6W HXFX AWIVFVIW ^ 8W"
  4236. "FW ;Y (Y 9V LY >Q.X $T>Z?T0W8W HW5W\"W<W GW2W Y -W6W GWGX >WCX FW6W!W<W<W3WCWCW!W=W JWCWC"
  4237. "W\"W2W%W2W ?W =W4W LX6X EY 9X .Y+Y-YBXBY+X0X ZJXJZ&X'X-Y CV DY DV EY)Y DV DX 3W@S 6X 2W"
  4238. "<W L| #\\FW ASFW,S9m >XFX >V ,SBVBWCS &V \"W6W :UGU *m 8XFX .V<TGU\"V.U#V<T"
  4239. "GU;Y -z z z z z z v NX +X -X -X -X -X -X -X -X ,X+X,X>WIX(X'X/X'X/X'X/X'X/X'X KZMZ XHW6X-X"
  4240. "/X'X/X'X/X'X/X GX <u JW7Y!X8W LX8W LX8W LX8W LX8W LX8W LX8W MW ,W ,W ,W ,W )W ,W ,W ,W /W2"
  4241. "W NW6W!W2W\"W2W\"W2W\"W2W\"W2W &WGWBW NW6W MW6W MW6W MW6W GWFW CW3W JWFW FW?V?V+W(V+WKXCY"
  4242. "KV+WKX5V+W(V%W .WFX N~X'WHT =~T-~\\$q Eo\"W4W NV4V GWBW >XIW GW LX "
  4243. " ;~X JSFX 3VDV?V@W9ZJW +V \"W !V V -X6X =X 2Z IX#~ /X NX5X ?X ?X4X .X Jd D~"
  4244. "X Dd IX 8UAV8VCV>z!X3X%Y ,X,Y+X -X /Y*X+X/X'X -X -XD[ DX -XBVNVBX0X>XJX(Y)X,X /Y)X,X9Y *X "
  4245. "AX <X/X LXHX AXJV=VJX.XEY ;X 5Z 6W &V 'W DW7W JW3W$W ,W3W!W 'W 1W3W MW6W MW ,W ,WKY "
  4246. ":W ,W7W7W=W6W!W2W NW3W$W3W MW H\\ DW ,W6W GWFW @WJVDVJW!` 9WGX <Y &X 9V LX =P (T?\\@T0W8"
  4247. "X IW5W\"W<W GW2W X ,W6W FVGW >XBW FW6W!W<W<W3WCWCW!W=W JWCWCW\"W2W%W2W ?W =W4W LW4W FY 8X -X"
  4248. "+X+YCXCY*X0X N\\MXM\\%Y)X+Y DV EY NQFVFQ Y+Y CV DX 3W@S 6X 1V<V K| ![HW @TFW.T9m =XFX"
  4249. " ?V ,TCVAVDT &V \"W6W :UGU *m 9XFX -V<SFU\"V/U\"V<SFU;X ,z z z z z z v NY ,X -"
  4250. "X -X -X -X -X -X -X ,X,Y,X>XJX(Y)X.Y)X.Y)X.Y)X.Y)X KZKZ!YJW6X,X/X'X/X'X/X'X/X GX <t IW6Y\"W"
  4251. "7W LW7W LW7W LW7W LW7W LW7W LW7W MW ,W ,W ,W ,W )W ,W ,W ,W /W2W NW6W!W2W\"W2W\"W2W\"W2W\"W2W "
  4252. " &WHWAW NW6W MW6W MW6W MW6W GWGX CW3W JWGX EV?V@W*V)W*VJVAWKW*VJV5W*V)W%W .XGW M~X"
  4253. "&WJT <~S,kNn#o Cm!W4W NV4V HXBX ?XJX FW MY <~X JSEX 5VCV?V@W8ZLW "
  4254. "*W #W !V V -X6X =X 3Z HX#~ /X NX5X @Y ?X4X /X Ge G~X Ge GX 8UAV9WCU>|\"X3X$X ,"
  4255. "X,X*X -X .X*X+X/X'X -X -XC[ EX -XA\\AX0X=WJX'X)X,X .X)X,X8X *X AX <X/X LXHX AXJV=VJX/YEY ;X"
  4256. " 6Z 5W &V 'W DW7W JW3W$W ,W3W!W 'W 1W3W MW6W MW ,W ,WJY ;W ,W7W7W=W6W!W2W NW3W$W3W M"
  4257. "W EZ EW ,W6W GWFW ?WKVDVKW!b 9WHW <Y $W 9V LW BTAVNUAT/W8X IW5W#W;V FW2W!X +W6W FWIX"
  4258. " >XBX GW6W!W<W<W3WCWCW!W=W JWCWCW\"W2W%W2W ?W =W4W MX4X HY 7X -Y-Y+ZDXDZ*X0X Mt#X)X*Y EV "
  4259. "FY NSGVGS Y-Y MQFVFQ X 3W@S 6X 1W>W 9X =\\KW >SEW<PCS 6XFX @V +SCVAWES %V "
  4260. "\"W6W :UGU &XFX -V<TFU#V/U\"V<TFU<X ,|\"|\"|\"|\"|\"|\"w MX ,X -X -X -X -X -X -X -X ,X,X+X="
  4261. "WJX'X)X-X)X-X)X-X)X-X)X LZIZ!XKW5X,X/X'X/X'X/X'X/X GX <s HW5X\"W7W LW7W LW7W LW7W LW7W LW7W"
  4262. " LW7W MW ,W ,W ,W ,W )W ,W ,W ,W /W2W NW6W!W2W\"W2W\"W2W\"W2W\"W2W &WIW@W NW6W MW6W MW6W MW6W"
  4263. " FWHW BW3W IWHW DW@VAW)W+W)WJT?UKW)WJT5W)W+W$W -WHX M~X&WJT ;eMQMe+jNQNj!m Bl W4"
  4264. "W NW6W HXBX >WJX FW LX <~X JSEX 6WCV?V@W7ZMW *W #W !V !W -X6"
  4265. "X =X 4Z GX#~ /X NX5X @X >X4X /X De J~X Je DX 8U@V:WDV>|\"X3X$X ,X-Y*X -X .X*X+X/X'X -X"
  4266. " -XB[ FX -XA\\AX0X=XKX'X*Y,X .X*Y,X8Y +X AX <Y1Y KWHW ?WJV=VJW/YCY <X 7Z 4W &W (W EW6"
  4267. "W JX5X$X -X5X!X (W 0W5X MW6W MW ,W ,WIY <W ,W7W7W=W6W!X4X NX5X$X5X MW CX EW ,W6W GXHX ?WK"
  4268. "VDVKW!XNY :WIX =Y #X :V MX BUCVMVBT/W9Y IW5W#W<W FW3X!W *W6W EVIX ?X@W GW6W!W=Y=W3XDW"
  4269. "DX!W=W JWCWCW\"X4X%X4W >W <W6W LX4X HY 7X ,X-X)ZEXEZ)X0X Lr\"X)X)Y FV GY NUHVHU Y/Y MSGVGS"
  4270. " !X 3XAS 6X 0W@W 8X ;\\NW =TEX@RDT 5XFY BV +TDV@WGT %V \"W6W :UGU (YF"
  4271. "X ,V=TEU#V0U!V=TEU<X ,|\"|\"|\"|\"|\"|\"w MX ,X -X -X -X -X -X -X -X ,X-Y+X=XKX'X*Y-X*Y-X*Y-X*Y-"
  4272. "X*Y MZGZ\"XLW5Y,Y1Y'Y1Y'Y1Y'Y1Y GX <r GW4X$W6W MW6W MW6W MW6W MW6W MW6W MW6X NX -X -X -X -X"
  4273. " *W ,W ,W ,W /W2W NW6W!X4X\"X4X\"X4X\"X4X\"X4X &WIV@X NW6W MW6W MW6W MW6W FWIX BX5X IWIX "
  4274. "CWAVAW(W,W(WJR=SJW(WJR4W(W,W$W -XIX M~X&WJS :dLQLd+iMQNj!l @j NW4W NW6W HW@W >WJW DW"
  4275. " MX .VCV :SDW 6VBV?V@W6b )W #W !V !V +X8X <X 5Z FX#~ /X MW5"
  4276. "X @X >X4X /X Ad L~X Ld AX 8VAV:WDU=|\"X3X$Y -X-Y*X -X .Y+X+X/X'X -X -XA[ GX -XA\\AX0X<WK"
  4277. "X'Y+X+X .Y+Y,X7X +X AX ;X1X JXJX ?XLW=WLX/XAY =X 7Y 3W %V (W EW7X JX5W\"W ,W5X W (W 0"
  4278. "W5X MW6W MW ,W ,WHY =W ,W7W7W=W6W W4W MX5W\"W5X MW BX FW ,W6W FWHW >WKVDVKW\"XLX 9WJW =Z #X"
  4279. " :V MX AUEVKVDU/X:Y IW5W#W<W EW4W!X *W6W EVJX >X@W GW6W!W=Y=W2WDWDW W=W JWCWCW\"X4W#W4"
  4280. "W >W <W6W LW2W IY 6X ,Y/Y(ZFXFZ(X0X Kp!Y+X'Y GV HY NWIVIW Y1Y MUHVHU \"X 2WAS 6X 0YDY 8X"
  4281. " :c <TE[FUDS 3XFY CV *SDV@WGS $V \"W6W :UGU )YFX ,V=TDU$V0V\"V=TDU=X +"
  4282. "|\"|\"|\"|\"|\"|#x MY -X -X -X -X -X -X -X -X ,X-Y+X<WKX'Y+X,Y+X,Y+X,Y+X,Y+X MZEZ#YNW4X*X1X%X1X"
  4283. "%X1X%X1X FX <p EW4X$W7X MW7X MW7X MW7X MW7X MW7X MW7Y MW ,W ,W ,W ,W *W ,W ,W ,W .W4W MW6W"
  4284. " W4W W4W W4W W4W W4W $WKV?W MW6W MW6W MW6W MW6W EWJW AX5W GWJW BXBVBW'X.W'XJP;QJW'XJP"
  4285. "4W'X.W#V ,XIW L~X%WLT :dLQLc*iMQMi k ?i NW4W NW6W IX@X ?XLX DW MY "
  4286. " 0VBV :SDW 7VAV?V@X6a )W #W !V !V +X8X <X 6Z EX#~ 0Y MW5X AY >X4X 0X =d ~X"
  4287. " d LUAW<XEV>X2X#X3X#X -X.Y)X -X -X+X+X/X'X -X -X@[ HX -X@Z@X0X<XLX&X+X+X -X+X+X7Y ,X AX "
  4288. ";X1X JXJX ?XLV;VLX0YAY =X 8Z 3W %V (W EW7X JX5W\"W ,W5X W (W 0W5X MW6W MW ,W ,WGY >W "
  4289. ",W7W7W=W6W W4W MX5W\"W5X MW BX FW ,W7X FWHW >WLVBVLW#YKX :WJW =Y !W :V MW @VHXJWHV-W:"
  4290. "Y IW5W#W<W EW4W!W )W6W EWKX ?X?X HW6W!X>Y>W1WDWDW W=W JWCWCW\"X4W#W4W >W <W6W MX2X KY 5X +Y"
  4291. "1Y'[GXH\\(X0X Jn NX+X&Y HV IY NYJVJY Y3Y MWIVIW #X 2WAS 6X 0[H[ 8X :V %` :TEiET 2YGY "
  4292. " DV *TEV?WIT $V \"W6W :UGU *YGY ,V<SCU%V0V\"V<SCU=X ,X2X$X2X$X2X$X2X$X2X$X2X"
  4293. "$X8X LX -X -X -X -X -X -X -X -X ,X.Y*X<XLX&X+X+X+X+X+X+X+X+X+X NZCZ#`3X*X1X%X1X%X1X%X1X FX"
  4294. " <m BW3W$W7X MW7X MW7X MW7X MW7X MW7X MW7Y MW ,W ,W ,W ,W *W ,W ,W ,W .W4W MW6W W4W W4W W4"
  4295. "W W4W W4W 5Z IWLV>W MW7X MW7X MW7X MW7X EWJW AX5W GWJW AXCVCW%X0W%X0W%X0W%X0W\"V +WJX "
  4296. " ?X 2WLT 9bKQKb)gLQMh Mi =g MW4W MV6W IX@X ?XLX CW MX 0VBV :SDW "
  4297. "7VAV?V@X5_ (W #W !V \"W +X8X <X 7Z DX 5X 'X LX7X @X =X4X 0X ;e Le JUAW<XFV"
  4298. "=X1W#X3X#Y .X.Y)X -X -Y,X+X/X'X -X -X?[ IX -X@Z@X0X;XMX&Y-Y+X -Y-Y+X6X ,X AX ;X1X IXLX >XL"
  4299. "V;VLX1Y?Y >X 9Z 2W %W )W EW7X JX5W\"X -W5X X )W 0X7Y MW6W MW ,W ,WFY ?W ,W7W7W=W6W W4"
  4300. "W MX5W\"W5X MW AW FW ,W7X FXJX =WMVBVMW#YJY ;WKX >Y W :V MW ?dId,W;Z IW5W#W=W DW4W!W"
  4301. " )W6W DVKW >X>W HW6W W>Y>W1WDWDW W=W JWCWDX\"X4W#W4W >W ;V7W LX2X LY 4X *X1X%]JXJ]'X0X Hj L"
  4302. "Y-Y%Y IV JY LYKVKY MY5Y MYJVJY $X 2XBS 6X 2q 9X :V #\\ 7TDgFT /XFX EV )TFV>VJT #"
  4303. "V \"W6W :UGU +XFX *V=TCU%V1V!V=TCU=X ,X1W$X1W$X1W$X1W$X1W$X2X%X7X LY .X -X -X -"
  4304. "X -X -X -X -X ,X.Y*X;XMX&Y-Y+Y-Y+Y-Y+Y-Y+Y-Y ZAZ$_3Y*X1X%X1X%X1X%X1X FX <i >W3W$W7X MW7X M"
  4305. "W7X MW7X MW7X MW7X MW7Z NX -X -X -X -X +W ,W ,W ,W .W4W MW6W W4W W4W W4W W4W W4W 5Z IWMV=W"
  4306. " MW7X MW7X MW7X MW7X EWKX AX5W GWKX @XDVDX$X2X$X2X$X2X$X2X\"V +XKW ?X 1WMT 7`JQKa"
  4307. "'fLQLf Kg <f LW4W MW8W HW>W >WLW BX NY 1VBV :SDW 8V@V?V?W4] &V $W "
  4308. " V \"V *Y:Y <X 8Z DY 5X 'X KW7X @X =X5Y 1Y 8e #e GU@W>YGW>X0X$X4Y\"Y /X/Y(X -X"
  4309. " ,Y-X+X/X'X -X -X>[ JX -X@Z@X0X;XMX%Y/Y*X ,Y/Y*X6Y -X AX ;Y3Y IXLX =WLV;VLW0X=Y ?X :Z 1W $"
  4310. "V )W EW8Y JY7X\"X -X7Y X )W 0X7Y MW6W MW ,W ,WEY @W ,W7W7W=W6W X6X MY7X\"X7Y MW AW FW"
  4311. " ,X8X EWJW <WMVBVMW#XHX :WLW >Y NW :V MW >bGc,W;[ JW6X#W=W DX6X!W )W6W DVLX >W=X IW7"
  4312. "X W>Y>W1XEWEX W=W IWDWDW!Y6X#X6X >W ;W8W MX0X MY 4X *Y3Y$^LXL^&X0X Ff IY/Y#Y JV KY JYLVL"
  4313. "Y KY7Y KYKVKY #X 2XBS 6X 3t ;X :V ![ 8TCfFT .XFX FV )UGV>WKT MW7X :UGU "
  4314. " ,XFX *V=TBU&V2W!V=TBU=X -X0X&X0X&X0X&X0X&X0X&X0W%X7X KY /X -X -X -X -X -X -X -X ,X/Y)"
  4315. "X;XMX%Y/Y)Y/Y)Y/Y)Y/Y)Y/Y Z?Z$^4Y)Y3Y%Y3Y%Y3Y%Y3Y FX <X -W3W$W8Y MW8Y MW8Y MW8Y MW8Y MW8Y "
  4316. "MW8[ NX -X -X -X -X +W ,W ,W ,W .X6X MW6W X6X X6X X6X X6X X6X 5Z I_=X MX8X MX8X MX8X MX8X "
  4317. "DWLW @Y7X FWLW >XEVFY\"X5Y\"X5Y\"X5Y\"X5Y!V *WLX @X /WNT 7`JQJ_&eKQKe Je :d KW4W MW8"
  4318. "W HW>X ?XNX AX Y 1VCV 9SDW 9V?V?V?X4\\ &W %W V \"V )X:X ;X 9Z"
  4319. " CX 4X (Y KW7X AX <Y6Y 1X 4e )e DVAX@ZHW=X0X$X4Y\"Y*P&X0Z(X -X ,Y-X+X/X'X -X -X=[ K"
  4320. "X -X?X?X0X:XNX%Y/Y*X ,Y/Y*X5X .Y AX :X3X HXLX =XNW;WNX1Y=Y ?X ;Z 0W $V )W EW8Y JY7W "
  4321. "W ,W7Y NX *W /W8Z MW6W MW ,W ,WDY AW ,W7W7W=W6W NW6W LY7W W7Y MW AW FW ,X9Y EWJW <WMVBVMW"
  4322. "$XFX ;WMX ?Y MW :V MW =`Ea+X<[ JW6W\"W>W BW6W W )W6W DWMX ?X=X IX8X W?[?W0WEWEW NW=W "
  4323. "IWDWDW!Y6W!W6W =W ;W8W MX0X NY 3X )Y5Y\"z%X0X C` FY/Y\"X JV KX HYMVMY IX7X IYLVLY \"X 1XCS"
  4324. " 6X 4v <X :V [ 8TBbET ,WEW FV (T$T LX8X :UGU ,WEW )V=m,V3W V=mCX -"
  4325. "X0X&X0X&X0X&X0X&X0X&X0X&X7X KY*P&X -X -X -X -X -X -X -X ,X0Z)X:XNX%Y/Y)Y/Y)Y/Y)Y/Y)Y/Y!Z=Z"
  4326. "%]3Y(X3X#X3X#X3X#X3X EX <X -W3W$W8Y MW8Y MW8Y MW8Y MW8Y MW8Y MW8[ MW ,X -X -X -X ,W ,W ,W "
  4327. ",W -W6W LW6W NW6W MW6W MW6W MW6W MW6W 4Z H^=W LX9Y MX9Y MX9Y MX9Y DWMX @Y7W EWMX =Y8Y "
  4328. "Y8Y Y8Y Y8Y Y8Y V *WLX AX .WNT 6^IQI]$cKRJc Id 8c KW4W MX:X IX>X ?XNX AY "
  4329. " Y4P VBV 9SDW 9V?V?V?Y4Z %W %W V #W )X:X ;X :Z CY 4X (Y KX9Y AX ;X6X 1"
  4330. "Y 1e /e @U@XB[JX<X/W$X4X Y,Q&X1Z'X -X +Y.X+X/X'X -X -X<[ LX -X?X?X0X:XNX$Y1Y)X +Y1Y"
  4331. ")X5Y /X @X :X4Y GXNX <XNV9VNX2Y;Y @X ;Y /W $W *W EW9Z JZ9X X -X9Z NX *W /X9Z MW6W MW"
  4332. " ,W ,WCY BW ,W7W7W=W6W NX8X LZ9X X9Z MW AW FW +W9Y EXLX <WNV@VNW%YEX ;WNW ?Y LW :V MW "
  4333. " <^C_)W=\\ JX7W\"W>W BX8X W )W6W CVNX >W;W IX8X X@[@X0XFWEW NW=W IWDWEX!Z8X!X8X =W :W:W LX"
  4334. "0X Y 2X (Y7Y Nv#X0X ?X AY1Y V IV JV FYNVNY GV5V GYMVMY !X 1XCS 6X 5x =X :V MZ 8T?ZBT"
  4335. " *VDV FV 'T&T KX8X :UGU ,VDV )V<m-V3V NV<mCX -X/W&X/W&X/W&X/W&X/W&X0X"
  4336. "'X6X JY,Q&X -X -X -X -X -X -X -X ,X1Z(X:XNX$Y1Y'Y1Y'Y1Y'Y1Y'Y1Y!Z;Z%[3Y'X4Y#X4Y#X4Y#X4Y EX"
  4337. " <X -W3W$W9Z MW9Z MW9Z MW9Z MW9Z MW9Z MW9] NX -X -X -X -X ,W ,W ,W ,W -X8X LW6W NX8X MX8X "
  4338. "MX8X MX8X MX8X 4Z H]=X KW9Y LW9Y LW9Y LW9Y CWNW ?Z9X DWNW ;Y;Z MY;Z MY;Z MY;Z MY;Z NV "
  4339. "*XMW AY -[ 3ZHRH[\"aJRI` Fb 6a JW4W LW:W HX=W >WNX @Y !Z6Q VBV K"
  4340. "P>SEW 9V>WAW>X3Z &W %W V #V 'X<X :X ;Z BY 4X )Y IW9X AY ;Y8Y 2Y .d 1d >U?"
  4341. "ZH^MZ<X.X%X5Y NY.R&X2Z&X -X *Y/X+X/X'X -X -X;[ MX -X&X0X9a$Z3Y(X *Y3Y(X4X$P-Y @X :Y5Y GXNX"
  4342. " <XNV9VNX2X9Y AX <Z /W #V *W EX:Z JZ9X NX .X9Z MX +W .X;[ MW6W MW ,W ,WBY CW ,W7W7W="
  4343. "W6W NX9Y LZ9X X9Z MW AW FW +W:Z DWLW :^@^$XDY <WNW @Z LW :V MW ;\\@['X>\\ JX8X\"W?W AX"
  4344. "9Y X *W6W CVNX ?X;X JX9Y NW@[@W/XFWFX NW=W IXEWEX!Z8X!X8W ;W ;W;X MX.X\"Y 1X 'Y9Y Lt\"X0X ?X"
  4345. " @Y3Y MT HV IT Dj ET3T EYNVNY X 0XDS 6X 6ZM`LY >X :V LY 7T)T (UCU ET(T "
  4346. " JX9Y :UGU ,UCU )V;m.V3V NV;mCY7P HX.X(X.X(X.X(X.X(X.X(X.X(X6X IY.R&X -X -X -X -"
  4347. "X -X -X -X ,X2Z'X9a$Z3Y&Z3Y&Z3Y&Z3Y&Z3Y!Z9Z&Z3Y&Y5Y#Y5Y#Y5Y#Y5Y EX <X -W3W$X:Z MX:Z MX:Z M"
  4348. "X:Z MX:Z MX:Z MX:^ NX -X -X -X -X -W ,W ,W ,W -X8X LW6W NX9Y MX9Y MX9Y MX9Y MX9Y 4Z H\\=Y K"
  4349. "W:Z LW:Z LW:Z LW:Z CWNW ?Z9X DWNW :[@[ K[@[ K[@[ K[@[ K[@[ MV )WNX AX ,[ 1WGRFW "
  4350. "N_IRH^ Da 5_ IW4W LX<X HW<W >` >Y !Y8S MX +VBV KQ?SFX 9V=VAV=Y6] &V &W"
  4351. " NV BX 1X 1V 'Y>Y :X <Z BY 3X GP3Z IX;Y AX :Y9Z 2X GX -X 7a 1a .X 6V@iNa;X.X%X6Z N"
  4352. "Z1T&X4\\&X -X *Z0X+X/X'X -X -X:[ NX -X&X0X9a#Z5Z(X *Z5Z(X4Y%R/Y @X 9Y7Y EWNW :WNV9VNW2Y9Y A"
  4353. "X =Z .W #V *W EX;[ J[;X MY .X;[ MY2P JW .Y=\\ MW6W MW ,W ,WAY DW ,W7W7W=W6W MX:X K[;X"
  4354. " MX;[ MW /P4X FX ,X<[ DXNX :^@^%XBX <` @Y KW :V MW 8V;W%X?^ KY9X!V@X @X:X NX *W6W C_"
  4355. " >X:W JY;Z NXB]BX.XGWGX MW=W HXFWFX [:X NX:X ;W :W<W LX.X\"Y 1X &Y;Y Ip X0X ?X @Z5Z LR GV "
  4356. " HR Bh CR1R Cj NX 0YES 6X 7ZJ\\IY ?X :V KY 8U+U 'TBT DU+T IY;Z :UGU "
  4357. " ,TBT (V;m.V4V MV;mCY8Q HX.X(X.X(X.X(X.X(X.X(X.X)X5X IZ1T&X -X -X -X -X -X -X -X ,X4\\'"
  4358. "X9a#Z5Z%Z5Z%Z5Z%Z5Z%Z5Z\"Z7Z&Z5Z%Y7Y!Y7Y!Y7Y!Y7Y DX <X -W4X$X;[ MX;[ MX;[ MX;[ MX;[ MX;[ MX"
  4359. ";`3P=Y .Y2P LY2P LY2P LY2P LW ,W ,W ,W ,X:X KW6W MX:X KX:X KX:X KX:X KX:X 3Z GZ<X JX<[ LX<"
  4360. "[ LX<[ LX<[ C` ?[;X C` 9_J_ I_J_ I_J_ I_J_ I_J_ LV )` AX +Z S <[GRFZ A_ 4^ HW4W"
  4361. " KX>X HX<X ?` =Z \"Y:T MX +VCV JSASFX :V<VAV<Y8_ 'W 'W NV BX 1X 2W"
  4362. " &X>X 9X =Z 1P2Z 3X GQ5Z GX=Y @X 9Y:Y KP8Z GX -X 4^ 1^ +X 5U?gM_9W,W%X7Z L[4U&X6]%X -X )"
  4363. "[2X+X/X'X -X -X9[ X -X&X0X8`\"Z7Z'X )Z7Z'X3X%T2Y ?X 9Z9Z E` :_9_3Y7Y BX >Z -W #W +W D"
  4364. "X=\\ J\\=Y LY7P HY=\\ LY5R JW -Y?] MW6W MW ,W ,W@Y EW ,W7W7W=W6W MY<Y K\\=Y MY=\\ MW /R6W DW ,Y"
  4365. "=[ CWNW 9^@^&X@X <^ @Y JW :V MW HXA` LZ;X V@W ?Y<Y MX +W6W B^ ?X9W JZ<Z NXB]BX.YHW"
  4366. "HY MW=W HYGWGY \\<Y NY<X :W :X>X LX.X#Y 0X %Y=Z Gl MX0X ?X ?Z7Z JP FV GP @f AP/P Ah MX "
  4367. "/YFSDP BX 8ZFVEY @X :V JX 7V.U %SAS CU.U HZ<Z :UGU ,SAS (V:m/V5W"
  4368. " MV:mBY;S HW,W(W,W(W,W(W,W(W,W(X.X)X5X H[4U&X -X -X -X -X -X -X -X ,X6]&X8`\"Z7Z#Z7Z#Z7Z#Z7"
  4369. "Z#Z7Z\"Z5Z&[8Z$Z9Z!Z9Z!Z9Z!Z9Z DX <X -W4W\"X=\\ LX=\\ LX=\\ LX=\\ LX=\\ LX=\\ LX=b6R<Y7P GY5R KY5R"
  4370. " KY5R KY5R LW ,W ,W ,W ,Y<Y KW6W MY<Y KY<Y KY<Y KY<Y KY<Y 3Z GY<Y JY=[ LY=[ LY=[ LY=[ B^ >"
  4371. "\\=Y B^ 7r Gr Gr Gr Gr KV (_ BX )Y S 8RBSCR <] 2\\ GW4W KZBZ HX;W >_ <[ "
  4372. " $[=U MX ,VBV JUCSHY :V;WCW<[<b (W 'W NV BX 1X 2W &Y@Y 9X >Z 0R5Z 2X GT9[ G"
  4373. "Y?Z AY 9[>[ KR;Z FX -X 1[ 1[ (X 5V>dL^9X,X&X9[ J[7W&X9_$X -X (\\6Z+X/X'X -X -X8[!X -X&X0X"
  4374. "8`![;[&X ([;[&X3Y&W7[ ?X 8Z;Z D` :^7^3X5Y CX ?Z ,W #W +W DY?] J]?Y KZ:R GY?] LZ8T JW"
  4375. " -ZA^ MW6W MW ,W ,W?Y FW ,W7W7W=W6W LY>Y J]?Y KY?] MW /T9X DX ,Y@] CWNW 9]>]'Y@Y =^ AY IW"
  4376. " :V MW HYCXNW L\\>Y VAX >Y>Y LY ,W6W B] >X9X K[>[ MXDVMVDX,YIWIY LW=W GYHWHY N]>Y LY"
  4377. ">Y :X :X@X LX,X%Y /X $ZAZ Ch KX0X ?X >[;[ ?V 6d >f LX /[HSFR BX 9Z3Y AX :V IX 7"
  4378. "V1V #R@R BU0U G[>[ :UGU ,R@R 'V(U)V6W LV(U<Z>U IX,X*X,X*X,X*X,X*X,X"
  4379. "*X,X*W4X G[7W&X -X -X -X -X -X -X -X ,X9_%X8`![;[![;[![;[![;[![;[\"Z3Z(];[\"Z;Z NZ;Z NZ;Z NZ"
  4380. ";Z CX <X -WJP;X\"Y?] LY?] LY?] LY?] LY?] LY?] LY?XNZ9T<Z:R GZ8T KZ8T KZ8T KZ8T LW ,W ,W ,W "
  4381. "+Y>Y JW6W LY>Y IY>Y IY>Y IY>Y IY>Y 2Z FY>Y HY@] KY@] KY@] KY@] B^ >]?Y A^ 6o Do Do Do "
  4382. "Do IV (_ CX (Y S (S ,[ 0[ GW4W J\\H\\ GW:W >^ :\\ %[@W MX ,VBV JXFS"
  4383. "IZ :V:WEW:\\@e (V 'V MV BX 1X 2V $ZDZ 8X ?Z /U;] 2X GV=\\ EZC[ @X 7[@[ JT?[ EX -X /Y "
  4384. " 1Y &X 5V=bK\\7X,X&X<^ I]=Z&X=b#X -X ']:\\+X/X'X -X -X7[\"X -X&X0X7_ \\?\\%X '\\?\\%X2X&Z<\\ >X 7["
  4385. "?[ B^ 9^7^4Y5Y CX ?Y +W \"V +W DZB_ J_CZ I[>T G[C_ K[=W JW ,\\GXNW MW6W MW ,W ,W>Y GW "
  4386. ",W7W7W=W6W KZBZ I_CZ J[C_ MW /W>Z DZ .ZB^ C` 8\\>\\&X>Y =\\ AY HW :V MW GZFYNY N]AZ N"
  4387. "WCX <ZBZ JZ:Q EW6W B] ?X7W K\\A^ NYFWMWFY,ZJWJY KW=X H[JWJ[ N_BZ JZBZ 8Y <ZDZ LX,X&Y .X #ZC"
  4388. "Z >_ FX0X ?X =\\?\\ >V 5b <d KX .\\JSHT BX 8X2X @X :V IX 5V4U Q?Q AV4V "
  4389. " F\\A^ ;UGU ,Q?Q 'V'U*V6W LV'U<[AW IX,X*X,X*X,X*X,X*X,X*X,X+X4X F]=Z&X -X -X -X"
  4390. " -X -X -X -X ,X=b$X7_ \\?\\ N\\?\\ N\\?\\ N\\?\\ N\\?\\ X1X(`?\\ [?[ L[?[ L[?[ L[?[ BX <X -WJS@Z\"ZB_ "
  4391. "LZB_ LZB_ LZB_ LZB_ LZB_ LZBYM\\>W;[>T F[=W J[=W J[=W J[=W LW ,W ,W ,W *ZBZ IW6W KZBZ GZBZ "
  4392. "GZBZ GZBZ GZBZ 1Z F[BZ GZB^ KZB^ KZB^ KZB^ A\\ =_CZ ?\\ 3l Al Al Al Al HV (^ BX (X "
  4393. " NS (S ,Z .Y FW4W In GX:X ?^ 9_ (]FZ MX ,VBV J[ISL\\ :V9XGX9^Fi )W )W "
  4394. " MV BX 1X 3W #[H[ Et Mx MZC_ 1X GZD^ C[G\\ @Y 7^F] IXF] DX -X ,V 1V #X 4V<^IY5X*X'y G"
  4395. "_D^&{!y NX &`B`+X/X'X -X -X6[#w LX&X0X7_ N^E^$X &^E^$X2Y'^C^ =X 7^E^ B^ 8]7]4Y3Y DX @~U&W "
  4396. "\"W ,W C\\HYNW JWNXG\\ H]EX F\\GXNW J]D[ JW +kMW MW6W MW ,W ,W=Y HW ,W7W7W=W6W K]H] IWNX"
  4397. "G\\ I\\GXNW MW /[E\\ Be 9[GXNW B^ 7\\>\\'X<X =\\ AX GW :V MW G\\IYM^$`F\\ MWEX ;]H] J]BV E"
  4398. "W6W A\\ ?X7X L_GaKP#ZJYMYJZ*[LWL[ KW=Y H\\LWL\\ MWNXG] J]H\\ 7a C[H[ L~W'x MX 1iEi HX CX0X ?X "
  4399. "<^E^ =V 4` :b JX -^MSLX Lz V0V ?X :V HW 4V7V MP>P @W8W 3~W :_GaKP"
  4400. " @UGU ,P>P 'V&U+V6V KV&U;]GZ JX*X,X*X,X*X,X*X,X*X,Y,Y,X4y7_D^&y Ny Ny Ny NX -X -X -"
  4401. "X ,{\"X7_ N^E^ L^E^ L^E^ L^E^ L^E^ MV/V(dE^ N^E^ L^E^ L^E^ L^E^ BX <X -WJWF[ \\HYNW K\\HYNW K"
  4402. "\\HYNW K\\HYNW K\\HYNW K\\HYNW K\\H[K^E[:]EX E]D[ I]D[ I]D[ I]D[ LW ,W ,W ,W )[F[ HW6W K]H] G]H"
  4403. "] G]H] G]H] G]H] 1Z F]G] F[GXNW J[GXNW J[GXNW J[GXNW A\\ =WNXG\\ ?\\ 1h =h =h =h =h FV ']"
  4404. " AV &W T )T +X -X EW4W Hl FX9W ?^ 8~R Jp MX ,VCV It 9V8XIX7sLZ "
  4405. "*W )W MV BX 1X 3W #n Et Mx Mu 0X Gs Ao @X 5t In CX -X )S 1S X 4V9XFU1X*X'x Ex&z y "
  4406. "NX %|*X/X'X -X -X5[$w LX&X0X6^ Mu#X %u#X1X'y =X 6u A^ 8]7]4X1X DX @~U&W \"W ,W ClMW J"
  4407. "WMk Fo EkMW Is JW *jMW MW6W MW ,W ,W<Y IW ,W7W7W=W6W Jp HWMk GkMW MW /q Ae 9kMW B^ 7\\=[(Y;"
  4408. "X >\\ Av 6W :V MW FkL]$u LXGX 9p Hp EW6W A[ ?X6X LpN\\#hKh)s JW<] Lu LWNm Hp 6` Bl K~"
  4409. "W'x MX 1iEi HX CX0X ?X ;u <V 3^ 8` IX ,o Lz NT.T >X :V HW 3X=X )X<X 2"
  4410. "~W :pN\\ @UGU V&U+V7i.V&U:o JX*X,X*X,X*X,X*X,X*X,X*X-X3y6x&y Ny Ny Ny NX -X "
  4411. "-X -X ,z!X6^ Mu Ju Ju Ju Ju KT-T(} Lu Ju Ju Ju AX <X -WJk NlMW KlMW KlMW KlMW KlMW KlMW Kn"
  4412. "Is9o Ds Hs Hs Hs LW ,W ,W ,W )p HW6W Jp Ep Ep Ep Ep Ls EkMW JkMW JkMW JkMW A\\ =WMk >\\ "
  4413. " /c 8c 8c 8c 8c CV '\\ ?T %W U *T *W ,V DW4W Gj EW8W >\\ 5~P In LX "
  4414. " -VBV Is 9V7g6qJZ *V )V LV BX 1X 3V !l Dt Mx Mt /X Gr ?m ?X 4r Hm BX -X &P 1P LX"
  4415. " 3V 3X*X'w Cv%x My NX #x(X/X'X -X -X4[%w LX&X0X5] Ls\"X $s\"X1Y(w ;X 5s ?\\ 7\\5\\5Y1Y EX @~U&W"
  4416. " !V ,W BjLW JWMj Dn DjMW Hr JW )hLW MW6W MW ,W ,W;Y JW ,W7W7W=W6W In GWMj EjMW MW /p"
  4417. " ?d 8iLW B^ 6Z<[)Y:Y >Z @v 6W :V MW EiK]$t JYLZ 7n Fo EW6W A[ ?X5W LWNfM\\\"gKg'q IW<"
  4418. "] Ks KWMk Fn 5` Aj J~W'x MX 1iEi HX CX0X ?X :s ;V 2\\ 6^ HX +n Lz MR,R =X :V HW "
  4419. "1ZEZ %ZDZ 0~W :WNfM\\ @UGU !V%U,V6i/V%U9n JX*X,X*X,X*X,X*X,X*X,X*X-"
  4420. "X3y5v%y Ny Ny Ny NX -X -X -X ,x NX5] Ls Hs Hs Hs Hs IR+R(WMs Js Hs Hs Hs @X <X -WJk MjLW J"
  4421. "jLW JjLW JjLW JjLW JjLW JmHr8n Cr Gr Gr Gr LW ,W ,W ,W (n GW6W In Cn Cn Cn Cn Ls CiLW Ii"
  4422. "LW IiLW IiLW @Z <WMj <Z +] 2] 2] 2] 2] @V &[ >R $V NU *U *U *U DW4W Fh DW8X ?\\ "
  4423. "4~ Hl KX -VBV Hp 8V5e4nGZ +W +W LV BX 1X 3V j Ct Mx Mr -X Gq =j "
  4424. ">Y 3p Gl AX -X 2X 3W 5X(X(u ?s$v Ky NX \"v'X/X'X -X -X3[&w LX&X0X5] Kq!X #p X0X(v :X "
  4425. "4p =\\ 7\\5\\6Y/Y FX @~U&W !V ,W AhKW JWLh Bm ChLW Gq JW (eJW MW6W MW ,W ,W:Y KW ,W7W7W"
  4426. "=W6W Hl FWLh ChLW MW /o >d 7gKW A\\ 5Z<Z(X8X >Z @v 6W :V MW DgI\\$s He 5l Dn EW6W @Y "
  4427. ">W4X MWMeM\\!eIe%o HW<] Jq JWLi Dk 2_ @h J~Y(x MX 1iEi HX CX0X ?X 9q :V 1Z 4\\ GX *m"
  4428. " Lz LP*P <X :V HW 0m \"l .~W :WMeM\\ @UGU !V%U,V6i/V%U8l JX(X.X(X"
  4429. ".X(X.X(X.X(X.Y)X/X2y3s$y Ny Ny Ny NX -X -X -X ,v LX5] Kq Fq Fq Fq Fq GP)P'VKp Gp Ep Ep Ep "
  4430. ">X <X -WJj KhKW IhKW IhKW IhKW IhKW IhKW IjEq7m Bq Fq Fq Fq LW ,W ,W ,W &j EW6W Hl Al Al A"
  4431. "l Al Ls AgKW HgKW HgKW HgKW @Z <WLh ;Z MV &[ =P \"U V +V )S (S CW4W "
  4432. "De DX8X ?\\ 2| Fh IX -VBV Ek 6V4c1kEZ +V +V KV BW 0X 4W Mf At Mx "
  4433. "Mq ,X Go :h =X 0l Ej ?X -W 1X 2W 6X(X(s ;o\"s Hy NX r%X/X'X -X -X2['w LX&X0X4\\ Im NX"
  4434. " !m NX0Y(t 9X 2m ;Z 5[5[5X-X FX @~U&W !W -W @fJW JWJe ?j AeJW En IW 'cIW MW6W MW ,W "
  4435. ",W9Y LW ,W7W7W=W6W Fh DWJe AeJW MW .m ;b 6eJW A\\ 5Z<Z)X6X >X ?v 6W :V MW CeG[$r Fc "
  4436. "2h Am EW6W @Y ?X3W MWMdL\\ cGc#m GW;\\ Hm HWKg Ah /] ?f I~Y(x MX 1iEi HX CX0X ?X 7m 8V 0"
  4437. "X 2Z FX (j Kz AX :V HW -g Lh ,~W :WMdL\\ @UGU \"V$U-V5i0V$"
  4438. "U7i HX(X.X(X.X(X.X(X.X(X.X(X/X2y1o\"y Ny Ny Ny NX -X -X -X ,t JX4\\ Im Bm Bm Bm Bm %VHm Dm "
  4439. "Bm Bm Bm =X <X -WJh HfJW HfJW HfJW HfJW HfJW HfJW HhBn4j ?n Cn Cn Cn KW ,W ,W ,W %h DW6W F"
  4440. "h =h =h =h =h KVMi >eJW GeJW GeJW GeJW ?X ;WJe 9X MW &Z =U W ,W *"
  4441. "R &Q BW4W B` AW6W >[ /y Dd GX -VCV Af 5V2a.gBZ ,W -W KV CX 0X 4V "
  4442. " Kd @t Mx Km *X Ek 6d ;X .h Bh >X .X 1X 1W 7X(X(q 7j Np Ey NX Mm\"X/X'X -X -X1[(w LX"
  4443. "&X0X4\\ Gi LX Ni LX/X$n 7X 0i 9Z 5[5[6Y-Y GX @~U&W V -W >cIW JWIb <g =bIW Ci FW %_G"
  4444. "W MW6W MW ,W ,W8Y MW ,W7W7W=W6W Ef CWIb =bIW MW +h 8a 5cIW @Z 4Y:Y*Y5X ?X ?v 6W :V MW "
  4445. " AbDY$WMf Ca 0f >k EW6W @Y ?W2W MWK`I[ NaEa i EW;\\ Fi FWIc >e ,\\ =b G~Y(x MX 1iEi HX CX0"
  4446. "X ?X 5i 6V /V 0X EX &f Iz AX :V /P;W *c Gb )~W :WK`I[ @UGU "
  4447. " #V#U.V4i1V#U6f FX(X.X(X.X(X.X(X.X(X.X(X/X2y/j Ny Ny Ny Ny NX -X -X -X ,p FX4\\ Gi >i "
  4448. ">i >i >i $VEi @i >i >i >i ;X <X -WIf EcIW FcIW FcIW FcIW FcIW FcIW Fd>i0g ;i >i >i >i HW "
  4449. ",W ,W ,W #d BW6W Ef ;f ;f ;f ;f JUJe ;cIW FcIW FcIW FcIW ?X ;WIb 7X MW %Y "
  4450. " =T X -X )P %P AW4W ?Z >W6X ?Z ,w B` EX .VBV <] 1V0]*b?[ -W -W"
  4451. " KV CW /X 4V I` >t Mx Hg 'X Bf 2` :X +d =b ;X .W 0X 1X 9X&X)m 0d Kj ?y NX Jg "
  4452. "NX/X'X -X -X0[)w LX&X0X3[ Dc IX Kf LX/Y!g 4X .e 7Z 5Z3Z7Y+Y HX @~U&W V -W =`GW JWG"
  4453. "^ 7b 9^GW Ad CW \"YDW MW6W MW ,W ,W7Y NW ,W7W7W=W6W B` @WG^ 9^GW MW (c 2] 3_GW @Z 3X:X*Y4Y "
  4454. "@X ?v 6W :V MW ?_AW$WKb @^ +` 9g CW6W ?W ?X2X NWJ^GY K]B^ Ke CW:[ Dd CWG_ 9` 'Y ;^ "
  4455. "F~[)x MX 1iEi HX CX0X ?X 2c 3V .T .V DX $b Gz AX :V /R>X &[ ?Z %~W "
  4456. " :WJ^GY ?UGU #V +V +V 1b EX&X0X&X0X&X0X&X0X&X0Y'X1X1y,d Ky Ny Ny Ny NX -X -X "
  4457. "-X ,j @X3[ Dc 8c 8c 8c 8c !VBc ;e :e :e :e 9X <X -WFa B`GW E`GW E`GW E`GW E`GW E`GW D`:d*"
  4458. "b 7d 9d 9d 9d EW ,W ,W ,W !` @W6W B` 5` 5` 5` 5` HVHa 7_GW D_GW D_GW D_GW ?X ;WG^ 5X "
  4459. " MW 7S @r >Y BS .V,W#Z ;V -V "
  4460. " 7W ;W EX ;\\ 6] +Z 5\\ 5Z <W 7X %\\ <] \"X ([ "
  4461. " 4c E] /[ (W W .W :Y #X 0Z 2X *\\ $W &W .Z =WDX 3XDW I["
  4462. " 0Y 8W -W :V MW <Z ;WH[ 9Y &Z 1] LW ?W >WGXBU FX=X E` \"W >] @WDY 3Z "
  4463. "2X C[ >T :[ KV /TAY EWGXBU =UGU"
  4464. " BT 6V +V +V ,Y ?\\ +[ 0[ 0[ 0[ 0[ KT=[ 2[ 0[ 0["
  4465. " 0[ 7Z ;Y .Y .Y .Y .Y .Y -Y2\\\"Z /\\ 1\\ 1\\ 1\\ CZ 3Z /Z /Z /Z /Z FVCZ 1Y .Y ."
  4466. "Y .Y ,W :WDX 2W LW 7R #S"
  4467. " >W /W 8W :V \"W 5X )X "
  4468. " &Z CW NV .W :W %W @W :W "
  4469. " -X -W :V MW LW FW ?W >W NW 0W =W "
  4470. " 3S GV /XGZ DW HUGU AT %"
  4471. "T 'R JT "
  4472. " #T (X :W NX LW "
  4473. " 7S =V /V 7W :V \"W 4X'Q "
  4474. "&Y %Z DW NV .W :W %W @W :W "
  4475. " -W ,W :V MW LW FW ?W >W NW 0W =W "
  4476. " 3S GV /j CW HUGU @T "
  4477. " %T 'P HT "
  4478. " \"Q 'W 9W NW KW "
  4479. " 7S =W 1W 7V :W \"V 2X)R "
  4480. " &X #Z EW NW /W :W %W "
  4481. " @W :W -W ,X ;V NX LW FW ?W >W NW 0W =W "
  4482. " 3S GV /j CW HUGU @U "
  4483. " &U U "
  4484. " \"P 'W 9W NW KV "
  4485. " 6S <V 1V 6V :V !V 1Y-U "
  4486. " 'X \"Z FW MV /W ;X %W "
  4487. " @W :W .X +W ;V NW KW FW ?W >W NW 0W =W "
  4488. " 3S GV /h AW HUGU ?T "
  4489. " %T NT "
  4490. " )X 9W X KV "
  4491. " 6S <W 3V 6V 9V \"V "
  4492. " /Z1X (X !Z Ga (V 9a ;W "
  4493. "$W @W :W .W *W ;V NW KW FW ?W >W NW 0W =W"
  4494. " 3S GV .f @W HUGU ?"
  4495. "U &U "
  4496. " U *W 8W W JV "
  4497. " 6S ;V 3V 6V :W \"V "
  4498. " .[5[ *Y Z Ha (W :a <X"
  4499. " $W @W :W /X *X <V X KW FW ?W >W NW 0W"
  4500. " =W 3S GV +a >W HUGU "
  4501. " >T %T "
  4502. " NT +X 8W !X (VIV "
  4503. " 6S :V 5V 5U 9W \""
  4504. "U +\\;] )X MZ Ia (W :a "
  4505. " =Y %W ?W :W /W )[ ?V #[ KW FW ?W >W N"
  4506. "W 0W =W 3S GV 'Z ;W "
  4507. " HUGU >U &U "
  4508. " U ,W 7W !W 'VIV "
  4509. " 6S :V 6W 6V "
  4510. " 4V *_C` )Y LZ Ja :a "
  4511. " (P7Y $W ?W :W 0X (b GV +b JW FW ?W >W "
  4512. " NW 0W =W 3S GV "
  4513. "7W HUGU >U &U "
  4514. " U -X 7W \"X 'VJW "
  4515. " 6S 9V 7V 5U "
  4516. " 3U 'x (Z KZ Ka :a "
  4517. " (R:Z $W ?W :W 0X (b GV +b JW FW ?W >W"
  4518. " NW 0W =W 3S GV "
  4519. " 7W #U &U "
  4520. " U -X 7W \"X &UJW "
  4521. " 6S 9W 9W "
  4522. " Bu ([ IZ La :a "
  4523. " (T>[ $X ?W :W 1X &a GV +a IW FW ?W >W N"
  4524. "W 0W =W 3S GV 7W "
  4525. " $V 'V "
  4526. " !V .X 6W #X %VLW "
  4527. " 5S "
  4528. " 2p -a 8XE] %Y"
  4529. " >W :W 3Z $_ GV +_ GW FW ?W >W NW 0W =W "
  4530. " 3S GV 7W /QGW "
  4531. " 2QGW ,QG"
  4532. "W 0Z 6W %Z %a "
  4533. " 5S 0l "
  4534. " +a 8p +_ "
  4535. " >W :W ;a !] GV +] EW FW ?W >W NW 0W =W "
  4536. " 3S GV 7W /` "
  4537. " 1` +` "
  4538. " 7a 5W -a #` "
  4539. " >e '`"
  4540. " 7o *^ =W :W "
  4541. " ;` KY GV +Y AW FW ?W >W NW 0W =W "
  4542. " 3S GV 7W /` 1` "
  4543. " +` "
  4544. " 7` 4W -` \"_ "
  4545. " 8\\ #_ "
  4546. " \"} 3n )^ =W :W ;` 9V "
  4547. " BW FW ?W >W NW 0W =W 'V "
  4548. " 7W /_ 0_ "
  4549. " *_ 6` 4W -` "
  4550. " !] "
  4551. " -] "
  4552. " } 3l '] <W :W ;_ 8V BW FW ?"
  4553. "W >W NW 0W =W 'V "
  4554. " 7W /^ /^ "
  4555. " )^ 5_ 3W -_ N[ "
  4556. " "
  4557. " ,[ M} 2j "
  4558. " &\\ ;W :W ;^ 7V BW FW ?W >W NW 0W =W"
  4559. " 7W -Y "
  4560. " *Y $Y "
  4561. " 2^ 2W -^ LX "
  4562. " "
  4563. " *X J} /d #Z 9W :"
  4564. "W ;\\ 5V BW FW ?W >W NW 0W =W "
  4565. " 7W "
  4566. " "
  4567. " /\\ 0W HT "
  4568. " "
  4569. " I} *[ NW 6W :W ;Z 3V "
  4570. " BW FW ?W >W NW 0W =W "
  4571. " 7W "
  4572. " /Z .W "
  4573. " "
  4574. " =} "
  4575. " "
  4576. " "
  4577. " "
  4578. " D" };
  4579. // Define a 40x38 'danger' color logo (used by cimg::dialog()).
  4580. static const unsigned char logo40x38[4576] = {
  4581. 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,
  4582. 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,
  4583. 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,
  4584. 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,
  4585. 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,
  4586. 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,
  4587. 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,
  4588. 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,
  4589. 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,
  4590. 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,
  4591. 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,
  4592. 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,
  4593. 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,
  4594. 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,
  4595. 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,
  4596. 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,
  4597. 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,
  4598. 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,
  4599. 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,
  4600. 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,
  4601. 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,
  4602. 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,
  4603. 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,
  4604. 123,123,123,86,200,200,200,91,49,124,118,124,71,32,124,95,49,56,114,52,82,121,0 };
  4605. //! Get/set default output stream for the \CImg library messages.
  4606. /**
  4607. \param file Desired output stream. Set to \c 0 to get the currently used output stream only.
  4608. \return Currently used output stream.
  4609. **/
  4610. inline std::FILE* output(std::FILE *file) {
  4611. cimg::mutex(1);
  4612. static std::FILE *res = cimg::_stderr();
  4613. if (file) res = file;
  4614. cimg::mutex(1,0);
  4615. return res;
  4616. }
  4617. // Return number of available CPU cores.
  4618. inline unsigned int nb_cpus() {
  4619. unsigned int res = 1;
  4620. #if cimg_OS==2
  4621. SYSTEM_INFO sysinfo;
  4622. GetSystemInfo(&sysinfo);
  4623. res = (unsigned int)sysinfo.dwNumberOfProcessors;
  4624. #elif cimg_OS == 1
  4625. res = (unsigned int)sysconf(_SC_NPROCESSORS_ONLN);
  4626. #endif
  4627. return res?res:1U;
  4628. }
  4629. // Lock/unlock mutex for CImg multi-thread programming.
  4630. inline int mutex(const unsigned int n, const int lock_mode) {
  4631. switch (lock_mode) {
  4632. case 0 : cimg::Mutex_attr().unlock(n); return 0;
  4633. case 1 : cimg::Mutex_attr().lock(n); return 0;
  4634. default : return cimg::Mutex_attr().trylock(n);
  4635. }
  4636. }
  4637. //! Display a warning message on the default output stream.
  4638. /**
  4639. \param format C-string containing the format of the message, as with <tt>std::printf()</tt>.
  4640. \note If configuration macro \c cimg_strict_warnings is set, this function throws a
  4641. \c CImgWarningException instead.
  4642. \warning As the first argument is a format string, it is highly recommended to write
  4643. \code
  4644. cimg::warn("%s",warning_message);
  4645. \endcode
  4646. instead of
  4647. \code
  4648. cimg::warn(warning_message);
  4649. \endcode
  4650. if \c warning_message can be arbitrary, to prevent nasty memory access.
  4651. **/
  4652. inline void warn(const char *const format, ...) {
  4653. if (cimg::exception_mode()>=1) {
  4654. char *const message = new char[16384];
  4655. std::va_list ap;
  4656. va_start(ap,format);
  4657. cimg_vsnprintf(message,16384,format,ap);
  4658. va_end(ap);
  4659. #ifdef cimg_strict_warnings
  4660. throw CImgWarningException(message);
  4661. #else
  4662. std::fprintf(cimg::output(),"\n%s[CImg] *** Warning ***%s%s\n",cimg::t_red,cimg::t_normal,message);
  4663. #endif
  4664. delete[] message;
  4665. }
  4666. }
  4667. // Execute an external system command.
  4668. /**
  4669. \param command C-string containing the command line to execute.
  4670. \param module_name Module name.
  4671. \return Status value of the executed command, whose meaning is OS-dependent.
  4672. \note This function is similar to <tt>std::system()</tt>
  4673. but it does not open an extra console windows
  4674. on Windows-based systems.
  4675. **/
  4676. inline int system(const char *const command, const char *const module_name=0, const bool is_verbose=false) {
  4677. cimg::unused(module_name);
  4678. #ifdef cimg_no_system_calls
  4679. return -1;
  4680. #else
  4681. if (is_verbose) return std::system(command);
  4682. #if cimg_OS==1
  4683. const unsigned int l = (unsigned int)std::strlen(command);
  4684. if (l) {
  4685. char *const ncommand = new char[l + 24];
  4686. std::memcpy(ncommand,command,l);
  4687. std::strcpy(ncommand + l," >/dev/null 2>&1"); // Make command silent.
  4688. const int out_val = std::system(ncommand);
  4689. delete[] ncommand;
  4690. return out_val;
  4691. } else return -1;
  4692. #elif cimg_OS==2
  4693. PROCESS_INFORMATION pi;
  4694. STARTUPINFO si;
  4695. std::memset(&pi,0,sizeof(PROCESS_INFORMATION));
  4696. std::memset(&si,0,sizeof(STARTUPINFO));
  4697. GetStartupInfo(&si);
  4698. si.cb = sizeof(si);
  4699. si.wShowWindow = SW_HIDE;
  4700. si.dwFlags |= SW_HIDE | STARTF_USESHOWWINDOW;
  4701. const BOOL res = CreateProcess((LPCTSTR)module_name,(LPTSTR)command,0,0,FALSE,0,0,0,&si,&pi);
  4702. if (res) {
  4703. WaitForSingleObject(pi.hProcess,INFINITE);
  4704. CloseHandle(pi.hThread);
  4705. CloseHandle(pi.hProcess);
  4706. return 0;
  4707. } else return std::system(command);
  4708. #else
  4709. return std::system(command);
  4710. #endif
  4711. #endif
  4712. }
  4713. //! Return a reference to a temporary variable of type T.
  4714. template<typename T>
  4715. inline T& temporary(const T&) {
  4716. static T temp;
  4717. return temp;
  4718. }
  4719. //! Exchange values of variables \c a and \c b.
  4720. template<typename T>
  4721. inline void swap(T& a, T& b) { T t = a; a = b; b = t; }
  4722. //! Exchange values of variables (\c a1,\c a2) and (\c b1,\c b2).
  4723. template<typename T1, typename T2>
  4724. inline void swap(T1& a1, T1& b1, T2& a2, T2& b2) {
  4725. cimg::swap(a1,b1); cimg::swap(a2,b2);
  4726. }
  4727. //! Exchange values of variables (\c a1,\c a2,\c a3) and (\c b1,\c b2,\c b3).
  4728. template<typename T1, typename T2, typename T3>
  4729. inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3) {
  4730. cimg::swap(a1,b1,a2,b2); cimg::swap(a3,b3);
  4731. }
  4732. //! Exchange values of variables (\c a1,\c a2,...,\c a4) and (\c b1,\c b2,...,\c b4).
  4733. template<typename T1, typename T2, typename T3, typename T4>
  4734. inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4) {
  4735. cimg::swap(a1,b1,a2,b2,a3,b3); cimg::swap(a4,b4);
  4736. }
  4737. //! Exchange values of variables (\c a1,\c a2,...,\c a5) and (\c b1,\c b2,...,\c b5).
  4738. template<typename T1, typename T2, typename T3, typename T4, typename T5>
  4739. inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5) {
  4740. cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4); cimg::swap(a5,b5);
  4741. }
  4742. //! Exchange values of variables (\c a1,\c a2,...,\c a6) and (\c b1,\c b2,...,\c b6).
  4743. template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
  4744. 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) {
  4745. cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5); cimg::swap(a6,b6);
  4746. }
  4747. //! Exchange values of variables (\c a1,\c a2,...,\c a7) and (\c b1,\c b2,...,\c b7).
  4748. template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
  4749. 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,
  4750. T7& a7, T7& b7) {
  4751. cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6); cimg::swap(a7,b7);
  4752. }
  4753. //! Exchange values of variables (\c a1,\c a2,...,\c a8) and (\c b1,\c b2,...,\c b8).
  4754. template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
  4755. 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,
  4756. T7& a7, T7& b7, T8& a8, T8& b8) {
  4757. cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6,a7,b7); cimg::swap(a8,b8);
  4758. }
  4759. //! Return the endianness of the current architecture.
  4760. /**
  4761. \return \c false for <i>Little Endian</i> or \c true for <i>Big Endian</i>.
  4762. **/
  4763. inline bool endianness() {
  4764. const int x = 1;
  4765. return ((unsigned char*)&x)[0]?false:true;
  4766. }
  4767. //! Reverse endianness of all elements in a memory buffer.
  4768. /**
  4769. \param[in,out] buffer Memory buffer whose endianness must be reversed.
  4770. \param size Number of buffer elements to reverse.
  4771. **/
  4772. template<typename T>
  4773. inline void invert_endianness(T* const buffer, const cimg_ulong size) {
  4774. if (size) switch (sizeof(T)) {
  4775. case 1 : break;
  4776. case 2 : {
  4777. for (unsigned short *ptr = (unsigned short*)buffer + size; ptr>(unsigned short*)buffer; ) {
  4778. const unsigned short val = *(--ptr);
  4779. *ptr = (unsigned short)((val>>8) | ((val<<8)));
  4780. }
  4781. } break;
  4782. case 4 : {
  4783. for (unsigned int *ptr = (unsigned int*)buffer + size; ptr>(unsigned int*)buffer; ) {
  4784. const unsigned int val = *(--ptr);
  4785. *ptr = (val>>24) | ((val>>8)&0xff00) | ((val<<8)&0xff0000) | (val<<24);
  4786. }
  4787. } break;
  4788. case 8 : {
  4789. const cimg_uint64
  4790. m0 = (cimg_uint64)0xff, m1 = m0<<8, m2 = m0<<16, m3 = m0<<24,
  4791. m4 = m0<<32, m5 = m0<<40, m6 = m0<<48, m7 = m0<<56;
  4792. for (cimg_uint64 *ptr = (cimg_uint64*)buffer + size; ptr>(cimg_uint64*)buffer; ) {
  4793. const cimg_uint64 val = *(--ptr);
  4794. *ptr = (((val&m7)>>56) | ((val&m6)>>40) | ((val&m5)>>24) | ((val&m4)>>8) |
  4795. ((val&m3)<<8) |((val&m2)<<24) | ((val&m1)<<40) | ((val&m0)<<56));
  4796. }
  4797. } break;
  4798. default : {
  4799. for (T* ptr = buffer + size; ptr>buffer; ) {
  4800. unsigned char *pb = (unsigned char*)(--ptr), *pe = pb + sizeof(T);
  4801. for (int i = 0; i<(int)sizeof(T)/2; ++i) swap(*(pb++),*(--pe));
  4802. }
  4803. }
  4804. }
  4805. }
  4806. //! Reverse endianness of a single variable.
  4807. /**
  4808. \param[in,out] a Variable to reverse.
  4809. \return Reference to reversed variable.
  4810. **/
  4811. template<typename T>
  4812. inline T& invert_endianness(T& a) {
  4813. invert_endianness(&a,1);
  4814. return a;
  4815. }
  4816. // Conversion functions to get more precision when trying to store unsigned ints values as floats.
  4817. inline unsigned int float2uint(const float f) {
  4818. int tmp = 0;
  4819. std::memcpy(&tmp,&f,sizeof(float));
  4820. if (tmp>=0) return (unsigned int)f;
  4821. unsigned int u;
  4822. // use memcpy instead of assignment to avoid undesired optimizations by C++-compiler.
  4823. std::memcpy(&u,&f,sizeof(float));
  4824. return ((u)<<1)>>1; // set sign bit to 0.
  4825. }
  4826. inline float uint2float(const unsigned int u) {
  4827. if (u<(1U<<19)) return (float)u; // Consider safe storage of unsigned int as floats until 19bits (i.e 524287).
  4828. float f;
  4829. const unsigned int v = u|(1U<<(8*sizeof(unsigned int)-1)); // set sign bit to 1.
  4830. // use memcpy instead of simple assignment to avoid undesired optimizations by C++-compiler.
  4831. std::memcpy(&f,&v,sizeof(float));
  4832. return f;
  4833. }
  4834. //! Return the value of a system timer, with a millisecond precision.
  4835. /**
  4836. \note The timer does not necessarily starts from \c 0.
  4837. **/
  4838. inline cimg_ulong time() {
  4839. #if cimg_OS==1
  4840. struct timeval st_time;
  4841. gettimeofday(&st_time,0);
  4842. return (cimg_ulong)(st_time.tv_usec/1000 + st_time.tv_sec*1000);
  4843. #elif cimg_OS==2
  4844. SYSTEMTIME st_time;
  4845. GetLocalTime(&st_time);
  4846. return (cimg_ulong)(st_time.wMilliseconds + 1000*(st_time.wSecond + 60*(st_time.wMinute + 60*st_time.wHour)));
  4847. #else
  4848. return 0;
  4849. #endif
  4850. }
  4851. // Implement a tic/toc mechanism to display elapsed time of algorithms.
  4852. inline cimg_ulong tictoc(const bool is_tic);
  4853. //! Start tic/toc timer for time measurement between code instructions.
  4854. /**
  4855. \return Current value of the timer (same value as time()).
  4856. **/
  4857. inline cimg_ulong tic() {
  4858. return cimg::tictoc(true);
  4859. }
  4860. //! End tic/toc timer and displays elapsed time from last call to tic().
  4861. /**
  4862. \return Time elapsed (in ms) since last call to tic().
  4863. **/
  4864. inline cimg_ulong toc() {
  4865. return cimg::tictoc(false);
  4866. }
  4867. //! Sleep for a given numbers of milliseconds.
  4868. /**
  4869. \param milliseconds Number of milliseconds to wait for.
  4870. \note This function frees the CPU ressources during the sleeping time.
  4871. It can be used to temporize your program properly, without wasting CPU time.
  4872. **/
  4873. inline void sleep(const unsigned int milliseconds) {
  4874. #if cimg_OS==1
  4875. struct timespec tv;
  4876. tv.tv_sec = milliseconds/1000;
  4877. tv.tv_nsec = (milliseconds%1000)*1000000;
  4878. nanosleep(&tv,0);
  4879. #elif cimg_OS==2
  4880. Sleep(milliseconds);
  4881. #else
  4882. cimg::unused(milliseconds);
  4883. #endif
  4884. }
  4885. inline unsigned int _wait(const unsigned int milliseconds, cimg_ulong& timer) {
  4886. if (!timer) timer = cimg::time();
  4887. const cimg_ulong current_time = cimg::time();
  4888. if (current_time>=timer + milliseconds) { timer = current_time; return 0; }
  4889. const unsigned int time_diff = (unsigned int)(timer + milliseconds - current_time);
  4890. timer = current_time + time_diff;
  4891. cimg::sleep(time_diff);
  4892. return time_diff;
  4893. }
  4894. //! Wait for a given number of milliseconds since the last call to wait().
  4895. /**
  4896. \param milliseconds Number of milliseconds to wait for.
  4897. \return Number of milliseconds elapsed since the last call to wait().
  4898. \note Same as sleep() with a waiting time computed with regard to the last call
  4899. of wait(). It may be used to temporize your program properly, without wasting CPU time.
  4900. **/
  4901. inline cimg_long wait(const unsigned int milliseconds) {
  4902. cimg::mutex(3);
  4903. static cimg_ulong timer = 0;
  4904. if (!timer) timer = cimg::time();
  4905. cimg::mutex(3,0);
  4906. return _wait(milliseconds,timer);
  4907. }
  4908. // Random number generators.
  4909. // CImg may use its own Random Number Generator (RNG) if configuration macro 'cimg_use_rng' is set.
  4910. // Use it for instance when you have to deal with concurrent threads trying to call std::srand()
  4911. // at the same time!
  4912. #ifdef cimg_use_rng
  4913. #include <stdint.h>
  4914. // Use a custom RNG.
  4915. inline unsigned int _rand(const unsigned int seed=0, const bool set_seed=false) {
  4916. static cimg_ulong next = 0xB16B00B5;
  4917. cimg::mutex(4);
  4918. if (set_seed) next = (cimg_ulong)seed;
  4919. else next = next*1103515245 + 12345U;
  4920. cimg::mutex(4,0);
  4921. return (unsigned int)(next&0xFFFFFFU);
  4922. }
  4923. inline unsigned int srand() {
  4924. unsigned int t = (unsigned int)cimg::time();
  4925. #if cimg_OS==1
  4926. t+=(unsigned int)getpid();
  4927. #elif cimg_OS==2
  4928. t+=(unsigned int)_getpid();
  4929. #endif
  4930. return cimg::_rand(t,true);
  4931. }
  4932. inline unsigned int srand(const unsigned int seed) {
  4933. return _rand(seed,true);
  4934. }
  4935. inline double rand(const double val_min, const double val_max) {
  4936. const double val = cimg::_rand()/16777215.;
  4937. return val_min + (val_max - val_min)*val;
  4938. }
  4939. #else
  4940. // Use the system RNG.
  4941. inline unsigned int srand() {
  4942. const unsigned int t = (unsigned int)cimg::time();
  4943. #if cimg_OS==1 || defined(__BORLANDC__)
  4944. std::srand(t + (unsigned int)getpid());
  4945. #elif cimg_OS==2
  4946. std::srand(t + (unsigned int)_getpid());
  4947. #else
  4948. std::srand(t);
  4949. #endif
  4950. return t;
  4951. }
  4952. inline unsigned int srand(const unsigned int seed) {
  4953. std::srand(seed);
  4954. return seed;
  4955. }
  4956. //! Return a random variable uniformely distributed between [val_min,val_max].
  4957. /**
  4958. **/
  4959. inline double rand(const double val_min, const double val_max) {
  4960. const double val = (double)std::rand()/RAND_MAX;
  4961. return val_min + (val_max - val_min)*val;
  4962. }
  4963. #endif
  4964. //! Return a random variable uniformely distributed between [0,val_max].
  4965. /**
  4966. **/
  4967. inline double rand(const double val_max=1) {
  4968. return cimg::rand(0,val_max);
  4969. }
  4970. //! Return a random variable following a gaussian distribution and a standard deviation of 1.
  4971. /**
  4972. **/
  4973. inline double grand() {
  4974. double x1, w;
  4975. do {
  4976. const double x2 = cimg::rand(-1,1);
  4977. x1 = cimg::rand(-1,1);
  4978. w = x1*x1 + x2*x2;
  4979. } while (w<=0 || w>=1.0);
  4980. return x1*std::sqrt((-2*std::log(w))/w);
  4981. }
  4982. //! Return a random variable following a Poisson distribution of parameter z.
  4983. /**
  4984. **/
  4985. inline unsigned int prand(const double z) {
  4986. if (z<=1.0e-10) return 0;
  4987. if (z>100) return (unsigned int)((std::sqrt(z) * cimg::grand()) + z);
  4988. unsigned int k = 0;
  4989. const double y = std::exp(-z);
  4990. for (double s = 1.0; s>=y; ++k) s*=cimg::rand();
  4991. return k - 1;
  4992. }
  4993. //! Cut (i.e. clamp) value in specified interval.
  4994. template<typename T, typename t>
  4995. inline T cut(const T& val, const t& val_min, const t& val_max) {
  4996. return val<val_min?(T)val_min:val>val_max?(T)val_max:val;
  4997. }
  4998. //! Bitwise-rotate value on the left.
  4999. template<typename T>
  5000. inline T rol(const T& a, const unsigned int n=1) {
  5001. return n?(T)((a<<n)|(a>>((sizeof(T)<<3) - n))):a;
  5002. }
  5003. inline float rol(const float a, const unsigned int n=1) {
  5004. return (float)rol((int)a,n);
  5005. }
  5006. inline double rol(const double a, const unsigned int n=1) {
  5007. return (double)rol((cimg_long)a,n);
  5008. }
  5009. inline double rol(const long double a, const unsigned int n=1) {
  5010. return (double)rol((cimg_long)a,n);
  5011. }
  5012. #ifdef cimg_use_half
  5013. inline half rol(const half a, const unsigned int n=1) {
  5014. return (half)rol((int)a,n);
  5015. }
  5016. #endif
  5017. //! Bitwise-rotate value on the right.
  5018. template<typename T>
  5019. inline T ror(const T& a, const unsigned int n=1) {
  5020. return n?(T)((a>>n)|(a<<((sizeof(T)<<3) - n))):a;
  5021. }
  5022. inline float ror(const float a, const unsigned int n=1) {
  5023. return (float)ror((int)a,n);
  5024. }
  5025. inline double ror(const double a, const unsigned int n=1) {
  5026. return (double)ror((cimg_long)a,n);
  5027. }
  5028. inline double ror(const long double a, const unsigned int n=1) {
  5029. return (double)ror((cimg_long)a,n);
  5030. }
  5031. #ifdef cimg_use_half
  5032. inline half ror(const half a, const unsigned int n=1) {
  5033. return (half)ror((int)a,n);
  5034. }
  5035. #endif
  5036. //! Return absolute value of a value.
  5037. template<typename T>
  5038. inline T abs(const T& a) {
  5039. return a>=0?a:-a;
  5040. }
  5041. inline bool abs(const bool a) {
  5042. return a;
  5043. }
  5044. inline int abs(const unsigned char a) {
  5045. return (int)a;
  5046. }
  5047. inline int abs(const unsigned short a) {
  5048. return (int)a;
  5049. }
  5050. inline int abs(const unsigned int a) {
  5051. return (int)a;
  5052. }
  5053. inline int abs(const int a) {
  5054. return std::abs(a);
  5055. }
  5056. inline cimg_int64 abs(const cimg_uint64 a) {
  5057. return (cimg_int64)a;
  5058. }
  5059. inline double abs(const double a) {
  5060. return std::fabs(a);
  5061. }
  5062. inline float abs(const float a) {
  5063. return (float)std::fabs((double)a);
  5064. }
  5065. //! Return square of a value.
  5066. template<typename T>
  5067. inline T sqr(const T& val) {
  5068. return val*val;
  5069. }
  5070. //! Return <tt>1 + log_10(x)</tt> of a value \c x.
  5071. inline int xln(const int x) {
  5072. return x>0?(int)(1 + std::log10((double)x)):1;
  5073. }
  5074. //! Return the minimum between three values.
  5075. template<typename t>
  5076. inline t min(const t& a, const t& b, const t& c) {
  5077. return std::min(std::min(a,b),c);
  5078. }
  5079. //! Return the minimum between four values.
  5080. template<typename t>
  5081. inline t min(const t& a, const t& b, const t& c, const t& d) {
  5082. return std::min(std::min(a,b),std::min(c,d));
  5083. }
  5084. //! Return the maximum between three values.
  5085. template<typename t>
  5086. inline t max(const t& a, const t& b, const t& c) {
  5087. return std::max(std::max(a,b),c);
  5088. }
  5089. //! Return the maximum between four values.
  5090. template<typename t>
  5091. inline t max(const t& a, const t& b, const t& c, const t& d) {
  5092. return std::max(std::max(a,b),std::max(c,d));
  5093. }
  5094. //! Return the sign of a value.
  5095. template<typename T>
  5096. inline T sign(const T& x) {
  5097. return (T)(x<0?-1:x>0);
  5098. }
  5099. //! Return the nearest power of 2 higher than given value.
  5100. template<typename T>
  5101. inline cimg_ulong nearest_pow2(const T& x) {
  5102. cimg_ulong i = 1;
  5103. while (x>i) i<<=1;
  5104. return i;
  5105. }
  5106. //! Return the sinc of a given value.
  5107. inline double sinc(const double x) {
  5108. return x?std::sin(x)/x:1;
  5109. }
  5110. //! Return the modulo of a value.
  5111. /**
  5112. \param x Input value.
  5113. \param m Modulo value.
  5114. \note This modulo function accepts negative and floating-points modulo numbers, as well as variables of any type.
  5115. **/
  5116. template<typename T>
  5117. inline T mod(const T& x, const T& m) {
  5118. const double dx = (double)x, dm = (double)m;
  5119. return (T)(dx - dm * std::floor(dx / dm));
  5120. }
  5121. inline int mod(const bool x, const bool m) {
  5122. return m?(x?1:0):0;
  5123. }
  5124. inline int mod(const unsigned char x, const unsigned char m) {
  5125. return x%m;
  5126. }
  5127. inline int mod(const char x, const char m) {
  5128. #if defined(CHAR_MAX) && CHAR_MAX==255
  5129. return x%m;
  5130. #else
  5131. return x>=0?x%m:(x%m?m + x%m:0);
  5132. #endif
  5133. }
  5134. inline int mod(const unsigned short x, const unsigned short m) {
  5135. return x%m;
  5136. }
  5137. inline int mod(const short x, const short m) {
  5138. return x>=0?x%m:(x%m?m + x%m:0);
  5139. }
  5140. inline int mod(const unsigned int x, const unsigned int m) {
  5141. return (int)(x%m);
  5142. }
  5143. inline int mod(const int x, const int m) {
  5144. return x>=0?x%m:(x%m?m + x%m:0);
  5145. }
  5146. inline cimg_int64 mod(const cimg_uint64 x, const cimg_uint64 m) {
  5147. return x%m;
  5148. }
  5149. inline cimg_int64 mod(const cimg_int64 x, const cimg_int64 m) {
  5150. return x>=0?x%m:(x%m?m + x%m:0);
  5151. }
  5152. //! Return the min-mod of two values.
  5153. /**
  5154. \note <i>minmod(\p a,\p b)</i> is defined to be:
  5155. - <i>minmod(\p a,\p b) = min(\p a,\p b)</i>, if \p a and \p b have the same sign.
  5156. - <i>minmod(\p a,\p b) = 0</i>, if \p a and \p b have different signs.
  5157. **/
  5158. template<typename T>
  5159. inline T minmod(const T& a, const T& b) {
  5160. return a*b<=0?0:(a>0?(a<b?a:b):(a<b?b:a));
  5161. }
  5162. //! Return base-2 logarithm of a value.
  5163. inline double log2(const double x) {
  5164. const double base = std::log(2.0);
  5165. return std::log(x)/base;
  5166. }
  5167. template<typename T>
  5168. inline T round(const T& x) {
  5169. return (T)std::floor((_cimg_Tfloat)x + 0.5f);
  5170. }
  5171. //! Return rounded value.
  5172. /**
  5173. \param x Value to be rounded.
  5174. \param y Rounding precision.
  5175. \param rounding_type Type of rounding operation (\c 0 = nearest, \c -1 = backward, \c 1 = forward).
  5176. \return Rounded value, having the same type as input value \c x.
  5177. **/
  5178. template<typename T>
  5179. inline T round(const T& x, const double y, const int rounding_type=0) {
  5180. if (y<=0) return x;
  5181. if (y==1) switch (rounding_type) {
  5182. case 0 : return round(x);
  5183. case 1 : return (T)std::ceil((_cimg_Tfloat)x);
  5184. default : return (T)std::floor((_cimg_Tfloat)x);
  5185. }
  5186. const double sx = (double)x/y, floor = std::floor(sx), delta = sx - floor;
  5187. return (T)(y*(rounding_type<0?floor:rounding_type>0?std::ceil(sx):delta<0.5?floor:std::ceil(sx)));
  5188. }
  5189. //! Return x^(1/3).
  5190. template<typename T>
  5191. inline double cbrt(const T& x) {
  5192. #if cimg_use_cpp11==1
  5193. return std::cbrt(x);
  5194. #else
  5195. return x>=0?std::pow((double)x,1.0/3):-std::pow(-(double)x,1.0/3);
  5196. #endif
  5197. }
  5198. // Code to compute fast median from 2,3,5,7,9,13,25 and 49 values.
  5199. // (contribution by RawTherapee: http://rawtherapee.com/).
  5200. template<typename T>
  5201. inline T median(T val0, T val1) {
  5202. return (val0 + val1)/2;
  5203. }
  5204. template<typename T>
  5205. inline T median(T val0, T val1, T val2) {
  5206. return std::max(std::min(val0,val1),std::min(val2,std::max(val0,val1)));
  5207. }
  5208. template<typename T>
  5209. inline T median(T val0, T val1, T val2, T val3, T val4) {
  5210. T tmp = std::min(val0,val1);
  5211. val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val3,val4); val4 = std::max(val3,val4);
  5212. val3 = std::max(val0,tmp); val1 = std::min(val1,val4); tmp = std::min(val1,val2); val2 = std::max(val1,val2);
  5213. val1 = tmp; tmp = std::min(val2,val3);
  5214. return std::max(val1,tmp);
  5215. }
  5216. template<typename T>
  5217. inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6) {
  5218. T tmp = std::min(val0,val5);
  5219. val5 = std::max(val0,val5); val0 = tmp; tmp = std::min(val0,val3); val3 = std::max(val0,val3); val0 = tmp;
  5220. tmp = std::min(val1,val6); val6 = std::max(val1,val6); val1 = tmp; tmp = std::min(val2,val4);
  5221. val4 = std::max(val2,val4); val2 = tmp; val1 = std::max(val0,val1); tmp = std::min(val3,val5);
  5222. val5 = std::max(val3,val5); val3 = tmp; tmp = std::min(val2,val6); val6 = std::max(val2,val6);
  5223. val3 = std::max(tmp,val3); val3 = std::min(val3,val6); tmp = std::min(val4,val5); val4 = std::max(val1,tmp);
  5224. tmp = std::min(val1,tmp); val3 = std::max(tmp,val3);
  5225. return std::min(val3,val4);
  5226. }
  5227. template<typename T>
  5228. inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6, T val7, T val8) {
  5229. T tmp = std::min(val1,val2);
  5230. val2 = std::max(val1,val2); val1 = tmp; tmp = std::min(val4,val5);
  5231. val5 = std::max(val4,val5); val4 = tmp; tmp = std::min(val7,val8);
  5232. val8 = std::max(val7,val8); val7 = tmp; tmp = std::min(val0,val1);
  5233. val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val3,val4);
  5234. val4 = std::max(val3,val4); val3 = tmp; tmp = std::min(val6,val7);
  5235. val7 = std::max(val6,val7); val6 = tmp; tmp = std::min(val1,val2);
  5236. val2 = std::max(val1,val2); val1 = tmp; tmp = std::min(val4,val5);
  5237. val5 = std::max(val4,val5); val4 = tmp; tmp = std::min(val7,val8);
  5238. val8 = std::max(val7,val8); val3 = std::max(val0,val3); val5 = std::min(val5,val8);
  5239. val7 = std::max(val4,tmp); tmp = std::min(val4,tmp); val6 = std::max(val3,val6);
  5240. val4 = std::max(val1,tmp); val2 = std::min(val2,val5); val4 = std::min(val4,val7);
  5241. tmp = std::min(val4,val2); val2 = std::max(val4,val2); val4 = std::max(val6,tmp);
  5242. return std::min(val4,val2);
  5243. }
  5244. template<typename T>
  5245. 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,
  5246. T val12) {
  5247. T tmp = std::min(val1,val7);
  5248. val7 = std::max(val1,val7); val1 = tmp; tmp = std::min(val9,val11); val11 = std::max(val9,val11); val9 = tmp;
  5249. tmp = std::min(val3,val4); val4 = std::max(val3,val4); val3 = tmp; tmp = std::min(val5,val8);
  5250. val8 = std::max(val5,val8); val5 = tmp; tmp = std::min(val0,val12); val12 = std::max(val0,val12);
  5251. val0 = tmp; tmp = std::min(val2,val6); val6 = std::max(val2,val6); val2 = tmp; tmp = std::min(val0,val1);
  5252. val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val2,val3); val3 = std::max(val2,val3); val2 = tmp;
  5253. tmp = std::min(val4,val6); val6 = std::max(val4,val6); val4 = tmp; tmp = std::min(val8,val11);
  5254. val11 = std::max(val8,val11); val8 = tmp; tmp = std::min(val7,val12); val12 = std::max(val7,val12); val7 = tmp;
  5255. tmp = std::min(val5,val9); val9 = std::max(val5,val9); val5 = tmp; tmp = std::min(val0,val2);
  5256. val2 = std::max(val0,val2); val0 = tmp; tmp = std::min(val3,val7); val7 = std::max(val3,val7); val3 = tmp;
  5257. tmp = std::min(val10,val11); val11 = std::max(val10,val11); val10 = tmp; tmp = std::min(val1,val4);
  5258. val4 = std::max(val1,val4); val1 = tmp; tmp = std::min(val6,val12); val12 = std::max(val6,val12); val6 = tmp;
  5259. tmp = std::min(val7,val8); val8 = std::max(val7,val8); val7 = tmp; val11 = std::min(val11,val12);
  5260. tmp = std::min(val4,val9); val9 = std::max(val4,val9); val4 = tmp; tmp = std::min(val6,val10);
  5261. val10 = std::max(val6,val10); val6 = tmp; tmp = std::min(val3,val4); val4 = std::max(val3,val4); val3 = tmp;
  5262. tmp = std::min(val5,val6); val6 = std::max(val5,val6); val5 = tmp; val8 = std::min(val8,val9);
  5263. val10 = std::min(val10,val11); tmp = std::min(val1,val7); val7 = std::max(val1,val7); val1 = tmp;
  5264. tmp = std::min(val2,val6); val6 = std::max(val2,val6); val2 = tmp; val3 = std::max(val1,val3);
  5265. tmp = std::min(val4,val7); val7 = std::max(val4,val7); val4 = tmp; val8 = std::min(val8,val10);
  5266. val5 = std::max(val0,val5); val5 = std::max(val2,val5); tmp = std::min(val6,val8); val8 = std::max(val6,val8);
  5267. val5 = std::max(val3,val5); val7 = std::min(val7,val8); val6 = std::max(val4,tmp); tmp = std::min(val4,tmp);
  5268. val5 = std::max(tmp,val5); val6 = std::min(val6,val7);
  5269. return std::max(val5,val6);
  5270. }
  5271. template<typename T>
  5272. inline T median(T val0, T val1, T val2, T val3, T val4,
  5273. T val5, T val6, T val7, T val8, T val9,
  5274. T val10, T val11, T val12, T val13, T val14,
  5275. T val15, T val16, T val17, T val18, T val19,
  5276. T val20, T val21, T val22, T val23, T val24) {
  5277. T tmp = std::min(val0,val1);
  5278. val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val3,val4); val4 = std::max(val3,val4);
  5279. val3 = tmp; tmp = std::min(val2,val4); val4 = std::max(val2,val4); val2 = std::min(tmp,val3);
  5280. val3 = std::max(tmp,val3); tmp = std::min(val6,val7); val7 = std::max(val6,val7); val6 = tmp;
  5281. tmp = std::min(val5,val7); val7 = std::max(val5,val7); val5 = std::min(tmp,val6); val6 = std::max(tmp,val6);
  5282. tmp = std::min(val9,val10); val10 = std::max(val9,val10); val9 = tmp; tmp = std::min(val8,val10);
  5283. val10 = std::max(val8,val10); val8 = std::min(tmp,val9); val9 = std::max(tmp,val9);
  5284. tmp = std::min(val12,val13); val13 = std::max(val12,val13); val12 = tmp; tmp = std::min(val11,val13);
  5285. val13 = std::max(val11,val13); val11 = std::min(tmp,val12); val12 = std::max(tmp,val12);
  5286. tmp = std::min(val15,val16); val16 = std::max(val15,val16); val15 = tmp; tmp = std::min(val14,val16);
  5287. val16 = std::max(val14,val16); val14 = std::min(tmp,val15); val15 = std::max(tmp,val15);
  5288. tmp = std::min(val18,val19); val19 = std::max(val18,val19); val18 = tmp; tmp = std::min(val17,val19);
  5289. val19 = std::max(val17,val19); val17 = std::min(tmp,val18); val18 = std::max(tmp,val18);
  5290. tmp = std::min(val21,val22); val22 = std::max(val21,val22); val21 = tmp; tmp = std::min(val20,val22);
  5291. val22 = std::max(val20,val22); val20 = std::min(tmp,val21); val21 = std::max(tmp,val21);
  5292. tmp = std::min(val23,val24); val24 = std::max(val23,val24); val23 = tmp; tmp = std::min(val2,val5);
  5293. val5 = std::max(val2,val5); val2 = tmp; tmp = std::min(val3,val6); val6 = std::max(val3,val6); val3 = tmp;
  5294. tmp = std::min(val0,val6); val6 = std::max(val0,val6); val0 = std::min(tmp,val3); val3 = std::max(tmp,val3);
  5295. tmp = std::min(val4,val7); val7 = std::max(val4,val7); val4 = tmp; tmp = std::min(val1,val7);
  5296. val7 = std::max(val1,val7); val1 = std::min(tmp,val4); val4 = std::max(tmp,val4); tmp = std::min(val11,val14);
  5297. val14 = std::max(val11,val14); val11 = tmp; tmp = std::min(val8,val14); val14 = std::max(val8,val14);
  5298. val8 = std::min(tmp,val11); val11 = std::max(tmp,val11); tmp = std::min(val12,val15);
  5299. val15 = std::max(val12,val15); val12 = tmp; tmp = std::min(val9,val15); val15 = std::max(val9,val15);
  5300. val9 = std::min(tmp,val12); val12 = std::max(tmp,val12); tmp = std::min(val13,val16);
  5301. val16 = std::max(val13,val16); val13 = tmp; tmp = std::min(val10,val16); val16 = std::max(val10,val16);
  5302. val10 = std::min(tmp,val13); val13 = std::max(tmp,val13); tmp = std::min(val20,val23);
  5303. val23 = std::max(val20,val23); val20 = tmp; tmp = std::min(val17,val23); val23 = std::max(val17,val23);
  5304. val17 = std::min(tmp,val20); val20 = std::max(tmp,val20); tmp = std::min(val21,val24);
  5305. val24 = std::max(val21,val24); val21 = tmp; tmp = std::min(val18,val24); val24 = std::max(val18,val24);
  5306. val18 = std::min(tmp,val21); val21 = std::max(tmp,val21); tmp = std::min(val19,val22);
  5307. val22 = std::max(val19,val22); val19 = tmp; val17 = std::max(val8,val17); tmp = std::min(val9,val18);
  5308. val18 = std::max(val9,val18); val9 = tmp; tmp = std::min(val0,val18); val18 = std::max(val0,val18);
  5309. val9 = std::max(tmp,val9); tmp = std::min(val10,val19); val19 = std::max(val10,val19); val10 = tmp;
  5310. tmp = std::min(val1,val19); val19 = std::max(val1,val19); val1 = std::min(tmp,val10);
  5311. val10 = std::max(tmp,val10); tmp = std::min(val11,val20); val20 = std::max(val11,val20); val11 = tmp;
  5312. tmp = std::min(val2,val20); val20 = std::max(val2,val20); val11 = std::max(tmp,val11);
  5313. tmp = std::min(val12,val21); val21 = std::max(val12,val21); val12 = tmp; tmp = std::min(val3,val21);
  5314. val21 = std::max(val3,val21); val3 = std::min(tmp,val12); val12 = std::max(tmp,val12);
  5315. tmp = std::min(val13,val22); val22 = std::max(val13,val22); val4 = std::min(val4,val22);
  5316. val13 = std::max(val4,tmp); tmp = std::min(val4,tmp); val4 = tmp; tmp = std::min(val14,val23);
  5317. val23 = std::max(val14,val23); val14 = tmp; tmp = std::min(val5,val23); val23 = std::max(val5,val23);
  5318. val5 = std::min(tmp,val14); val14 = std::max(tmp,val14); tmp = std::min(val15,val24);
  5319. val24 = std::max(val15,val24); val15 = tmp; val6 = std::min(val6,val24); tmp = std::min(val6,val15);
  5320. val15 = std::max(val6,val15); val6 = tmp; tmp = std::min(val7,val16); val7 = std::min(tmp,val19);
  5321. tmp = std::min(val13,val21); val15 = std::min(val15,val23); tmp = std::min(val7,tmp);
  5322. val7 = std::min(tmp,val15); val9 = std::max(val1,val9); val11 = std::max(val3,val11);
  5323. val17 = std::max(val5,val17); val17 = std::max(val11,val17); val17 = std::max(val9,val17);
  5324. tmp = std::min(val4,val10); val10 = std::max(val4,val10); val4 = tmp; tmp = std::min(val6,val12);
  5325. val12 = std::max(val6,val12); val6 = tmp; tmp = std::min(val7,val14); val14 = std::max(val7,val14);
  5326. val7 = tmp; tmp = std::min(val4,val6); val6 = std::max(val4,val6); val7 = std::max(tmp,val7);
  5327. tmp = std::min(val12,val14); val14 = std::max(val12,val14); val12 = tmp; val10 = std::min(val10,val14);
  5328. tmp = std::min(val6,val7); val7 = std::max(val6,val7); val6 = tmp; tmp = std::min(val10,val12);
  5329. val12 = std::max(val10,val12); val10 = std::max(val6,tmp); tmp = std::min(val6,tmp);
  5330. val17 = std::max(tmp,val17); tmp = std::min(val12,val17); val17 = std::max(val12,val17); val12 = tmp;
  5331. val7 = std::min(val7,val17); tmp = std::min(val7,val10); val10 = std::max(val7,val10); val7 = tmp;
  5332. tmp = std::min(val12,val18); val18 = std::max(val12,val18); val12 = std::max(val7,tmp);
  5333. val10 = std::min(val10,val18); tmp = std::min(val12,val20); val20 = std::max(val12,val20); val12 = tmp;
  5334. tmp = std::min(val10,val20);
  5335. return std::max(tmp,val12);
  5336. }
  5337. template<typename T>
  5338. inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6,
  5339. T val7, T val8, T val9, T val10, T val11, T val12, T val13,
  5340. T val14, T val15, T val16, T val17, T val18, T val19, T val20,
  5341. T val21, T val22, T val23, T val24, T val25, T val26, T val27,
  5342. T val28, T val29, T val30, T val31, T val32, T val33, T val34,
  5343. T val35, T val36, T val37, T val38, T val39, T val40, T val41,
  5344. T val42, T val43, T val44, T val45, T val46, T val47, T val48) {
  5345. T tmp = std::min(val0,val32);
  5346. val32 = std::max(val0,val32); val0 = tmp; tmp = std::min(val1,val33); val33 = std::max(val1,val33); val1 = tmp;
  5347. tmp = std::min(val2,val34); val34 = std::max(val2,val34); val2 = tmp; tmp = std::min(val3,val35);
  5348. val35 = std::max(val3,val35); val3 = tmp; tmp = std::min(val4,val36); val36 = std::max(val4,val36); val4 = tmp;
  5349. tmp = std::min(val5,val37); val37 = std::max(val5,val37); val5 = tmp; tmp = std::min(val6,val38);
  5350. val38 = std::max(val6,val38); val6 = tmp; tmp = std::min(val7,val39); val39 = std::max(val7,val39); val7 = tmp;
  5351. tmp = std::min(val8,val40); val40 = std::max(val8,val40); val8 = tmp; tmp = std::min(val9,val41);
  5352. val41 = std::max(val9,val41); val9 = tmp; tmp = std::min(val10,val42); val42 = std::max(val10,val42);
  5353. val10 = tmp; tmp = std::min(val11,val43); val43 = std::max(val11,val43); val11 = tmp;
  5354. tmp = std::min(val12,val44); val44 = std::max(val12,val44); val12 = tmp; tmp = std::min(val13,val45);
  5355. val45 = std::max(val13,val45); val13 = tmp; tmp = std::min(val14,val46); val46 = std::max(val14,val46);
  5356. val14 = tmp; tmp = std::min(val15,val47); val47 = std::max(val15,val47); val15 = tmp;
  5357. tmp = std::min(val16,val48); val48 = std::max(val16,val48); val16 = tmp; tmp = std::min(val0,val16);
  5358. val16 = std::max(val0,val16); val0 = tmp; tmp = std::min(val1,val17); val17 = std::max(val1,val17);
  5359. val1 = tmp; tmp = std::min(val2,val18); val18 = std::max(val2,val18); val2 = tmp; tmp = std::min(val3,val19);
  5360. val19 = std::max(val3,val19); val3 = tmp; tmp = std::min(val4,val20); val20 = std::max(val4,val20); val4 = tmp;
  5361. tmp = std::min(val5,val21); val21 = std::max(val5,val21); val5 = tmp; tmp = std::min(val6,val22);
  5362. val22 = std::max(val6,val22); val6 = tmp; tmp = std::min(val7,val23); val23 = std::max(val7,val23); val7 = tmp;
  5363. tmp = std::min(val8,val24); val24 = std::max(val8,val24); val8 = tmp; tmp = std::min(val9,val25);
  5364. val25 = std::max(val9,val25); val9 = tmp; tmp = std::min(val10,val26); val26 = std::max(val10,val26);
  5365. val10 = tmp; tmp = std::min(val11,val27); val27 = std::max(val11,val27); val11 = tmp;
  5366. tmp = std::min(val12,val28); val28 = std::max(val12,val28); val12 = tmp; tmp = std::min(val13,val29);
  5367. val29 = std::max(val13,val29); val13 = tmp; tmp = std::min(val14,val30); val30 = std::max(val14,val30);
  5368. val14 = tmp; tmp = std::min(val15,val31); val31 = std::max(val15,val31); val15 = tmp;
  5369. tmp = std::min(val32,val48); val48 = std::max(val32,val48); val32 = tmp; tmp = std::min(val16,val32);
  5370. val32 = std::max(val16,val32); val16 = tmp; tmp = std::min(val17,val33); val33 = std::max(val17,val33);
  5371. val17 = tmp; tmp = std::min(val18,val34); val34 = std::max(val18,val34); val18 = tmp;
  5372. tmp = std::min(val19,val35); val35 = std::max(val19,val35); val19 = tmp; tmp = std::min(val20,val36);
  5373. val36 = std::max(val20,val36); val20 = tmp; tmp = std::min(val21,val37); val37 = std::max(val21,val37);
  5374. val21 = tmp; tmp = std::min(val22,val38); val38 = std::max(val22,val38); val22 = tmp;
  5375. tmp = std::min(val23,val39); val39 = std::max(val23,val39); val23 = tmp; tmp = std::min(val24,val40);
  5376. val40 = std::max(val24,val40); val24 = tmp; tmp = std::min(val25,val41); val41 = std::max(val25,val41);
  5377. val25 = tmp; tmp = std::min(val26,val42); val42 = std::max(val26,val42); val26 = tmp;
  5378. tmp = std::min(val27,val43); val43 = std::max(val27,val43); val27 = tmp; tmp = std::min(val28,val44);
  5379. val44 = std::max(val28,val44); val28 = tmp; tmp = std::min(val29,val45); val45 = std::max(val29,val45);
  5380. val29 = tmp; tmp = std::min(val30,val46); val46 = std::max(val30,val46); val30 = tmp;
  5381. tmp = std::min(val31,val47); val47 = std::max(val31,val47); val31 = tmp; tmp = std::min(val0,val8);
  5382. val8 = std::max(val0,val8); val0 = tmp; tmp = std::min(val1,val9); val9 = std::max(val1,val9); val1 = tmp;
  5383. tmp = std::min(val2,val10); val10 = std::max(val2,val10); val2 = tmp; tmp = std::min(val3,val11);
  5384. val11 = std::max(val3,val11); val3 = tmp; tmp = std::min(val4,val12); val12 = std::max(val4,val12); val4 = tmp;
  5385. tmp = std::min(val5,val13); val13 = std::max(val5,val13); val5 = tmp; tmp = std::min(val6,val14);
  5386. val14 = std::max(val6,val14); val6 = tmp; tmp = std::min(val7,val15); val15 = std::max(val7,val15); val7 = tmp;
  5387. tmp = std::min(val16,val24); val24 = std::max(val16,val24); val16 = tmp; tmp = std::min(val17,val25);
  5388. val25 = std::max(val17,val25); val17 = tmp; tmp = std::min(val18,val26); val26 = std::max(val18,val26);
  5389. val18 = tmp; tmp = std::min(val19,val27); val27 = std::max(val19,val27); val19 = tmp;
  5390. tmp = std::min(val20,val28); val28 = std::max(val20,val28); val20 = tmp; tmp = std::min(val21,val29);
  5391. val29 = std::max(val21,val29); val21 = tmp; tmp = std::min(val22,val30); val30 = std::max(val22,val30);
  5392. val22 = tmp; tmp = std::min(val23,val31); val31 = std::max(val23,val31); val23 = tmp;
  5393. tmp = std::min(val32,val40); val40 = std::max(val32,val40); val32 = tmp; tmp = std::min(val33,val41);
  5394. val41 = std::max(val33,val41); val33 = tmp; tmp = std::min(val34,val42); val42 = std::max(val34,val42);
  5395. val34 = tmp; tmp = std::min(val35,val43); val43 = std::max(val35,val43); val35 = tmp;
  5396. tmp = std::min(val36,val44); val44 = std::max(val36,val44); val36 = tmp; tmp = std::min(val37,val45);
  5397. val45 = std::max(val37,val45); val37 = tmp; tmp = std::min(val38,val46); val46 = std::max(val38,val46);
  5398. val38 = tmp; tmp = std::min(val39,val47); val47 = std::max(val39,val47); val39 = tmp;
  5399. tmp = std::min(val8,val32); val32 = std::max(val8,val32); val8 = tmp; tmp = std::min(val9,val33);
  5400. val33 = std::max(val9,val33); val9 = tmp; tmp = std::min(val10,val34); val34 = std::max(val10,val34);
  5401. val10 = tmp; tmp = std::min(val11,val35); val35 = std::max(val11,val35); val11 = tmp;
  5402. tmp = std::min(val12,val36); val36 = std::max(val12,val36); val12 = tmp; tmp = std::min(val13,val37);
  5403. val37 = std::max(val13,val37); val13 = tmp; tmp = std::min(val14,val38); val38 = std::max(val14,val38);
  5404. val14 = tmp; tmp = std::min(val15,val39); val39 = std::max(val15,val39); val15 = tmp;
  5405. tmp = std::min(val24,val48); val48 = std::max(val24,val48); val24 = tmp; tmp = std::min(val8,val16);
  5406. val16 = std::max(val8,val16); val8 = tmp; tmp = std::min(val9,val17); val17 = std::max(val9,val17);
  5407. val9 = tmp; tmp = std::min(val10,val18); val18 = std::max(val10,val18); val10 = tmp;
  5408. tmp = std::min(val11,val19); val19 = std::max(val11,val19); val11 = tmp; tmp = std::min(val12,val20);
  5409. val20 = std::max(val12,val20); val12 = tmp; tmp = std::min(val13,val21); val21 = std::max(val13,val21);
  5410. val13 = tmp; tmp = std::min(val14,val22); val22 = std::max(val14,val22); val14 = tmp;
  5411. tmp = std::min(val15,val23); val23 = std::max(val15,val23); val15 = tmp; tmp = std::min(val24,val32);
  5412. val32 = std::max(val24,val32); val24 = tmp; tmp = std::min(val25,val33); val33 = std::max(val25,val33);
  5413. val25 = tmp; tmp = std::min(val26,val34); val34 = std::max(val26,val34); val26 = tmp;
  5414. tmp = std::min(val27,val35); val35 = std::max(val27,val35); val27 = tmp; tmp = std::min(val28,val36);
  5415. val36 = std::max(val28,val36); val28 = tmp; tmp = std::min(val29,val37); val37 = std::max(val29,val37);
  5416. val29 = tmp; tmp = std::min(val30,val38); val38 = std::max(val30,val38); val30 = tmp;
  5417. tmp = std::min(val31,val39); val39 = std::max(val31,val39); val31 = tmp; tmp = std::min(val40,val48);
  5418. val48 = std::max(val40,val48); val40 = tmp; tmp = std::min(val0,val4); val4 = std::max(val0,val4);
  5419. val0 = tmp; tmp = std::min(val1,val5); val5 = std::max(val1,val5); val1 = tmp; tmp = std::min(val2,val6);
  5420. val6 = std::max(val2,val6); val2 = tmp; tmp = std::min(val3,val7); val7 = std::max(val3,val7); val3 = tmp;
  5421. tmp = std::min(val8,val12); val12 = std::max(val8,val12); val8 = tmp; tmp = std::min(val9,val13);
  5422. val13 = std::max(val9,val13); val9 = tmp; tmp = std::min(val10,val14); val14 = std::max(val10,val14);
  5423. val10 = tmp; tmp = std::min(val11,val15); val15 = std::max(val11,val15); val11 = tmp;
  5424. tmp = std::min(val16,val20); val20 = std::max(val16,val20); val16 = tmp; tmp = std::min(val17,val21);
  5425. val21 = std::max(val17,val21); val17 = tmp; tmp = std::min(val18,val22); val22 = std::max(val18,val22);
  5426. val18 = tmp; tmp = std::min(val19,val23); val23 = std::max(val19,val23); val19 = tmp;
  5427. tmp = std::min(val24,val28); val28 = std::max(val24,val28); val24 = tmp; tmp = std::min(val25,val29);
  5428. val29 = std::max(val25,val29); val25 = tmp; tmp = std::min(val26,val30); val30 = std::max(val26,val30);
  5429. val26 = tmp; tmp = std::min(val27,val31); val31 = std::max(val27,val31); val27 = tmp;
  5430. tmp = std::min(val32,val36); val36 = std::max(val32,val36); val32 = tmp; tmp = std::min(val33,val37);
  5431. val37 = std::max(val33,val37); val33 = tmp; tmp = std::min(val34,val38); val38 = std::max(val34,val38);
  5432. val34 = tmp; tmp = std::min(val35,val39); val39 = std::max(val35,val39); val35 = tmp;
  5433. tmp = std::min(val40,val44); val44 = std::max(val40,val44); val40 = tmp; tmp = std::min(val41,val45);
  5434. val45 = std::max(val41,val45); val41 = tmp; tmp = std::min(val42,val46); val46 = std::max(val42,val46);
  5435. val42 = tmp; tmp = std::min(val43,val47); val47 = std::max(val43,val47); val43 = tmp;
  5436. tmp = std::min(val4,val32); val32 = std::max(val4,val32); val4 = tmp; tmp = std::min(val5,val33);
  5437. val33 = std::max(val5,val33); val5 = tmp; tmp = std::min(val6,val34); val34 = std::max(val6,val34);
  5438. val6 = tmp; tmp = std::min(val7,val35); val35 = std::max(val7,val35); val7 = tmp;
  5439. tmp = std::min(val12,val40); val40 = std::max(val12,val40); val12 = tmp; tmp = std::min(val13,val41);
  5440. val41 = std::max(val13,val41); val13 = tmp; tmp = std::min(val14,val42); val42 = std::max(val14,val42);
  5441. val14 = tmp; tmp = std::min(val15,val43); val43 = std::max(val15,val43); val15 = tmp;
  5442. tmp = std::min(val20,val48); val48 = std::max(val20,val48); val20 = tmp; tmp = std::min(val4,val16);
  5443. val16 = std::max(val4,val16); val4 = tmp; tmp = std::min(val5,val17); val17 = std::max(val5,val17);
  5444. val5 = tmp; tmp = std::min(val6,val18); val18 = std::max(val6,val18); val6 = tmp;
  5445. tmp = std::min(val7,val19); val19 = std::max(val7,val19); val7 = tmp; tmp = std::min(val12,val24);
  5446. val24 = std::max(val12,val24); val12 = tmp; tmp = std::min(val13,val25); val25 = std::max(val13,val25);
  5447. val13 = tmp; tmp = std::min(val14,val26); val26 = std::max(val14,val26); val14 = tmp;
  5448. tmp = std::min(val15,val27); val27 = std::max(val15,val27); val15 = tmp; tmp = std::min(val20,val32);
  5449. val32 = std::max(val20,val32); val20 = tmp; tmp = std::min(val21,val33); val33 = std::max(val21,val33);
  5450. val21 = tmp; tmp = std::min(val22,val34); val34 = std::max(val22,val34); val22 = tmp;
  5451. tmp = std::min(val23,val35); val35 = std::max(val23,val35); val23 = tmp; tmp = std::min(val28,val40);
  5452. val40 = std::max(val28,val40); val28 = tmp; tmp = std::min(val29,val41); val41 = std::max(val29,val41);
  5453. val29 = tmp; tmp = std::min(val30,val42); val42 = std::max(val30,val42); val30 = tmp;
  5454. tmp = std::min(val31,val43); val43 = std::max(val31,val43); val31 = tmp; tmp = std::min(val36,val48);
  5455. val48 = std::max(val36,val48); val36 = tmp; tmp = std::min(val4,val8); val8 = std::max(val4,val8);
  5456. val4 = tmp; tmp = std::min(val5,val9); val9 = std::max(val5,val9); val5 = tmp; tmp = std::min(val6,val10);
  5457. val10 = std::max(val6,val10); val6 = tmp; tmp = std::min(val7,val11); val11 = std::max(val7,val11); val7 = tmp;
  5458. tmp = std::min(val12,val16); val16 = std::max(val12,val16); val12 = tmp; tmp = std::min(val13,val17);
  5459. val17 = std::max(val13,val17); val13 = tmp; tmp = std::min(val14,val18); val18 = std::max(val14,val18);
  5460. val14 = tmp; tmp = std::min(val15,val19); val19 = std::max(val15,val19); val15 = tmp;
  5461. tmp = std::min(val20,val24); val24 = std::max(val20,val24); val20 = tmp; tmp = std::min(val21,val25);
  5462. val25 = std::max(val21,val25); val21 = tmp; tmp = std::min(val22,val26); val26 = std::max(val22,val26);
  5463. val22 = tmp; tmp = std::min(val23,val27); val27 = std::max(val23,val27); val23 = tmp;
  5464. tmp = std::min(val28,val32); val32 = std::max(val28,val32); val28 = tmp; tmp = std::min(val29,val33);
  5465. val33 = std::max(val29,val33); val29 = tmp; tmp = std::min(val30,val34); val34 = std::max(val30,val34);
  5466. val30 = tmp; tmp = std::min(val31,val35); val35 = std::max(val31,val35); val31 = tmp;
  5467. tmp = std::min(val36,val40); val40 = std::max(val36,val40); val36 = tmp; tmp = std::min(val37,val41);
  5468. val41 = std::max(val37,val41); val37 = tmp; tmp = std::min(val38,val42); val42 = std::max(val38,val42);
  5469. val38 = tmp; tmp = std::min(val39,val43); val43 = std::max(val39,val43); val39 = tmp;
  5470. tmp = std::min(val44,val48); val48 = std::max(val44,val48); val44 = tmp; tmp = std::min(val0,val2);
  5471. val2 = std::max(val0,val2); val0 = tmp; tmp = std::min(val1,val3); val3 = std::max(val1,val3); val1 = tmp;
  5472. tmp = std::min(val4,val6); val6 = std::max(val4,val6); val4 = tmp; tmp = std::min(val5,val7);
  5473. val7 = std::max(val5,val7); val5 = tmp; tmp = std::min(val8,val10); val10 = std::max(val8,val10); val8 = tmp;
  5474. tmp = std::min(val9,val11); val11 = std::max(val9,val11); val9 = tmp; tmp = std::min(val12,val14);
  5475. val14 = std::max(val12,val14); val12 = tmp; tmp = std::min(val13,val15); val15 = std::max(val13,val15);
  5476. val13 = tmp; tmp = std::min(val16,val18); val18 = std::max(val16,val18); val16 = tmp;
  5477. tmp = std::min(val17,val19); val19 = std::max(val17,val19); val17 = tmp; tmp = std::min(val20,val22);
  5478. val22 = std::max(val20,val22); val20 = tmp; tmp = std::min(val21,val23); val23 = std::max(val21,val23);
  5479. val21 = tmp; tmp = std::min(val24,val26); val26 = std::max(val24,val26); val24 = tmp;
  5480. tmp = std::min(val25,val27); val27 = std::max(val25,val27); val25 = tmp; tmp = std::min(val28,val30);
  5481. val30 = std::max(val28,val30); val28 = tmp; tmp = std::min(val29,val31); val31 = std::max(val29,val31);
  5482. val29 = tmp; tmp = std::min(val32,val34); val34 = std::max(val32,val34); val32 = tmp;
  5483. tmp = std::min(val33,val35); val35 = std::max(val33,val35); val33 = tmp; tmp = std::min(val36,val38);
  5484. val38 = std::max(val36,val38); val36 = tmp; tmp = std::min(val37,val39); val39 = std::max(val37,val39);
  5485. val37 = tmp; tmp = std::min(val40,val42); val42 = std::max(val40,val42); val40 = tmp;
  5486. tmp = std::min(val41,val43); val43 = std::max(val41,val43); val41 = tmp; tmp = std::min(val44,val46);
  5487. val46 = std::max(val44,val46); val44 = tmp; tmp = std::min(val45,val47); val47 = std::max(val45,val47);
  5488. val45 = tmp; tmp = std::min(val2,val32); val32 = std::max(val2,val32); val2 = tmp; tmp = std::min(val3,val33);
  5489. val33 = std::max(val3,val33); val3 = tmp; tmp = std::min(val6,val36); val36 = std::max(val6,val36); val6 = tmp;
  5490. tmp = std::min(val7,val37); val37 = std::max(val7,val37); val7 = tmp; tmp = std::min(val10,val40);
  5491. val40 = std::max(val10,val40); val10 = tmp; tmp = std::min(val11,val41); val41 = std::max(val11,val41);
  5492. val11 = tmp; tmp = std::min(val14,val44); val44 = std::max(val14,val44); val14 = tmp;
  5493. tmp = std::min(val15,val45); val45 = std::max(val15,val45); val15 = tmp; tmp = std::min(val18,val48);
  5494. val48 = std::max(val18,val48); val18 = tmp; tmp = std::min(val2,val16); val16 = std::max(val2,val16);
  5495. val2 = tmp; tmp = std::min(val3,val17); val17 = std::max(val3,val17); val3 = tmp;
  5496. tmp = std::min(val6,val20); val20 = std::max(val6,val20); val6 = tmp; tmp = std::min(val7,val21);
  5497. val21 = std::max(val7,val21); val7 = tmp; tmp = std::min(val10,val24); val24 = std::max(val10,val24);
  5498. val10 = tmp; tmp = std::min(val11,val25); val25 = std::max(val11,val25); val11 = tmp;
  5499. tmp = std::min(val14,val28); val28 = std::max(val14,val28); val14 = tmp; tmp = std::min(val15,val29);
  5500. val29 = std::max(val15,val29); val15 = tmp; tmp = std::min(val18,val32); val32 = std::max(val18,val32);
  5501. val18 = tmp; tmp = std::min(val19,val33); val33 = std::max(val19,val33); val19 = tmp;
  5502. tmp = std::min(val22,val36); val36 = std::max(val22,val36); val22 = tmp; tmp = std::min(val23,val37);
  5503. val37 = std::max(val23,val37); val23 = tmp; tmp = std::min(val26,val40); val40 = std::max(val26,val40);
  5504. val26 = tmp; tmp = std::min(val27,val41); val41 = std::max(val27,val41); val27 = tmp;
  5505. tmp = std::min(val30,val44); val44 = std::max(val30,val44); val30 = tmp; tmp = std::min(val31,val45);
  5506. val45 = std::max(val31,val45); val31 = tmp; tmp = std::min(val34,val48); val48 = std::max(val34,val48);
  5507. val34 = tmp; tmp = std::min(val2,val8); val8 = std::max(val2,val8); val2 = tmp; tmp = std::min(val3,val9);
  5508. val9 = std::max(val3,val9); val3 = tmp; tmp = std::min(val6,val12); val12 = std::max(val6,val12); val6 = tmp;
  5509. tmp = std::min(val7,val13); val13 = std::max(val7,val13); val7 = tmp; tmp = std::min(val10,val16);
  5510. val16 = std::max(val10,val16); val10 = tmp; tmp = std::min(val11,val17); val17 = std::max(val11,val17);
  5511. val11 = tmp; tmp = std::min(val14,val20); val20 = std::max(val14,val20); val14 = tmp;
  5512. tmp = std::min(val15,val21); val21 = std::max(val15,val21); val15 = tmp; tmp = std::min(val18,val24);
  5513. val24 = std::max(val18,val24); val18 = tmp; tmp = std::min(val19,val25); val25 = std::max(val19,val25);
  5514. val19 = tmp; tmp = std::min(val22,val28); val28 = std::max(val22,val28); val22 = tmp;
  5515. tmp = std::min(val23,val29); val29 = std::max(val23,val29); val23 = tmp; tmp = std::min(val26,val32);
  5516. val32 = std::max(val26,val32); val26 = tmp; tmp = std::min(val27,val33); val33 = std::max(val27,val33);
  5517. val27 = tmp; tmp = std::min(val30,val36); val36 = std::max(val30,val36); val30 = tmp;
  5518. tmp = std::min(val31,val37); val37 = std::max(val31,val37); val31 = tmp; tmp = std::min(val34,val40);
  5519. val40 = std::max(val34,val40); val34 = tmp; tmp = std::min(val35,val41); val41 = std::max(val35,val41);
  5520. val35 = tmp; tmp = std::min(val38,val44); val44 = std::max(val38,val44); val38 = tmp;
  5521. tmp = std::min(val39,val45); val45 = std::max(val39,val45); val39 = tmp; tmp = std::min(val42,val48);
  5522. val48 = std::max(val42,val48); val42 = tmp; tmp = std::min(val2,val4); val4 = std::max(val2,val4);
  5523. val2 = tmp; tmp = std::min(val3,val5); val5 = std::max(val3,val5); val3 = tmp; tmp = std::min(val6,val8);
  5524. val8 = std::max(val6,val8); val6 = tmp; tmp = std::min(val7,val9); val9 = std::max(val7,val9); val7 = tmp;
  5525. tmp = std::min(val10,val12); val12 = std::max(val10,val12); val10 = tmp; tmp = std::min(val11,val13);
  5526. val13 = std::max(val11,val13); val11 = tmp; tmp = std::min(val14,val16); val16 = std::max(val14,val16);
  5527. val14 = tmp; tmp = std::min(val15,val17); val17 = std::max(val15,val17); val15 = tmp;
  5528. tmp = std::min(val18,val20); val20 = std::max(val18,val20); val18 = tmp; tmp = std::min(val19,val21);
  5529. val21 = std::max(val19,val21); val19 = tmp; tmp = std::min(val22,val24); val24 = std::max(val22,val24);
  5530. val22 = tmp; tmp = std::min(val23,val25); val25 = std::max(val23,val25); val23 = tmp;
  5531. tmp = std::min(val26,val28); val28 = std::max(val26,val28); val26 = tmp; tmp = std::min(val27,val29);
  5532. val29 = std::max(val27,val29); val27 = tmp; tmp = std::min(val30,val32); val32 = std::max(val30,val32);
  5533. val30 = tmp; tmp = std::min(val31,val33); val33 = std::max(val31,val33); val31 = tmp;
  5534. tmp = std::min(val34,val36); val36 = std::max(val34,val36); val34 = tmp; tmp = std::min(val35,val37);
  5535. val37 = std::max(val35,val37); val35 = tmp; tmp = std::min(val38,val40); val40 = std::max(val38,val40);
  5536. val38 = tmp; tmp = std::min(val39,val41); val41 = std::max(val39,val41); val39 = tmp;
  5537. tmp = std::min(val42,val44); val44 = std::max(val42,val44); val42 = tmp; tmp = std::min(val43,val45);
  5538. val45 = std::max(val43,val45); val43 = tmp; tmp = std::min(val46,val48); val48 = std::max(val46,val48);
  5539. val46 = tmp; val1 = std::max(val0,val1); val3 = std::max(val2,val3); val5 = std::max(val4,val5);
  5540. val7 = std::max(val6,val7); val9 = std::max(val8,val9); val11 = std::max(val10,val11);
  5541. val13 = std::max(val12,val13); val15 = std::max(val14,val15); val17 = std::max(val16,val17);
  5542. val19 = std::max(val18,val19); val21 = std::max(val20,val21); val23 = std::max(val22,val23);
  5543. val24 = std::min(val24,val25); val26 = std::min(val26,val27); val28 = std::min(val28,val29);
  5544. val30 = std::min(val30,val31); val32 = std::min(val32,val33); val34 = std::min(val34,val35);
  5545. val36 = std::min(val36,val37); val38 = std::min(val38,val39); val40 = std::min(val40,val41);
  5546. val42 = std::min(val42,val43); val44 = std::min(val44,val45); val46 = std::min(val46,val47);
  5547. val32 = std::max(val1,val32); val34 = std::max(val3,val34); val36 = std::max(val5,val36);
  5548. val38 = std::max(val7,val38); val9 = std::min(val9,val40); val11 = std::min(val11,val42);
  5549. val13 = std::min(val13,val44); val15 = std::min(val15,val46); val17 = std::min(val17,val48);
  5550. val24 = std::max(val9,val24); val26 = std::max(val11,val26); val28 = std::max(val13,val28);
  5551. val30 = std::max(val15,val30); val17 = std::min(val17,val32); val19 = std::min(val19,val34);
  5552. val21 = std::min(val21,val36); val23 = std::min(val23,val38); val24 = std::max(val17,val24);
  5553. val26 = std::max(val19,val26); val21 = std::min(val21,val28); val23 = std::min(val23,val30);
  5554. val24 = std::max(val21,val24); val23 = std::min(val23,val26);
  5555. return std::max(val23,val24);
  5556. }
  5557. //! Return sqrt(x^2 + y^2).
  5558. template<typename T>
  5559. inline T hypot(const T x, const T y) {
  5560. return std::sqrt(x*x + y*y);
  5561. }
  5562. template<typename T>
  5563. inline T hypot(const T x, const T y, const T z) {
  5564. return std::sqrt(x*x + y*y + z*z);
  5565. }
  5566. template<typename T>
  5567. inline T _hypot(const T x, const T y) { // Slower but more precise version
  5568. T nx = cimg::abs(x), ny = cimg::abs(y), t;
  5569. if (nx<ny) { t = nx; nx = ny; } else t = ny;
  5570. if (nx>0) { t/=nx; return nx*std::sqrt(1 + t*t); }
  5571. return 0;
  5572. }
  5573. //! Return the factorial of n
  5574. inline double factorial(const int n) {
  5575. if (n<0) return cimg::type<double>::nan();
  5576. if (n<2) return 1;
  5577. double res = 2;
  5578. for (int i = 3; i<=n; ++i) res*=i;
  5579. return res;
  5580. }
  5581. //! Return the number of permutations of k objects in a set of n objects.
  5582. inline double permutations(const int k, const int n, const bool with_order) {
  5583. if (n<0 || k<0) return cimg::type<double>::nan();
  5584. if (k>n) return 0;
  5585. double res = 1;
  5586. for (int i = n; i>=n - k + 1; --i) res*=i;
  5587. return with_order?res:res/cimg::factorial(k);
  5588. }
  5589. inline double _fibonacci(int exp) {
  5590. double
  5591. base = (1 + std::sqrt(5.0))/2,
  5592. result = 1/std::sqrt(5.0);
  5593. while (exp) {
  5594. if (exp&1) result*=base;
  5595. exp>>=1;
  5596. base*=base;
  5597. }
  5598. return result;
  5599. }
  5600. //! Calculate fibonacci number.
  5601. // (Precise up to n = 78, less precise for n>78).
  5602. inline double fibonacci(const int n) {
  5603. if (n<0) return cimg::type<double>::nan();
  5604. if (n<3) return 1;
  5605. if (n<11) {
  5606. cimg_uint64 fn1 = 1, fn2 = 1, fn = 0;
  5607. for (int i = 3; i<=n; ++i) { fn = fn1 + fn2; fn2 = fn1; fn1 = fn; }
  5608. return (double)fn;
  5609. }
  5610. if (n<75) // precise up to n = 74, faster than the integer calculation above for n>10
  5611. return (double)((cimg_uint64)(_fibonacci(n) + 0.5));
  5612. if (n<94) { // precise up to n = 78, less precise for n>78 up to n = 93, overflows for n>93
  5613. cimg_uint64
  5614. fn1 = (cimg_uint64)1304969544928657ULL,
  5615. fn2 = (cimg_uint64)806515533049393ULL,
  5616. fn = 0;
  5617. for (int i = 75; i<=n; ++i) { fn = fn1 + fn2; fn2 = fn1; fn1 = fn; }
  5618. return (double)fn;
  5619. }
  5620. return _fibonacci(n); // Not precise, but better than the wrong overflowing calculation
  5621. }
  5622. //! Calculate greatest common divisor.
  5623. inline long gcd(long a, long b) {
  5624. while (a) { const long c = a; a = b%a; b = c; }
  5625. return b;
  5626. }
  5627. //! Convert ascii character to lower case.
  5628. inline char lowercase(const char x) {
  5629. return (char)((x<'A'||x>'Z')?x:x - 'A' + 'a');
  5630. }
  5631. inline double lowercase(const double x) {
  5632. return (double)((x<'A'||x>'Z')?x:x - 'A' + 'a');
  5633. }
  5634. //! Convert C-string to lower case.
  5635. inline void lowercase(char *const str) {
  5636. if (str) for (char *ptr = str; *ptr; ++ptr) *ptr = lowercase(*ptr);
  5637. }
  5638. //! Convert ascii character to upper case.
  5639. inline char uppercase(const char x) {
  5640. return (char)((x<'a'||x>'z')?x:x - 'a' + 'A');
  5641. }
  5642. inline double uppercase(const double x) {
  5643. return (double)((x<'a'||x>'z')?x:x - 'a' + 'A');
  5644. }
  5645. //! Convert C-string to upper case.
  5646. inline void uppercase(char *const str) {
  5647. if (str) for (char *ptr = str; *ptr; ++ptr) *ptr = uppercase(*ptr);
  5648. }
  5649. //! Read value in a C-string.
  5650. /**
  5651. \param str C-string containing the float value to read.
  5652. \return Read value.
  5653. \note Same as <tt>std::atof()</tt> extended to manage the retrieval of fractions from C-strings,
  5654. as in <em>"1/2"</em>.
  5655. **/
  5656. inline double atof(const char *const str) {
  5657. double x = 0, y = 1;
  5658. return str && cimg_sscanf(str,"%lf/%lf",&x,&y)>0?x/y:0;
  5659. }
  5660. //! Compare the first \p l characters of two C-strings, ignoring the case.
  5661. /**
  5662. \param str1 C-string.
  5663. \param str2 C-string.
  5664. \param l Number of characters to compare.
  5665. \return \c 0 if the two strings are equal, something else otherwise.
  5666. \note This function has to be defined since it is not provided by all C++-compilers (not ANSI).
  5667. **/
  5668. inline int strncasecmp(const char *const str1, const char *const str2, const int l) {
  5669. if (!l) return 0;
  5670. if (!str1) return str2?-1:0;
  5671. const char *nstr1 = str1, *nstr2 = str2;
  5672. int k, diff = 0; for (k = 0; k<l && !(diff = lowercase(*nstr1) - lowercase(*nstr2)); ++k) { ++nstr1; ++nstr2; }
  5673. return k!=l?diff:0;
  5674. }
  5675. //! Compare two C-strings, ignoring the case.
  5676. /**
  5677. \param str1 C-string.
  5678. \param str2 C-string.
  5679. \return \c 0 if the two strings are equal, something else otherwise.
  5680. \note This function has to be defined since it is not provided by all C++-compilers (not ANSI).
  5681. **/
  5682. inline int strcasecmp(const char *const str1, const char *const str2) {
  5683. if (!str1) return str2?-1:0;
  5684. const int
  5685. l1 = (int)std::strlen(str1),
  5686. l2 = (int)std::strlen(str2);
  5687. return cimg::strncasecmp(str1,str2,1 + (l1<l2?l1:l2));
  5688. }
  5689. //! Ellipsize a string.
  5690. /**
  5691. \param str C-string.
  5692. \param l Max number of characters.
  5693. \param is_ending Tell if the dots are placed at the end or at the center of the ellipsized string.
  5694. **/
  5695. inline char *strellipsize(char *const str, const unsigned int l=64,
  5696. const bool is_ending=true) {
  5697. if (!str) return str;
  5698. const unsigned int nl = l<5?5:l, ls = (unsigned int)std::strlen(str);
  5699. if (ls<=nl) return str;
  5700. if (is_ending) std::strcpy(str + nl - 5,"(...)");
  5701. else {
  5702. const unsigned int ll = (nl - 5)/2 + 1 - (nl%2), lr = nl - ll - 5;
  5703. std::strcpy(str + ll,"(...)");
  5704. std::memmove(str + ll + 5,str + ls - lr,lr);
  5705. }
  5706. str[nl] = 0;
  5707. return str;
  5708. }
  5709. //! Ellipsize a string.
  5710. /**
  5711. \param str C-string.
  5712. \param res output C-string.
  5713. \param l Max number of characters.
  5714. \param is_ending Tell if the dots are placed at the end or at the center of the ellipsized string.
  5715. **/
  5716. inline char *strellipsize(const char *const str, char *const res, const unsigned int l=64,
  5717. const bool is_ending=true) {
  5718. const unsigned int nl = l<5?5:l, ls = (unsigned int)std::strlen(str);
  5719. if (ls<=nl) { std::strcpy(res,str); return res; }
  5720. if (is_ending) {
  5721. std::strncpy(res,str,nl - 5);
  5722. std::strcpy(res + nl -5,"(...)");
  5723. } else {
  5724. const unsigned int ll = (nl - 5)/2 + 1 - (nl%2), lr = nl - ll - 5;
  5725. std::strncpy(res,str,ll);
  5726. std::strcpy(res + ll,"(...)");
  5727. std::strncpy(res + ll + 5,str + ls - lr,lr);
  5728. }
  5729. res[nl] = 0;
  5730. return res;
  5731. }
  5732. //! Remove delimiters on the start and/or end of a C-string.
  5733. /**
  5734. \param[in,out] str C-string to work with (modified at output).
  5735. \param delimiter Delimiter character code to remove.
  5736. \param is_symmetric Tells if the removal is done only if delimiters are symmetric
  5737. (both at the beginning and the end of \c s).
  5738. \param is_iterative Tells if the removal is done if several iterations are possible.
  5739. \return \c true if delimiters have been removed, \c false otherwise.
  5740. **/
  5741. inline bool strpare(char *const str, const char delimiter,
  5742. const bool is_symmetric, const bool is_iterative) {
  5743. if (!str) return false;
  5744. const int l = (int)std::strlen(str);
  5745. int p, q;
  5746. if (is_symmetric) for (p = 0, q = l - 1; p<q && str[p]==delimiter && str[q]==delimiter; ) {
  5747. --q; ++p; if (!is_iterative) break;
  5748. } else {
  5749. for (p = 0; p<l && str[p]==delimiter; ) { ++p; if (!is_iterative) break; }
  5750. for (q = l - 1; q>p && str[q]==delimiter; ) { --q; if (!is_iterative) break; }
  5751. }
  5752. const int n = q - p + 1;
  5753. if (n!=l) { std::memmove(str,str + p,(unsigned int)n); str[n] = 0; return true; }
  5754. return false;
  5755. }
  5756. //! Remove white spaces on the start and/or end of a C-string.
  5757. inline bool strpare(char *const str, const bool is_symmetric, const bool is_iterative) {
  5758. if (!str) return false;
  5759. const int l = (int)std::strlen(str);
  5760. int p, q;
  5761. if (is_symmetric) for (p = 0, q = l - 1; p<q && (signed char)str[p]<=' ' && (signed char)str[q]<=' '; ) {
  5762. --q; ++p; if (!is_iterative) break;
  5763. } else {
  5764. for (p = 0; p<l && (signed char)str[p]<=' '; ) { ++p; if (!is_iterative) break; }
  5765. for (q = l - 1; q>p && (signed char)str[q]<=' '; ) { --q; if (!is_iterative) break; }
  5766. }
  5767. const int n = q - p + 1;
  5768. if (n!=l) { std::memmove(str,str + p,(unsigned int)n); str[n] = 0; return true; }
  5769. return false;
  5770. }
  5771. //! Replace reserved characters (for Windows filename) by another character.
  5772. /**
  5773. \param[in,out] str C-string to work with (modified at output).
  5774. \param[in] c Replacement character.
  5775. **/
  5776. inline void strwindows_reserved(char *const str, const char c='_') {
  5777. for (char *s = str; *s; ++s) {
  5778. const char i = *s;
  5779. if (i=='<' || i=='>' || i==':' || i=='\"' || i=='/' || i=='\\' || i=='|' || i=='?' || i=='*') *s = c;
  5780. }
  5781. }
  5782. //! Replace escape sequences in C-strings by their binary ascii values.
  5783. /**
  5784. \param[in,out] str C-string to work with (modified at output).
  5785. **/
  5786. inline void strunescape(char *const str) {
  5787. #define cimg_strunescape(ci,co) case ci : *nd = co; ++ns; break;
  5788. unsigned int val = 0;
  5789. for (char *ns = str, *nd = str; *ns || (bool)(*nd=0); ++nd) if (*ns=='\\') switch (*(++ns)) {
  5790. cimg_strunescape('a','\a');
  5791. cimg_strunescape('b','\b');
  5792. cimg_strunescape('e',0x1B);
  5793. cimg_strunescape('f','\f');
  5794. cimg_strunescape('n','\n');
  5795. cimg_strunescape('r','\r');
  5796. cimg_strunescape('t','\t');
  5797. cimg_strunescape('v','\v');
  5798. cimg_strunescape('\\','\\');
  5799. cimg_strunescape('\'','\'');
  5800. cimg_strunescape('\"','\"');
  5801. cimg_strunescape('\?','\?');
  5802. case 0 : *nd = 0; break;
  5803. case '0' : case '1' : case '2' : case '3' : case '4' : case '5' : case '6' : case '7' :
  5804. cimg_sscanf(ns,"%o",&val); while (*ns>='0' && *ns<='7') ++ns;
  5805. *nd = (char)val; break;
  5806. case 'x' :
  5807. cimg_sscanf(++ns,"%x",&val);
  5808. while ((*ns>='0' && *ns<='9') || (*ns>='a' && *ns<='f') || (*ns>='A' && *ns<='F')) ++ns;
  5809. *nd = (char)val; break;
  5810. default : *nd = *(ns++);
  5811. } else *nd = *(ns++);
  5812. }
  5813. // Return a temporary string describing the size of a memory buffer.
  5814. inline const char *strbuffersize(const cimg_ulong size);
  5815. // Return string that identifies the running OS.
  5816. inline const char *stros() {
  5817. #if defined(linux) || defined(__linux) || defined(__linux__)
  5818. static const char *const str = "Linux";
  5819. #elif defined(sun) || defined(__sun)
  5820. static const char *const str = "Sun OS";
  5821. #elif defined(BSD) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined (__DragonFly__)
  5822. static const char *const str = "BSD";
  5823. #elif defined(sgi) || defined(__sgi)
  5824. static const char *const str = "Irix";
  5825. #elif defined(__MACOSX__) || defined(__APPLE__)
  5826. static const char *const str = "Mac OS";
  5827. #elif defined(unix) || defined(__unix) || defined(__unix__)
  5828. static const char *const str = "Generic Unix";
  5829. #elif defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || \
  5830. defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
  5831. static const char *const str = "Windows";
  5832. #else
  5833. const char
  5834. *const _str1 = std::getenv("OSTYPE"),
  5835. *const _str2 = _str1?_str1:std::getenv("OS"),
  5836. *const str = _str2?_str2:"Unknown OS";
  5837. #endif
  5838. return str;
  5839. }
  5840. //! Return the basename of a filename.
  5841. inline const char* basename(const char *const s, const char separator=cimg_file_separator) {
  5842. const char *p = 0, *np = s;
  5843. while (np>=s && (p=np)) np = std::strchr(np,separator) + 1;
  5844. return p;
  5845. }
  5846. // Return a random filename.
  5847. inline const char* filenamerand() {
  5848. cimg::mutex(6);
  5849. static char randomid[9];
  5850. cimg::srand();
  5851. for (unsigned int k = 0; k<8; ++k) {
  5852. const int v = (int)cimg::rand(65535)%3;
  5853. randomid[k] = (char)(v==0?('0' + ((int)cimg::rand(65535)%10)):
  5854. (v==1?('a' + ((int)cimg::rand(65535)%26)):
  5855. ('A' + ((int)cimg::rand(65535)%26))));
  5856. }
  5857. cimg::mutex(6,0);
  5858. return randomid;
  5859. }
  5860. // Convert filename as a Windows-style filename (short path name).
  5861. inline void winformat_string(char *const str) {
  5862. if (str && *str) {
  5863. #if cimg_OS==2
  5864. char *const nstr = new char[MAX_PATH];
  5865. if (GetShortPathNameA(str,nstr,MAX_PATH)) std::strcpy(str,nstr);
  5866. delete[] nstr;
  5867. #endif
  5868. }
  5869. }
  5870. // Open a file (with wide character support on Windows).
  5871. inline std::FILE *win_fopen(const char *const path, const char *const mode);
  5872. //! Open a file.
  5873. /**
  5874. \param path Path of the filename to open.
  5875. \param mode C-string describing the opening mode.
  5876. \return Opened file.
  5877. \note Same as <tt>std::fopen()</tt> but throw a \c CImgIOException when
  5878. the specified file cannot be opened, instead of returning \c 0.
  5879. **/
  5880. inline std::FILE *fopen(const char *const path, const char *const mode) {
  5881. if (!path)
  5882. throw CImgArgumentException("cimg::fopen(): Specified file path is (null).");
  5883. if (!mode)
  5884. throw CImgArgumentException("cimg::fopen(): File '%s', specified mode is (null).",
  5885. path);
  5886. std::FILE *res = 0;
  5887. if (*path=='-' && (!path[1] || path[1]=='.')) {
  5888. res = (*mode=='r')?cimg::_stdin():cimg::_stdout();
  5889. #if cimg_OS==2
  5890. if (*mode && mode[1]=='b') { // Force stdin/stdout to be in binary mode.
  5891. #ifdef __BORLANDC__
  5892. if (setmode(_fileno(res),0x8000)==-1) res = 0;
  5893. #else
  5894. if (_setmode(_fileno(res),0x8000)==-1) res = 0;
  5895. #endif
  5896. }
  5897. #endif
  5898. } else res = std_fopen(path,mode);
  5899. if (!res) throw CImgIOException("cimg::fopen(): Failed to open file '%s' with mode '%s'.",
  5900. path,mode);
  5901. return res;
  5902. }
  5903. //! Close a file.
  5904. /**
  5905. \param file File to close.
  5906. \return \c 0 if file has been closed properly, something else otherwise.
  5907. \note Same as <tt>std::fclose()</tt> but display a warning message if
  5908. the file has not been closed properly.
  5909. **/
  5910. inline int fclose(std::FILE *file) {
  5911. if (!file) { warn("cimg::fclose(): Specified file is (null)."); return 0; }
  5912. if (file==cimg::_stdin(false) || file==cimg::_stdout(false)) return 0;
  5913. const int errn = std::fclose(file);
  5914. if (errn!=0) warn("cimg::fclose(): Error code %d returned during file closing.",
  5915. errn);
  5916. return errn;
  5917. }
  5918. //! Version of 'fseek()' that supports >=64bits offsets everywhere (for Windows).
  5919. inline int fseek(FILE *stream, cimg_long offset, int origin) {
  5920. #if defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
  5921. return _fseeki64(stream,(__int64)offset,origin);
  5922. #else
  5923. return std::fseek(stream,offset,origin);
  5924. #endif
  5925. }
  5926. //! Version of 'ftell()' that supports >=64bits offsets everywhere (for Windows).
  5927. inline cimg_long ftell(FILE *stream) {
  5928. #if defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
  5929. return (cimg_long)_ftelli64(stream);
  5930. #else
  5931. return (cimg_long)std::ftell(stream);
  5932. #endif
  5933. }
  5934. //! Check if a path is a directory.
  5935. /**
  5936. \param path Specified path to test.
  5937. **/
  5938. inline bool is_directory(const char *const path) {
  5939. if (!path || !*path) return false;
  5940. #if cimg_OS==1
  5941. struct stat st_buf;
  5942. return (!stat(path,&st_buf) && S_ISDIR(st_buf.st_mode));
  5943. #elif cimg_OS==2
  5944. const unsigned int res = (unsigned int)GetFileAttributesA(path);
  5945. return res==INVALID_FILE_ATTRIBUTES?false:(res&16);
  5946. #else
  5947. return false;
  5948. #endif
  5949. }
  5950. //! Check if a path is a file.
  5951. /**
  5952. \param path Specified path to test.
  5953. **/
  5954. inline bool is_file(const char *const path) {
  5955. if (!path || !*path) return false;
  5956. std::FILE *const file = std_fopen(path,"rb");
  5957. if (!file) return false;
  5958. std::fclose(file);
  5959. return !is_directory(path);
  5960. }
  5961. //! Get file size.
  5962. /**
  5963. \param filename Specified filename to get size from.
  5964. \return File size or '-1' if file does not exist.
  5965. **/
  5966. inline cimg_int64 fsize(const char *const filename) {
  5967. std::FILE *const file = std::fopen(filename,"rb");
  5968. if (!file) return (cimg_int64)-1;
  5969. std::fseek(file,0,SEEK_END);
  5970. const cimg_int64 siz = (cimg_int64)std::ftell(file);
  5971. std::fclose(file);
  5972. return siz;
  5973. }
  5974. //! Get last write time of a given file or directory (multiple-attributes version).
  5975. /**
  5976. \param path Specified path to get attributes from.
  5977. \param[in,out] attr Type of requested time attributes.
  5978. Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second }
  5979. Replaced by read attributes after return (or -1 if an error occured).
  5980. \param nb_attr Number of attributes to read/write.
  5981. \return Latest read attribute.
  5982. **/
  5983. template<typename T>
  5984. inline int fdate(const char *const path, T *attr, const unsigned int nb_attr) {
  5985. #define _cimg_fdate_err() for (unsigned int i = 0; i<nb_attr; ++i) attr[i] = (T)-1
  5986. int res = -1;
  5987. if (!path || !*path) { _cimg_fdate_err(); return -1; }
  5988. cimg::mutex(6);
  5989. #if cimg_OS==2
  5990. HANDLE file = CreateFileA(path,GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
  5991. if (file!=INVALID_HANDLE_VALUE) {
  5992. FILETIME _ft;
  5993. SYSTEMTIME ft;
  5994. if (GetFileTime(file,0,0,&_ft) && FileTimeToSystemTime(&_ft,&ft)) {
  5995. for (unsigned int i = 0; i<nb_attr; ++i) {
  5996. res = (int)(attr[i]==0?ft.wYear:attr[i]==1?ft.wMonth:attr[i]==2?ft.wDay:
  5997. attr[i]==3?ft.wDayOfWeek:attr[i]==4?ft.wHour:attr[i]==5?ft.wMinute:
  5998. attr[i]==6?ft.wSecond:-1);
  5999. attr[i] = (T)res;
  6000. }
  6001. } else _cimg_fdate_err();
  6002. CloseHandle(file);
  6003. } else _cimg_fdate_err();
  6004. #elif cimg_OS==1
  6005. struct stat st_buf;
  6006. if (!stat(path,&st_buf)) {
  6007. const time_t _ft = st_buf.st_mtime;
  6008. const struct tm& ft = *std::localtime(&_ft);
  6009. for (unsigned int i = 0; i<nb_attr; ++i) {
  6010. res = (int)(attr[i]==0?ft.tm_year + 1900:attr[i]==1?ft.tm_mon + 1:attr[i]==2?ft.tm_mday:
  6011. attr[i]==3?ft.tm_wday:attr[i]==4?ft.tm_hour:attr[i]==5?ft.tm_min:
  6012. attr[i]==6?ft.tm_sec:-1);
  6013. attr[i] = (T)res;
  6014. }
  6015. } else _cimg_fdate_err();
  6016. #endif
  6017. cimg::mutex(6,0);
  6018. return res;
  6019. }
  6020. //! Get last write time of a given file or directory (single-attribute version).
  6021. /**
  6022. \param path Specified path to get attributes from.
  6023. \param attr Type of requested time attributes.
  6024. Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second }
  6025. \return Specified attribute or -1 if an error occured.
  6026. **/
  6027. inline int fdate(const char *const path, unsigned int attr) {
  6028. int out = (int)attr;
  6029. return fdate(path,&out,1);
  6030. }
  6031. //! Get current local time (multiple-attributes version).
  6032. /**
  6033. \param[in,out] attr Type of requested time attributes.
  6034. Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second }
  6035. Replaced by read attributes after return (or -1 if an error occured).
  6036. \param nb_attr Number of attributes to read/write.
  6037. \return Latest read attribute.
  6038. **/
  6039. template<typename T>
  6040. inline int date(T *attr, const unsigned int nb_attr) {
  6041. int res = -1;
  6042. cimg::mutex(6);
  6043. #if cimg_OS==2
  6044. SYSTEMTIME st;
  6045. GetLocalTime(&st);
  6046. for (unsigned int i = 0; i<nb_attr; ++i) {
  6047. res = (int)(attr[i]==0?st.wYear:attr[i]==1?st.wMonth:attr[i]==2?st.wDay:
  6048. attr[i]==3?st.wDayOfWeek:attr[i]==4?st.wHour:attr[i]==5?st.wMinute:
  6049. attr[i]==6?st.wSecond:-1);
  6050. attr[i] = (T)res;
  6051. }
  6052. #else
  6053. time_t _st;
  6054. std::time(&_st);
  6055. struct tm *st = std::localtime(&_st);
  6056. for (unsigned int i = 0; i<nb_attr; ++i) {
  6057. res = (int)(attr[i]==0?st->tm_year + 1900:attr[i]==1?st->tm_mon + 1:attr[i]==2?st->tm_mday:
  6058. attr[i]==3?st->tm_wday:attr[i]==4?st->tm_hour:attr[i]==5?st->tm_min:
  6059. attr[i]==6?st->tm_sec:-1);
  6060. attr[i] = (T)res;
  6061. }
  6062. #endif
  6063. cimg::mutex(6,0);
  6064. return res;
  6065. }
  6066. //! Get current local time (single-attribute version).
  6067. /**
  6068. \param attr Type of requested time attribute.
  6069. Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second }
  6070. \return Specified attribute or -1 if an error occured.
  6071. **/
  6072. inline int date(unsigned int attr) {
  6073. int out = (int)attr;
  6074. return date(&out,1);
  6075. }
  6076. // Get/set path to store temporary files.
  6077. inline const char* temporary_path(const char *const user_path=0, const bool reinit_path=false);
  6078. // Get/set path to the <i>Program Files/</i> directory (Windows only).
  6079. #if cimg_OS==2
  6080. inline const char* programfiles_path(const char *const user_path=0, const bool reinit_path=false);
  6081. #endif
  6082. // Get/set path to the ImageMagick's \c convert binary.
  6083. inline const char* imagemagick_path(const char *const user_path=0, const bool reinit_path=false);
  6084. // Get/set path to the GraphicsMagick's \c gm binary.
  6085. inline const char* graphicsmagick_path(const char *const user_path=0, const bool reinit_path=false);
  6086. // Get/set path to the XMedcon's \c medcon binary.
  6087. inline const char* medcon_path(const char *const user_path=0, const bool reinit_path=false);
  6088. // Get/set path to the FFMPEG's \c ffmpeg binary.
  6089. inline const char *ffmpeg_path(const char *const user_path=0, const bool reinit_path=false);
  6090. // Get/set path to the \c gzip binary.
  6091. inline const char *gzip_path(const char *const user_path=0, const bool reinit_path=false);
  6092. // Get/set path to the \c gunzip binary.
  6093. inline const char *gunzip_path(const char *const user_path=0, const bool reinit_path=false);
  6094. // Get/set path to the \c dcraw binary.
  6095. inline const char *dcraw_path(const char *const user_path=0, const bool reinit_path=false);
  6096. // Get/set path to the \c wget binary.
  6097. inline const char *wget_path(const char *const user_path=0, const bool reinit_path=false);
  6098. // Get/set path to the \c curl binary.
  6099. inline const char *curl_path(const char *const user_path=0, const bool reinit_path=false);
  6100. //! Split filename into two C-strings \c body and \c extension.
  6101. /**
  6102. filename and body must not overlap!
  6103. **/
  6104. inline const char *split_filename(const char *const filename, char *const body=0) {
  6105. if (!filename) { if (body) *body = 0; return 0; }
  6106. const char *p = 0; for (const char *np = filename; np>=filename && (p=np); np = std::strchr(np,'.') + 1) {}
  6107. if (p==filename) {
  6108. if (body) std::strcpy(body,filename);
  6109. return filename + std::strlen(filename);
  6110. }
  6111. const unsigned int l = (unsigned int)(p - filename - 1);
  6112. if (body) { if (l) std::memcpy(body,filename,l); body[l] = 0; }
  6113. return p;
  6114. }
  6115. //! Generate a numbered version of a filename.
  6116. inline char* number_filename(const char *const filename, const int number,
  6117. const unsigned int digits, char *const str) {
  6118. if (!filename) { if (str) *str = 0; return 0; }
  6119. char *const format = new char[1024], *const body = new char[1024];
  6120. const char *const ext = cimg::split_filename(filename,body);
  6121. if (*ext) cimg_snprintf(format,1024,"%%s_%%.%ud.%%s",digits);
  6122. else cimg_snprintf(format,1024,"%%s_%%.%ud",digits);
  6123. cimg_sprintf(str,format,body,number,ext);
  6124. delete[] format; delete[] body;
  6125. return str;
  6126. }
  6127. //! Read data from file.
  6128. /**
  6129. \param[out] ptr Pointer to memory buffer that will contain the binary data read from file.
  6130. \param nmemb Number of elements to read.
  6131. \param stream File to read data from.
  6132. \return Number of read elements.
  6133. \note Same as <tt>std::fread()</tt> but may display warning message if all elements could not be read.
  6134. **/
  6135. template<typename T>
  6136. inline size_t fread(T *const ptr, const size_t nmemb, std::FILE *stream) {
  6137. if (!ptr || !stream)
  6138. throw CImgArgumentException("cimg::fread(): Invalid reading request of %u %s%s from file %p to buffer %p.",
  6139. nmemb,cimg::type<T>::string(),nmemb>1?"s":"",stream,ptr);
  6140. if (!nmemb) return 0;
  6141. const size_t wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T);
  6142. size_t to_read = nmemb, al_read = 0, l_to_read = 0, l_al_read = 0;
  6143. do {
  6144. l_to_read = (to_read*sizeof(T))<wlimitT?to_read:wlimit;
  6145. l_al_read = std::fread((void*)(ptr + al_read),sizeof(T),l_to_read,stream);
  6146. al_read+=l_al_read;
  6147. to_read-=l_al_read;
  6148. } while (l_to_read==l_al_read && to_read>0);
  6149. if (to_read>0)
  6150. warn("cimg::fread(): Only %lu/%lu elements could be read from file.",
  6151. (unsigned long)al_read,(unsigned long)nmemb);
  6152. return al_read;
  6153. }
  6154. //! Write data to file.
  6155. /**
  6156. \param ptr Pointer to memory buffer containing the binary data to write on file.
  6157. \param nmemb Number of elements to write.
  6158. \param[out] stream File to write data on.
  6159. \return Number of written elements.
  6160. \note Similar to <tt>std::fwrite</tt> but may display warning messages if all elements could not be written.
  6161. **/
  6162. template<typename T>
  6163. inline size_t fwrite(const T *ptr, const size_t nmemb, std::FILE *stream) {
  6164. if (!ptr || !stream)
  6165. throw CImgArgumentException("cimg::fwrite(): Invalid writing request of %u %s%s from buffer %p to file %p.",
  6166. nmemb,cimg::type<T>::string(),nmemb>1?"s":"",ptr,stream);
  6167. if (!nmemb) return 0;
  6168. const size_t wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T);
  6169. size_t to_write = nmemb, al_write = 0, l_to_write = 0, l_al_write = 0;
  6170. do {
  6171. l_to_write = (to_write*sizeof(T))<wlimitT?to_write:wlimit;
  6172. l_al_write = std::fwrite((void*)(ptr + al_write),sizeof(T),l_to_write,stream);
  6173. al_write+=l_al_write;
  6174. to_write-=l_al_write;
  6175. } while (l_to_write==l_al_write && to_write>0);
  6176. if (to_write>0)
  6177. warn("cimg::fwrite(): Only %lu/%lu elements could be written in file.",
  6178. (unsigned long)al_write,(unsigned long)nmemb);
  6179. return al_write;
  6180. }
  6181. //! Create an empty file.
  6182. /**
  6183. \param file Input file (can be \c 0 if \c filename is set).
  6184. \param filename Filename, as a C-string (can be \c 0 if \c file is set).
  6185. **/
  6186. inline void fempty(std::FILE *const file, const char *const filename) {
  6187. if (!file && !filename)
  6188. throw CImgArgumentException("cimg::fempty(): Specified filename is (null).");
  6189. std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
  6190. if (!file) cimg::fclose(nfile);
  6191. }
  6192. // Try to guess format from an image file.
  6193. inline const char *ftype(std::FILE *const file, const char *const filename);
  6194. // Load file from network as a local temporary file.
  6195. inline char *load_network(const char *const url, char *const filename_local,
  6196. const unsigned int timeout=0, const bool try_fallback=false,
  6197. const char *const referer=0);
  6198. //! Return options specified on the command line.
  6199. inline const char* option(const char *const name, const int argc, const char *const *const argv,
  6200. const char *const defaut, const char *const usage, const bool reset_static) {
  6201. static bool first = true, visu = false;
  6202. if (reset_static) { first = true; return 0; }
  6203. const char *res = 0;
  6204. if (first) {
  6205. first = false;
  6206. visu = cimg::option("-h",argc,argv,(char*)0,(char*)0,false)!=0;
  6207. visu |= cimg::option("-help",argc,argv,(char*)0,(char*)0,false)!=0;
  6208. visu |= cimg::option("--help",argc,argv,(char*)0,(char*)0,false)!=0;
  6209. }
  6210. if (!name && visu) {
  6211. if (usage) {
  6212. std::fprintf(cimg::output(),"\n %s%s%s",cimg::t_red,cimg::basename(argv[0]),cimg::t_normal);
  6213. std::fprintf(cimg::output(),": %s",usage);
  6214. std::fprintf(cimg::output()," (%s, %s)\n\n",cimg_date,cimg_time);
  6215. }
  6216. if (defaut) std::fprintf(cimg::output(),"%s\n",defaut);
  6217. }
  6218. if (name) {
  6219. if (argc>0) {
  6220. int k = 0;
  6221. while (k<argc && std::strcmp(argv[k],name)) ++k;
  6222. res = (k++==argc?defaut:(k==argc?argv[--k]:argv[k]));
  6223. } else res = defaut;
  6224. if (visu && usage) std::fprintf(cimg::output()," %s%-16s%s %-24s %s%s%s\n",
  6225. cimg::t_bold,name,cimg::t_normal,res?res:"0",
  6226. cimg::t_green,usage,cimg::t_normal);
  6227. }
  6228. return res;
  6229. }
  6230. inline const char* option(const char *const name, const int argc, const char *const *const argv,
  6231. const char *const defaut, const char *const usage=0) {
  6232. return option(name,argc,argv,defaut,usage,false);
  6233. }
  6234. inline bool option(const char *const name, const int argc, const char *const *const argv,
  6235. const bool defaut, const char *const usage=0) {
  6236. const char *const s = cimg::option(name,argc,argv,(char*)0);
  6237. const bool res = s?(cimg::strcasecmp(s,"false") && cimg::strcasecmp(s,"off") && cimg::strcasecmp(s,"0")):defaut;
  6238. cimg::option(name,0,0,res?"true":"false",usage);
  6239. return res;
  6240. }
  6241. inline int option(const char *const name, const int argc, const char *const *const argv,
  6242. const int defaut, const char *const usage=0) {
  6243. const char *const s = cimg::option(name,argc,argv,(char*)0);
  6244. const int res = s?std::atoi(s):defaut;
  6245. char *const tmp = new char[256];
  6246. cimg_snprintf(tmp,256,"%d",res);
  6247. cimg::option(name,0,0,tmp,usage);
  6248. delete[] tmp;
  6249. return res;
  6250. }
  6251. inline char option(const char *const name, const int argc, const char *const *const argv,
  6252. const char defaut, const char *const usage=0) {
  6253. const char *const s = cimg::option(name,argc,argv,(char*)0);
  6254. const char res = s?*s:defaut;
  6255. char tmp[8];
  6256. *tmp = res; tmp[1] = 0;
  6257. cimg::option(name,0,0,tmp,usage);
  6258. return res;
  6259. }
  6260. inline float option(const char *const name, const int argc, const char *const *const argv,
  6261. const float defaut, const char *const usage=0) {
  6262. const char *const s = cimg::option(name,argc,argv,(char*)0);
  6263. const float res = s?(float)cimg::atof(s):defaut;
  6264. char *const tmp = new char[256];
  6265. cimg_snprintf(tmp,256,"%g",res);
  6266. cimg::option(name,0,0,tmp,usage);
  6267. delete[] tmp;
  6268. return res;
  6269. }
  6270. inline double option(const char *const name, const int argc, const char *const *const argv,
  6271. const double defaut, const char *const usage=0) {
  6272. const char *const s = cimg::option(name,argc,argv,(char*)0);
  6273. const double res = s?cimg::atof(s):defaut;
  6274. char *const tmp = new char[256];
  6275. cimg_snprintf(tmp,256,"%g",res);
  6276. cimg::option(name,0,0,tmp,usage);
  6277. delete[] tmp;
  6278. return res;
  6279. }
  6280. //! Print information about \CImg environement variables.
  6281. /**
  6282. \note Output is done on the default output stream.
  6283. **/
  6284. inline void info() {
  6285. std::fprintf(cimg::output(),"\n %s%sCImg Library %u.%u.%u%s, compiled %s ( %s ) with the following flags:\n\n",
  6286. cimg::t_red,cimg::t_bold,cimg_version/100,(cimg_version/10)%10,cimg_version%10,
  6287. cimg::t_normal,cimg_date,cimg_time);
  6288. std::fprintf(cimg::output()," > Operating System: %s%-13s%s %s('cimg_OS'=%d)%s\n",
  6289. cimg::t_bold,
  6290. cimg_OS==1?"Unix":(cimg_OS==2?"Windows":"Unknow"),
  6291. cimg::t_normal,cimg::t_green,
  6292. cimg_OS,
  6293. cimg::t_normal);
  6294. std::fprintf(cimg::output()," > CPU endianness: %s%s Endian%s\n",
  6295. cimg::t_bold,
  6296. cimg::endianness()?"Big":"Little",
  6297. cimg::t_normal);
  6298. std::fprintf(cimg::output()," > Verbosity mode: %s%-13s%s %s('cimg_verbosity'=%d)%s\n",
  6299. cimg::t_bold,
  6300. cimg_verbosity==0?"Quiet":
  6301. cimg_verbosity==1?"Console":
  6302. cimg_verbosity==2?"Dialog":
  6303. cimg_verbosity==3?"Console+Warnings":"Dialog+Warnings",
  6304. cimg::t_normal,cimg::t_green,
  6305. cimg_verbosity,
  6306. cimg::t_normal);
  6307. std::fprintf(cimg::output()," > Stricts warnings: %s%-13s%s %s('cimg_strict_warnings' %s)%s\n",
  6308. cimg::t_bold,
  6309. #ifdef cimg_strict_warnings
  6310. "Yes",cimg::t_normal,cimg::t_green,"defined",
  6311. #else
  6312. "No",cimg::t_normal,cimg::t_green,"undefined",
  6313. #endif
  6314. cimg::t_normal);
  6315. std::fprintf(cimg::output()," > Support for C++11: %s%-13s%s %s('cimg_use_cpp11'=%d)%s\n",
  6316. cimg::t_bold,
  6317. cimg_use_cpp11?"Yes":"No",
  6318. cimg::t_normal,cimg::t_green,
  6319. (int)cimg_use_cpp11,
  6320. cimg::t_normal);
  6321. std::fprintf(cimg::output()," > Using VT100 messages: %s%-13s%s %s('cimg_use_vt100' %s)%s\n",
  6322. cimg::t_bold,
  6323. #ifdef cimg_use_vt100
  6324. "Yes",cimg::t_normal,cimg::t_green,"defined",
  6325. #else
  6326. "No",cimg::t_normal,cimg::t_green,"undefined",
  6327. #endif
  6328. cimg::t_normal);
  6329. std::fprintf(cimg::output()," > Display type: %s%-13s%s %s('cimg_display'=%d)%s\n",
  6330. cimg::t_bold,
  6331. cimg_display==0?"No display":cimg_display==1?"X11":cimg_display==2?"Windows GDI":"Unknown",
  6332. cimg::t_normal,cimg::t_green,
  6333. (int)cimg_display,
  6334. cimg::t_normal);
  6335. #if cimg_display==1
  6336. std::fprintf(cimg::output()," > Using XShm for X11: %s%-13s%s %s('cimg_use_xshm' %s)%s\n",
  6337. cimg::t_bold,
  6338. #ifdef cimg_use_xshm
  6339. "Yes",cimg::t_normal,cimg::t_green,"defined",
  6340. #else
  6341. "No",cimg::t_normal,cimg::t_green,"undefined",
  6342. #endif
  6343. cimg::t_normal);
  6344. std::fprintf(cimg::output()," > Using XRand for X11: %s%-13s%s %s('cimg_use_xrandr' %s)%s\n",
  6345. cimg::t_bold,
  6346. #ifdef cimg_use_xrandr
  6347. "Yes",cimg::t_normal,cimg::t_green,"defined",
  6348. #else
  6349. "No",cimg::t_normal,cimg::t_green,"undefined",
  6350. #endif
  6351. cimg::t_normal);
  6352. #endif
  6353. std::fprintf(cimg::output()," > Using OpenMP: %s%-13s%s %s('cimg_use_openmp' %s)%s\n",
  6354. cimg::t_bold,
  6355. #ifdef cimg_use_openmp
  6356. "Yes",cimg::t_normal,cimg::t_green,"defined",
  6357. #else
  6358. "No",cimg::t_normal,cimg::t_green,"undefined",
  6359. #endif
  6360. cimg::t_normal);
  6361. std::fprintf(cimg::output()," > Using PNG library: %s%-13s%s %s('cimg_use_png' %s)%s\n",
  6362. cimg::t_bold,
  6363. #ifdef cimg_use_png
  6364. "Yes",cimg::t_normal,cimg::t_green,"defined",
  6365. #else
  6366. "No",cimg::t_normal,cimg::t_green,"undefined",
  6367. #endif
  6368. cimg::t_normal);
  6369. std::fprintf(cimg::output()," > Using JPEG library: %s%-13s%s %s('cimg_use_jpeg' %s)%s\n",
  6370. cimg::t_bold,
  6371. #ifdef cimg_use_jpeg
  6372. "Yes",cimg::t_normal,cimg::t_green,"defined",
  6373. #else
  6374. "No",cimg::t_normal,cimg::t_green,"undefined",
  6375. #endif
  6376. cimg::t_normal);
  6377. std::fprintf(cimg::output()," > Using TIFF library: %s%-13s%s %s('cimg_use_tiff' %s)%s\n",
  6378. cimg::t_bold,
  6379. #ifdef cimg_use_tiff
  6380. "Yes",cimg::t_normal,cimg::t_green,"defined",
  6381. #else
  6382. "No",cimg::t_normal,cimg::t_green,"undefined",
  6383. #endif
  6384. cimg::t_normal);
  6385. std::fprintf(cimg::output()," > Using Magick++ library: %s%-13s%s %s('cimg_use_magick' %s)%s\n",
  6386. cimg::t_bold,
  6387. #ifdef cimg_use_magick
  6388. "Yes",cimg::t_normal,cimg::t_green,"defined",
  6389. #else
  6390. "No",cimg::t_normal,cimg::t_green,"undefined",
  6391. #endif
  6392. cimg::t_normal);
  6393. std::fprintf(cimg::output()," > Using FFTW3 library: %s%-13s%s %s('cimg_use_fftw3' %s)%s\n",
  6394. cimg::t_bold,
  6395. #ifdef cimg_use_fftw3
  6396. "Yes",cimg::t_normal,cimg::t_green,"defined",
  6397. #else
  6398. "No",cimg::t_normal,cimg::t_green,"undefined",
  6399. #endif
  6400. cimg::t_normal);
  6401. std::fprintf(cimg::output()," > Using LAPACK library: %s%-13s%s %s('cimg_use_lapack' %s)%s\n",
  6402. cimg::t_bold,
  6403. #ifdef cimg_use_lapack
  6404. "Yes",cimg::t_normal,cimg::t_green,"defined",
  6405. #else
  6406. "No",cimg::t_normal,cimg::t_green,"undefined",
  6407. #endif
  6408. cimg::t_normal);
  6409. char *const tmp = new char[1024];
  6410. cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::imagemagick_path());
  6411. std::fprintf(cimg::output()," > Path of ImageMagick: %s%-13s%s\n",
  6412. cimg::t_bold,
  6413. tmp,
  6414. cimg::t_normal);
  6415. cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::graphicsmagick_path());
  6416. std::fprintf(cimg::output()," > Path of GraphicsMagick: %s%-13s%s\n",
  6417. cimg::t_bold,
  6418. tmp,
  6419. cimg::t_normal);
  6420. cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::medcon_path());
  6421. std::fprintf(cimg::output()," > Path of 'medcon': %s%-13s%s\n",
  6422. cimg::t_bold,
  6423. tmp,
  6424. cimg::t_normal);
  6425. cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::temporary_path());
  6426. std::fprintf(cimg::output()," > Temporary path: %s%-13s%s\n",
  6427. cimg::t_bold,
  6428. tmp,
  6429. cimg::t_normal);
  6430. std::fprintf(cimg::output(),"\n");
  6431. delete[] tmp;
  6432. }
  6433. // Declare LAPACK function signatures if LAPACK support is enabled.
  6434. #ifdef cimg_use_lapack
  6435. template<typename T>
  6436. inline void getrf(int &N, T *lapA, int *IPIV, int &INFO) {
  6437. dgetrf_(&N,&N,lapA,&N,IPIV,&INFO);
  6438. }
  6439. inline void getrf(int &N, float *lapA, int *IPIV, int &INFO) {
  6440. sgetrf_(&N,&N,lapA,&N,IPIV,&INFO);
  6441. }
  6442. template<typename T>
  6443. inline void getri(int &N, T *lapA, int *IPIV, T* WORK, int &LWORK, int &INFO) {
  6444. dgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO);
  6445. }
  6446. inline void getri(int &N, float *lapA, int *IPIV, float* WORK, int &LWORK, int &INFO) {
  6447. sgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO);
  6448. }
  6449. template<typename T>
  6450. inline void gesvd(char &JOB, int &M, int &N, T *lapA, int &MN,
  6451. T *lapS, T *lapU, T *lapV, T *WORK, int &LWORK, int &INFO) {
  6452. dgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO);
  6453. }
  6454. inline void gesvd(char &JOB, int &M, int &N, float *lapA, int &MN,
  6455. float *lapS, float *lapU, float *lapV, float *WORK, int &LWORK, int &INFO) {
  6456. sgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO);
  6457. }
  6458. template<typename T>
  6459. inline void getrs(char &TRANS, int &N, T *lapA, int *IPIV, T *lapB, int &INFO) {
  6460. int one = 1;
  6461. dgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO);
  6462. }
  6463. inline void getrs(char &TRANS, int &N, float *lapA, int *IPIV, float *lapB, int &INFO) {
  6464. int one = 1;
  6465. sgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO);
  6466. }
  6467. template<typename T>
  6468. inline void syev(char &JOB, char &UPLO, int &N, T *lapA, T *lapW, T *WORK, int &LWORK, int &INFO) {
  6469. dsyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO);
  6470. }
  6471. inline void syev(char &JOB, char &UPLO, int &N, float *lapA, float *lapW, float *WORK, int &LWORK, int &INFO) {
  6472. ssyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO);
  6473. }
  6474. template<typename T>
  6475. inline void sgels(char & TRANS, int &M, int &N, int &NRHS, T* lapA, int &LDA,
  6476. T* lapB, int &LDB, T* WORK, int &LWORK, int &INFO){
  6477. dgels_(&TRANS, &M, &N, &NRHS, lapA, &LDA, lapB, &LDB, WORK, &LWORK, &INFO);
  6478. }
  6479. inline void sgels(char & TRANS, int &M, int &N, int &NRHS, float* lapA, int &LDA,
  6480. float* lapB, int &LDB, float* WORK, int &LWORK, int &INFO){
  6481. sgels_(&TRANS, &M, &N, &NRHS, lapA, &LDA, lapB, &LDB, WORK, &LWORK, &INFO);
  6482. }
  6483. #endif
  6484. // End of the 'cimg' namespace
  6485. }
  6486. /*------------------------------------------------
  6487. #
  6488. #
  6489. # Definition of mathematical operators and
  6490. # external functions.
  6491. #
  6492. #
  6493. -------------------------------------------------*/
  6494. #define _cimg_create_ext_operators(typ) \
  6495. template<typename T> \
  6496. inline CImg<typename cimg::superset<T,typ>::type> operator+(const typ val, const CImg<T>& img) { \
  6497. return img + val; \
  6498. } \
  6499. template<typename T> \
  6500. inline CImg<typename cimg::superset<T,typ>::type> operator-(const typ val, const CImg<T>& img) { \
  6501. typedef typename cimg::superset<T,typ>::type Tt; \
  6502. return CImg<Tt>(img._width,img._height,img._depth,img._spectrum,val)-=img; \
  6503. } \
  6504. template<typename T> \
  6505. inline CImg<typename cimg::superset<T,typ>::type> operator*(const typ val, const CImg<T>& img) { \
  6506. return img*val; \
  6507. } \
  6508. template<typename T> \
  6509. inline CImg<typename cimg::superset<T,typ>::type> operator/(const typ val, const CImg<T>& img) { \
  6510. return val*img.get_invert(); \
  6511. } \
  6512. template<typename T> \
  6513. inline CImg<typename cimg::superset<T,typ>::type> operator&(const typ val, const CImg<T>& img) { \
  6514. return img & val; \
  6515. } \
  6516. template<typename T> \
  6517. inline CImg<typename cimg::superset<T,typ>::type> operator|(const typ val, const CImg<T>& img) { \
  6518. return img | val; \
  6519. } \
  6520. template<typename T> \
  6521. inline CImg<typename cimg::superset<T,typ>::type> operator^(const typ val, const CImg<T>& img) { \
  6522. return img ^ val; \
  6523. } \
  6524. template<typename T> \
  6525. inline bool operator==(const typ val, const CImg<T>& img) { \
  6526. return img == val; \
  6527. } \
  6528. template<typename T> \
  6529. inline bool operator!=(const typ val, const CImg<T>& img) { \
  6530. return img != val; \
  6531. }
  6532. _cimg_create_ext_operators(bool)
  6533. _cimg_create_ext_operators(unsigned char)
  6534. _cimg_create_ext_operators(char)
  6535. _cimg_create_ext_operators(signed char)
  6536. _cimg_create_ext_operators(unsigned short)
  6537. _cimg_create_ext_operators(short)
  6538. _cimg_create_ext_operators(unsigned int)
  6539. _cimg_create_ext_operators(int)
  6540. _cimg_create_ext_operators(cimg_uint64)
  6541. _cimg_create_ext_operators(cimg_int64)
  6542. _cimg_create_ext_operators(float)
  6543. _cimg_create_ext_operators(double)
  6544. _cimg_create_ext_operators(long double)
  6545. template<typename T>
  6546. inline CImg<_cimg_Tfloat> operator+(const char *const expression, const CImg<T>& img) {
  6547. return img + expression;
  6548. }
  6549. template<typename T>
  6550. inline CImg<_cimg_Tfloat> operator-(const char *const expression, const CImg<T>& img) {
  6551. return CImg<_cimg_Tfloat>(img,false).fill(expression,true)-=img;
  6552. }
  6553. template<typename T>
  6554. inline CImg<_cimg_Tfloat> operator*(const char *const expression, const CImg<T>& img) {
  6555. return img*expression;
  6556. }
  6557. template<typename T>
  6558. inline CImg<_cimg_Tfloat> operator/(const char *const expression, const CImg<T>& img) {
  6559. return expression*img.get_invert();
  6560. }
  6561. template<typename T>
  6562. inline CImg<T> operator&(const char *const expression, const CImg<T>& img) {
  6563. return img & expression;
  6564. }
  6565. template<typename T>
  6566. inline CImg<T> operator|(const char *const expression, const CImg<T>& img) {
  6567. return img | expression;
  6568. }
  6569. template<typename T>
  6570. inline CImg<T> operator^(const char *const expression, const CImg<T>& img) {
  6571. return img ^ expression;
  6572. }
  6573. template<typename T>
  6574. inline bool operator==(const char *const expression, const CImg<T>& img) {
  6575. return img==expression;
  6576. }
  6577. template<typename T>
  6578. inline bool operator!=(const char *const expression, const CImg<T>& img) {
  6579. return img!=expression;
  6580. }
  6581. template<typename T>
  6582. inline CImg<_cimg_Tfloat> sqr(const CImg<T>& instance) {
  6583. return instance.get_sqr();
  6584. }
  6585. template<typename T>
  6586. inline CImg<_cimg_Tfloat> sqrt(const CImg<T>& instance) {
  6587. return instance.get_sqrt();
  6588. }
  6589. template<typename T>
  6590. inline CImg<_cimg_Tfloat> exp(const CImg<T>& instance) {
  6591. return instance.get_exp();
  6592. }
  6593. template<typename T>
  6594. inline CImg<_cimg_Tfloat> log(const CImg<T>& instance) {
  6595. return instance.get_log();
  6596. }
  6597. template<typename T>
  6598. inline CImg<_cimg_Tfloat> log2(const CImg<T>& instance) {
  6599. return instance.get_log2();
  6600. }
  6601. template<typename T>
  6602. inline CImg<_cimg_Tfloat> log10(const CImg<T>& instance) {
  6603. return instance.get_log10();
  6604. }
  6605. template<typename T>
  6606. inline CImg<_cimg_Tfloat> abs(const CImg<T>& instance) {
  6607. return instance.get_abs();
  6608. }
  6609. template<typename T>
  6610. inline CImg<_cimg_Tfloat> sign(const CImg<T>& instance) {
  6611. return instance.get_sign();
  6612. }
  6613. template<typename T>
  6614. inline CImg<_cimg_Tfloat> cos(const CImg<T>& instance) {
  6615. return instance.get_cos();
  6616. }
  6617. template<typename T>
  6618. inline CImg<_cimg_Tfloat> sin(const CImg<T>& instance) {
  6619. return instance.get_sin();
  6620. }
  6621. template<typename T>
  6622. inline CImg<_cimg_Tfloat> sinc(const CImg<T>& instance) {
  6623. return instance.get_sinc();
  6624. }
  6625. template<typename T>
  6626. inline CImg<_cimg_Tfloat> tan(const CImg<T>& instance) {
  6627. return instance.get_tan();
  6628. }
  6629. template<typename T>
  6630. inline CImg<_cimg_Tfloat> acos(const CImg<T>& instance) {
  6631. return instance.get_acos();
  6632. }
  6633. template<typename T>
  6634. inline CImg<_cimg_Tfloat> asin(const CImg<T>& instance) {
  6635. return instance.get_asin();
  6636. }
  6637. template<typename T>
  6638. inline CImg<_cimg_Tfloat> atan(const CImg<T>& instance) {
  6639. return instance.get_atan();
  6640. }
  6641. template<typename T>
  6642. inline CImg<_cimg_Tfloat> cosh(const CImg<T>& instance) {
  6643. return instance.get_cosh();
  6644. }
  6645. template<typename T>
  6646. inline CImg<_cimg_Tfloat> sinh(const CImg<T>& instance) {
  6647. return instance.get_sinh();
  6648. }
  6649. template<typename T>
  6650. inline CImg<_cimg_Tfloat> tanh(const CImg<T>& instance) {
  6651. return instance.get_tanh();
  6652. }
  6653. template<typename T>
  6654. inline CImg<T> transpose(const CImg<T>& instance) {
  6655. return instance.get_transpose();
  6656. }
  6657. template<typename T>
  6658. inline CImg<_cimg_Tfloat> invert(const CImg<T>& instance) {
  6659. return instance.get_invert();
  6660. }
  6661. template<typename T>
  6662. inline CImg<_cimg_Tfloat> pseudoinvert(const CImg<T>& instance) {
  6663. return instance.get_pseudoinvert();
  6664. }
  6665. /*-----------------------------------
  6666. #
  6667. # Define the CImgDisplay structure
  6668. #
  6669. ----------------------------------*/
  6670. //! Allow the creation of windows, display images on them and manage user events (keyboard, mouse and windows events).
  6671. /**
  6672. CImgDisplay methods rely on a low-level graphic library to perform: it can be either \b X-Window
  6673. (X11, for Unix-based systems) or \b GDI32 (for Windows-based systems).
  6674. If both libraries are missing, CImgDisplay will not be able to display images on screen, and will enter
  6675. a minimal mode where warning messages will be outputed each time the program is trying to call one of the
  6676. CImgDisplay method.
  6677. The configuration variable \c cimg_display tells about the graphic library used.
  6678. It is set automatically by \CImg when one of these graphic libraries has been detected.
  6679. But, you can override its value if necessary. Valid choices are:
  6680. - 0: Disable display capabilities.
  6681. - 1: Use \b X-Window (X11) library.
  6682. - 2: Use \b GDI32 library.
  6683. Remember to link your program against \b X11 or \b GDI32 libraries if you use CImgDisplay.
  6684. **/
  6685. struct CImgDisplay {
  6686. cimg_ulong _timer, _fps_frames, _fps_timer;
  6687. unsigned int _width, _height, _normalization;
  6688. float _fps_fps, _min, _max;
  6689. bool _is_fullscreen;
  6690. char *_title;
  6691. unsigned int _window_width, _window_height, _button, *_keys, *_released_keys;
  6692. int _window_x, _window_y, _mouse_x, _mouse_y, _wheel;
  6693. bool _is_closed, _is_resized, _is_moved, _is_event,
  6694. _is_keyESC, _is_keyF1, _is_keyF2, _is_keyF3, _is_keyF4, _is_keyF5, _is_keyF6, _is_keyF7,
  6695. _is_keyF8, _is_keyF9, _is_keyF10, _is_keyF11, _is_keyF12, _is_keyPAUSE, _is_key1, _is_key2,
  6696. _is_key3, _is_key4, _is_key5, _is_key6, _is_key7, _is_key8, _is_key9, _is_key0,
  6697. _is_keyBACKSPACE, _is_keyINSERT, _is_keyHOME, _is_keyPAGEUP, _is_keyTAB, _is_keyQ, _is_keyW, _is_keyE,
  6698. _is_keyR, _is_keyT, _is_keyY, _is_keyU, _is_keyI, _is_keyO, _is_keyP, _is_keyDELETE,
  6699. _is_keyEND, _is_keyPAGEDOWN, _is_keyCAPSLOCK, _is_keyA, _is_keyS, _is_keyD, _is_keyF, _is_keyG,
  6700. _is_keyH, _is_keyJ, _is_keyK, _is_keyL, _is_keyENTER, _is_keySHIFTLEFT, _is_keyZ, _is_keyX,
  6701. _is_keyC, _is_keyV, _is_keyB, _is_keyN, _is_keyM, _is_keySHIFTRIGHT, _is_keyARROWUP, _is_keyCTRLLEFT,
  6702. _is_keyAPPLEFT, _is_keyALT, _is_keySPACE, _is_keyALTGR, _is_keyAPPRIGHT, _is_keyMENU, _is_keyCTRLRIGHT,
  6703. _is_keyARROWLEFT, _is_keyARROWDOWN, _is_keyARROWRIGHT, _is_keyPAD0, _is_keyPAD1, _is_keyPAD2, _is_keyPAD3,
  6704. _is_keyPAD4, _is_keyPAD5, _is_keyPAD6, _is_keyPAD7, _is_keyPAD8, _is_keyPAD9, _is_keyPADADD, _is_keyPADSUB,
  6705. _is_keyPADMUL, _is_keyPADDIV;
  6706. //@}
  6707. //---------------------------
  6708. //
  6709. //! \name Plugins
  6710. //@{
  6711. //---------------------------
  6712. #ifdef cimgdisplay_plugin
  6713. #include cimgdisplay_plugin
  6714. #endif
  6715. #ifdef cimgdisplay_plugin1
  6716. #include cimgdisplay_plugin1
  6717. #endif
  6718. #ifdef cimgdisplay_plugin2
  6719. #include cimgdisplay_plugin2
  6720. #endif
  6721. #ifdef cimgdisplay_plugin3
  6722. #include cimgdisplay_plugin3
  6723. #endif
  6724. #ifdef cimgdisplay_plugin4
  6725. #include cimgdisplay_plugin4
  6726. #endif
  6727. #ifdef cimgdisplay_plugin5
  6728. #include cimgdisplay_plugin5
  6729. #endif
  6730. #ifdef cimgdisplay_plugin6
  6731. #include cimgdisplay_plugin6
  6732. #endif
  6733. #ifdef cimgdisplay_plugin7
  6734. #include cimgdisplay_plugin7
  6735. #endif
  6736. #ifdef cimgdisplay_plugin8
  6737. #include cimgdisplay_plugin8
  6738. #endif
  6739. //@}
  6740. //--------------------------------------------------------
  6741. //
  6742. //! \name Constructors / Destructor / Instance Management
  6743. //@{
  6744. //--------------------------------------------------------
  6745. //! Destructor.
  6746. /**
  6747. \note If the associated window is visible on the screen, it is closed by the call to the destructor.
  6748. **/
  6749. ~CImgDisplay() {
  6750. assign();
  6751. delete[] _keys;
  6752. delete[] _released_keys;
  6753. }
  6754. //! Construct an empty display.
  6755. /**
  6756. \note Constructing an empty CImgDisplay instance does not make a window appearing on the screen, until
  6757. display of valid data is performed.
  6758. \par Example
  6759. \code
  6760. CImgDisplay disp; // Does actually nothing.
  6761. ...
  6762. disp.display(img); // Construct new window and display image in it.
  6763. \endcode
  6764. **/
  6765. CImgDisplay():
  6766. _width(0),_height(0),_normalization(0),
  6767. _min(0),_max(0),
  6768. _is_fullscreen(false),
  6769. _title(0),
  6770. _window_width(0),_window_height(0),_button(0),
  6771. _keys(new unsigned int[128]),_released_keys(new unsigned int[128]),
  6772. _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0),
  6773. _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
  6774. assign();
  6775. }
  6776. //! Construct a display with specified dimensions.
  6777. /** \param width Window width.
  6778. \param height Window height.
  6779. \param title Window title.
  6780. \param normalization Normalization type
  6781. (<tt>0</tt>=none, <tt>1</tt>=always, <tt>2</tt>=once, <tt>3</tt>=pixel type-dependent, see normalization()).
  6782. \param is_fullscreen Tells if fullscreen mode is enabled.
  6783. \param is_closed Tells if associated window is initially visible or not.
  6784. \note A black background is initially displayed on the associated window.
  6785. **/
  6786. CImgDisplay(const unsigned int width, const unsigned int height,
  6787. const char *const title=0, const unsigned int normalization=3,
  6788. const bool is_fullscreen=false, const bool is_closed=false):
  6789. _width(0),_height(0),_normalization(0),
  6790. _min(0),_max(0),
  6791. _is_fullscreen(false),
  6792. _title(0),
  6793. _window_width(0),_window_height(0),_button(0),
  6794. _keys(new unsigned int[128]),_released_keys(new unsigned int[128]),
  6795. _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0),
  6796. _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
  6797. assign(width,height,title,normalization,is_fullscreen,is_closed);
  6798. }
  6799. //! Construct a display from an image.
  6800. /** \param img Image used as a model to create the window.
  6801. \param title Window title.
  6802. \param normalization Normalization type
  6803. (<tt>0</tt>=none, <tt>1</tt>=always, <tt>2</tt>=once, <tt>3</tt>=pixel type-dependent, see normalization()).
  6804. \param is_fullscreen Tells if fullscreen mode is enabled.
  6805. \param is_closed Tells if associated window is initially visible or not.
  6806. \note The pixels of the input image are initially displayed on the associated window.
  6807. **/
  6808. template<typename T>
  6809. explicit CImgDisplay(const CImg<T>& img,
  6810. const char *const title=0, const unsigned int normalization=3,
  6811. const bool is_fullscreen=false, const bool is_closed=false):
  6812. _width(0),_height(0),_normalization(0),
  6813. _min(0),_max(0),
  6814. _is_fullscreen(false),
  6815. _title(0),
  6816. _window_width(0),_window_height(0),_button(0),
  6817. _keys(new unsigned int[128]),_released_keys(new unsigned int[128]),
  6818. _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0),
  6819. _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
  6820. assign(img,title,normalization,is_fullscreen,is_closed);
  6821. }
  6822. //! Construct a display from an image list.
  6823. /** \param list The images list to display.
  6824. \param title Window title.
  6825. \param normalization Normalization type
  6826. (<tt>0</tt>=none, <tt>1</tt>=always, <tt>2</tt>=once, <tt>3</tt>=pixel type-dependent, see normalization()).
  6827. \param is_fullscreen Tells if fullscreen mode is enabled.
  6828. \param is_closed Tells if associated window is initially visible or not.
  6829. \note All images of the list, appended along the X-axis, are initially displayed on the associated window.
  6830. **/
  6831. template<typename T>
  6832. explicit CImgDisplay(const CImgList<T>& list,
  6833. const char *const title=0, const unsigned int normalization=3,
  6834. const bool is_fullscreen=false, const bool is_closed=false):
  6835. _width(0),_height(0),_normalization(0),
  6836. _min(0),_max(0),
  6837. _is_fullscreen(false),
  6838. _title(0),
  6839. _window_width(0),_window_height(0),_button(0),
  6840. _keys(new unsigned int[128]),_released_keys(new unsigned int[128]),
  6841. _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0),
  6842. _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
  6843. assign(list,title,normalization,is_fullscreen,is_closed);
  6844. }
  6845. //! Construct a display as a copy of an existing one.
  6846. /**
  6847. \param disp Display instance to copy.
  6848. \note The pixel buffer of the input window is initially displayed on the associated window.
  6849. **/
  6850. CImgDisplay(const CImgDisplay& disp):
  6851. _width(0),_height(0),_normalization(0),
  6852. _min(0),_max(0),
  6853. _is_fullscreen(false),
  6854. _title(0),
  6855. _window_width(0),_window_height(0),_button(0),
  6856. _keys(new unsigned int[128]),_released_keys(new unsigned int[128]),
  6857. _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0),
  6858. _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
  6859. assign(disp);
  6860. }
  6861. //! Take a screenshot.
  6862. /**
  6863. \param[out] img Output screenshot. Can be empty on input
  6864. **/
  6865. template<typename T>
  6866. static void screenshot(CImg<T>& img) {
  6867. return screenshot(0,0,cimg::type<int>::max(),cimg::type<int>::max(),img);
  6868. }
  6869. #if cimg_display==0
  6870. static void _no_display_exception() {
  6871. throw CImgDisplayException("CImgDisplay(): No display available.");
  6872. }
  6873. //! Destructor - Empty constructor \inplace.
  6874. /**
  6875. \note Replace the current instance by an empty display.
  6876. **/
  6877. CImgDisplay& assign() {
  6878. return flush();
  6879. }
  6880. //! Construct a display with specified dimensions \inplace.
  6881. /**
  6882. **/
  6883. CImgDisplay& assign(const unsigned int width, const unsigned int height,
  6884. const char *const title=0, const unsigned int normalization=3,
  6885. const bool is_fullscreen=false, const bool is_closed=false) {
  6886. cimg::unused(width,height,title,normalization,is_fullscreen,is_closed);
  6887. _no_display_exception();
  6888. return assign();
  6889. }
  6890. //! Construct a display from an image \inplace.
  6891. /**
  6892. **/
  6893. template<typename T>
  6894. CImgDisplay& assign(const CImg<T>& img,
  6895. const char *const title=0, const unsigned int normalization=3,
  6896. const bool is_fullscreen=false, const bool is_closed=false) {
  6897. _no_display_exception();
  6898. return assign(img._width,img._height,title,normalization,is_fullscreen,is_closed);
  6899. }
  6900. //! Construct a display from an image list \inplace.
  6901. /**
  6902. **/
  6903. template<typename T>
  6904. CImgDisplay& assign(const CImgList<T>& list,
  6905. const char *const title=0, const unsigned int normalization=3,
  6906. const bool is_fullscreen=false, const bool is_closed=false) {
  6907. _no_display_exception();
  6908. return assign(list._width,list._width,title,normalization,is_fullscreen,is_closed);
  6909. }
  6910. //! Construct a display as a copy of another one \inplace.
  6911. /**
  6912. **/
  6913. CImgDisplay& assign(const CImgDisplay &disp) {
  6914. _no_display_exception();
  6915. return assign(disp._width,disp._height);
  6916. }
  6917. #endif
  6918. //! Return a reference to an empty display.
  6919. /**
  6920. \note Can be useful for writing function prototypes where one of the argument (of type CImgDisplay&)
  6921. must have a default value.
  6922. \par Example
  6923. \code
  6924. void foo(CImgDisplay& disp=CImgDisplay::empty());
  6925. \endcode
  6926. **/
  6927. static CImgDisplay& empty() {
  6928. static CImgDisplay _empty;
  6929. return _empty.assign();
  6930. }
  6931. //! Return a reference to an empty display \const.
  6932. static const CImgDisplay& const_empty() {
  6933. static const CImgDisplay _empty;
  6934. return _empty;
  6935. }
  6936. #define cimg_fitscreen(dx,dy,dz) CImgDisplay::_fitscreen(dx,dy,dz,480,-85,false), \
  6937. CImgDisplay::_fitscreen(dx,dy,dz,480,-85,true)
  6938. static unsigned int _fitscreen(const unsigned int dx, const unsigned int dy, const unsigned int dz,
  6939. const int dmin, const int dmax, const bool return_y) {
  6940. const unsigned int _nw = dx + (dz>1?dz:0), _nh = dy + (dz>1?dz:0);
  6941. unsigned int nw = _nw?_nw:1, nh = _nh?_nh:1;
  6942. const unsigned int
  6943. sw = (unsigned int)CImgDisplay::screen_width(),
  6944. sh = (unsigned int)CImgDisplay::screen_height(),
  6945. mw = dmin<0?(unsigned int)(sw*-dmin/100):(unsigned int)dmin,
  6946. mh = dmin<0?(unsigned int)(sh*-dmin/100):(unsigned int)dmin,
  6947. Mw = dmax<0?(unsigned int)(sw*-dmax/100):(unsigned int)dmax,
  6948. Mh = dmax<0?(unsigned int)(sh*-dmax/100):(unsigned int)dmax;
  6949. if (nw<mw) { nh = nh*mw/nw; nh+=(nh==0); nw = mw; }
  6950. if (nh<mh) { nw = nw*mh/nh; nw+=(nw==0); nh = mh; }
  6951. if (nw>Mw) { nh = nh*Mw/nw; nh+=(nh==0); nw = Mw; }
  6952. if (nh>Mh) { nw = nw*Mh/nh; nw+=(nw==0); nh = Mh; }
  6953. if (nw<mw) nw = mw;
  6954. if (nh<mh) nh = mh;
  6955. return return_y?nh:nw;
  6956. }
  6957. //@}
  6958. //------------------------------------------
  6959. //
  6960. //! \name Overloaded Operators
  6961. //@{
  6962. //------------------------------------------
  6963. //! Display image on associated window.
  6964. /**
  6965. \note <tt>disp = img</tt> is equivalent to <tt>disp.display(img)</tt>.
  6966. **/
  6967. template<typename t>
  6968. CImgDisplay& operator=(const CImg<t>& img) {
  6969. return display(img);
  6970. }
  6971. //! Display list of images on associated window.
  6972. /**
  6973. \note <tt>disp = list</tt> is equivalent to <tt>disp.display(list)</tt>.
  6974. **/
  6975. template<typename t>
  6976. CImgDisplay& operator=(const CImgList<t>& list) {
  6977. return display(list);
  6978. }
  6979. //! Construct a display as a copy of another one \inplace.
  6980. /**
  6981. \note Equivalent to assign(const CImgDisplay&).
  6982. **/
  6983. CImgDisplay& operator=(const CImgDisplay& disp) {
  6984. return assign(disp);
  6985. }
  6986. //! Return \c false if display is empty, \c true otherwise.
  6987. /**
  6988. \note <tt>if (disp) { ... }</tt> is equivalent to <tt>if (!disp.is_empty()) { ... }</tt>.
  6989. **/
  6990. operator bool() const {
  6991. return !is_empty();
  6992. }
  6993. //@}
  6994. //------------------------------------------
  6995. //
  6996. //! \name Instance Checking
  6997. //@{
  6998. //------------------------------------------
  6999. //! Return \c true if display is empty, \c false otherwise.
  7000. /**
  7001. **/
  7002. bool is_empty() const {
  7003. return !(_width && _height);
  7004. }
  7005. //! Return \c true if display is closed (i.e. not visible on the screen), \c false otherwise.
  7006. /**
  7007. \note
  7008. - When a user physically closes the associated window, the display is set to closed.
  7009. - A closed display is not destroyed. Its associated window can be show again on the screen using show().
  7010. **/
  7011. bool is_closed() const {
  7012. return _is_closed;
  7013. }
  7014. //! Return \c true if associated window has been resized on the screen, \c false otherwise.
  7015. /**
  7016. **/
  7017. bool is_resized() const {
  7018. return _is_resized;
  7019. }
  7020. //! Return \c true if associated window has been moved on the screen, \c false otherwise.
  7021. /**
  7022. **/
  7023. bool is_moved() const {
  7024. return _is_moved;
  7025. }
  7026. //! Return \c true if any event has occured on the associated window, \c false otherwise.
  7027. /**
  7028. **/
  7029. bool is_event() const {
  7030. return _is_event;
  7031. }
  7032. //! Return \c true if current display is in fullscreen mode, \c false otherwise.
  7033. /**
  7034. **/
  7035. bool is_fullscreen() const {
  7036. return _is_fullscreen;
  7037. }
  7038. //! Return \c true if any key is being pressed on the associated window, \c false otherwise.
  7039. /**
  7040. \note The methods below do the same only for specific keys.
  7041. **/
  7042. bool is_key() const {
  7043. return _is_keyESC || _is_keyF1 || _is_keyF2 || _is_keyF3 ||
  7044. _is_keyF4 || _is_keyF5 || _is_keyF6 || _is_keyF7 ||
  7045. _is_keyF8 || _is_keyF9 || _is_keyF10 || _is_keyF11 ||
  7046. _is_keyF12 || _is_keyPAUSE || _is_key1 || _is_key2 ||
  7047. _is_key3 || _is_key4 || _is_key5 || _is_key6 ||
  7048. _is_key7 || _is_key8 || _is_key9 || _is_key0 ||
  7049. _is_keyBACKSPACE || _is_keyINSERT || _is_keyHOME ||
  7050. _is_keyPAGEUP || _is_keyTAB || _is_keyQ || _is_keyW ||
  7051. _is_keyE || _is_keyR || _is_keyT || _is_keyY ||
  7052. _is_keyU || _is_keyI || _is_keyO || _is_keyP ||
  7053. _is_keyDELETE || _is_keyEND || _is_keyPAGEDOWN ||
  7054. _is_keyCAPSLOCK || _is_keyA || _is_keyS || _is_keyD ||
  7055. _is_keyF || _is_keyG || _is_keyH || _is_keyJ ||
  7056. _is_keyK || _is_keyL || _is_keyENTER ||
  7057. _is_keySHIFTLEFT || _is_keyZ || _is_keyX || _is_keyC ||
  7058. _is_keyV || _is_keyB || _is_keyN || _is_keyM ||
  7059. _is_keySHIFTRIGHT || _is_keyARROWUP || _is_keyCTRLLEFT ||
  7060. _is_keyAPPLEFT || _is_keyALT || _is_keySPACE || _is_keyALTGR ||
  7061. _is_keyAPPRIGHT || _is_keyMENU || _is_keyCTRLRIGHT ||
  7062. _is_keyARROWLEFT || _is_keyARROWDOWN || _is_keyARROWRIGHT ||
  7063. _is_keyPAD0 || _is_keyPAD1 || _is_keyPAD2 ||
  7064. _is_keyPAD3 || _is_keyPAD4 || _is_keyPAD5 ||
  7065. _is_keyPAD6 || _is_keyPAD7 || _is_keyPAD8 ||
  7066. _is_keyPAD9 || _is_keyPADADD || _is_keyPADSUB ||
  7067. _is_keyPADMUL || _is_keyPADDIV;
  7068. }
  7069. //! Return \c true if key specified by given keycode is being pressed on the associated window, \c false otherwise.
  7070. /**
  7071. \param keycode Keycode to test.
  7072. \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
  7073. your code stay portable (see cimg::keyESC).
  7074. \par Example
  7075. \code
  7076. CImgDisplay disp(400,400);
  7077. while (!disp.is_closed()) {
  7078. if (disp.key(cimg::keyTAB)) { ... } // Equivalent to 'if (disp.is_keyTAB())'.
  7079. disp.wait();
  7080. }
  7081. \endcode
  7082. **/
  7083. bool is_key(const unsigned int keycode) const {
  7084. #define _cimg_iskey_test(k) if (keycode==cimg::key##k) return _is_key##k;
  7085. _cimg_iskey_test(ESC); _cimg_iskey_test(F1); _cimg_iskey_test(F2); _cimg_iskey_test(F3);
  7086. _cimg_iskey_test(F4); _cimg_iskey_test(F5); _cimg_iskey_test(F6); _cimg_iskey_test(F7);
  7087. _cimg_iskey_test(F8); _cimg_iskey_test(F9); _cimg_iskey_test(F10); _cimg_iskey_test(F11);
  7088. _cimg_iskey_test(F12); _cimg_iskey_test(PAUSE); _cimg_iskey_test(1); _cimg_iskey_test(2);
  7089. _cimg_iskey_test(3); _cimg_iskey_test(4); _cimg_iskey_test(5); _cimg_iskey_test(6);
  7090. _cimg_iskey_test(7); _cimg_iskey_test(8); _cimg_iskey_test(9); _cimg_iskey_test(0);
  7091. _cimg_iskey_test(BACKSPACE); _cimg_iskey_test(INSERT); _cimg_iskey_test(HOME);
  7092. _cimg_iskey_test(PAGEUP); _cimg_iskey_test(TAB); _cimg_iskey_test(Q); _cimg_iskey_test(W);
  7093. _cimg_iskey_test(E); _cimg_iskey_test(R); _cimg_iskey_test(T); _cimg_iskey_test(Y);
  7094. _cimg_iskey_test(U); _cimg_iskey_test(I); _cimg_iskey_test(O); _cimg_iskey_test(P);
  7095. _cimg_iskey_test(DELETE); _cimg_iskey_test(END); _cimg_iskey_test(PAGEDOWN);
  7096. _cimg_iskey_test(CAPSLOCK); _cimg_iskey_test(A); _cimg_iskey_test(S); _cimg_iskey_test(D);
  7097. _cimg_iskey_test(F); _cimg_iskey_test(G); _cimg_iskey_test(H); _cimg_iskey_test(J);
  7098. _cimg_iskey_test(K); _cimg_iskey_test(L); _cimg_iskey_test(ENTER);
  7099. _cimg_iskey_test(SHIFTLEFT); _cimg_iskey_test(Z); _cimg_iskey_test(X); _cimg_iskey_test(C);
  7100. _cimg_iskey_test(V); _cimg_iskey_test(B); _cimg_iskey_test(N); _cimg_iskey_test(M);
  7101. _cimg_iskey_test(SHIFTRIGHT); _cimg_iskey_test(ARROWUP); _cimg_iskey_test(CTRLLEFT);
  7102. _cimg_iskey_test(APPLEFT); _cimg_iskey_test(ALT); _cimg_iskey_test(SPACE); _cimg_iskey_test(ALTGR);
  7103. _cimg_iskey_test(APPRIGHT); _cimg_iskey_test(MENU); _cimg_iskey_test(CTRLRIGHT);
  7104. _cimg_iskey_test(ARROWLEFT); _cimg_iskey_test(ARROWDOWN); _cimg_iskey_test(ARROWRIGHT);
  7105. _cimg_iskey_test(PAD0); _cimg_iskey_test(PAD1); _cimg_iskey_test(PAD2);
  7106. _cimg_iskey_test(PAD3); _cimg_iskey_test(PAD4); _cimg_iskey_test(PAD5);
  7107. _cimg_iskey_test(PAD6); _cimg_iskey_test(PAD7); _cimg_iskey_test(PAD8);
  7108. _cimg_iskey_test(PAD9); _cimg_iskey_test(PADADD); _cimg_iskey_test(PADSUB);
  7109. _cimg_iskey_test(PADMUL); _cimg_iskey_test(PADDIV);
  7110. return false;
  7111. }
  7112. //! Return \c true if key specified by given keycode is being pressed on the associated window, \c false otherwise.
  7113. /**
  7114. \param keycode C-string containing the keycode label of the key to test.
  7115. \note Use it when the key you want to test can be dynamically set by the user.
  7116. \par Example
  7117. \code
  7118. CImgDisplay disp(400,400);
  7119. const char *const keycode = "TAB";
  7120. while (!disp.is_closed()) {
  7121. if (disp.is_key(keycode)) { ... } // Equivalent to 'if (disp.is_keyTAB())'.
  7122. disp.wait();
  7123. }
  7124. \endcode
  7125. **/
  7126. bool& is_key(const char *const keycode) {
  7127. static bool f = false;
  7128. f = false;
  7129. #define _cimg_iskey_test2(k) if (!cimg::strcasecmp(keycode,#k)) return _is_key##k;
  7130. _cimg_iskey_test2(ESC); _cimg_iskey_test2(F1); _cimg_iskey_test2(F2); _cimg_iskey_test2(F3);
  7131. _cimg_iskey_test2(F4); _cimg_iskey_test2(F5); _cimg_iskey_test2(F6); _cimg_iskey_test2(F7);
  7132. _cimg_iskey_test2(F8); _cimg_iskey_test2(F9); _cimg_iskey_test2(F10); _cimg_iskey_test2(F11);
  7133. _cimg_iskey_test2(F12); _cimg_iskey_test2(PAUSE); _cimg_iskey_test2(1); _cimg_iskey_test2(2);
  7134. _cimg_iskey_test2(3); _cimg_iskey_test2(4); _cimg_iskey_test2(5); _cimg_iskey_test2(6);
  7135. _cimg_iskey_test2(7); _cimg_iskey_test2(8); _cimg_iskey_test2(9); _cimg_iskey_test2(0);
  7136. _cimg_iskey_test2(BACKSPACE); _cimg_iskey_test2(INSERT); _cimg_iskey_test2(HOME);
  7137. _cimg_iskey_test2(PAGEUP); _cimg_iskey_test2(TAB); _cimg_iskey_test2(Q); _cimg_iskey_test2(W);
  7138. _cimg_iskey_test2(E); _cimg_iskey_test2(R); _cimg_iskey_test2(T); _cimg_iskey_test2(Y);
  7139. _cimg_iskey_test2(U); _cimg_iskey_test2(I); _cimg_iskey_test2(O); _cimg_iskey_test2(P);
  7140. _cimg_iskey_test2(DELETE); _cimg_iskey_test2(END); _cimg_iskey_test2(PAGEDOWN);
  7141. _cimg_iskey_test2(CAPSLOCK); _cimg_iskey_test2(A); _cimg_iskey_test2(S); _cimg_iskey_test2(D);
  7142. _cimg_iskey_test2(F); _cimg_iskey_test2(G); _cimg_iskey_test2(H); _cimg_iskey_test2(J);
  7143. _cimg_iskey_test2(K); _cimg_iskey_test2(L); _cimg_iskey_test2(ENTER);
  7144. _cimg_iskey_test2(SHIFTLEFT); _cimg_iskey_test2(Z); _cimg_iskey_test2(X); _cimg_iskey_test2(C);
  7145. _cimg_iskey_test2(V); _cimg_iskey_test2(B); _cimg_iskey_test2(N); _cimg_iskey_test2(M);
  7146. _cimg_iskey_test2(SHIFTRIGHT); _cimg_iskey_test2(ARROWUP); _cimg_iskey_test2(CTRLLEFT);
  7147. _cimg_iskey_test2(APPLEFT); _cimg_iskey_test2(ALT); _cimg_iskey_test2(SPACE); _cimg_iskey_test2(ALTGR);
  7148. _cimg_iskey_test2(APPRIGHT); _cimg_iskey_test2(MENU); _cimg_iskey_test2(CTRLRIGHT);
  7149. _cimg_iskey_test2(ARROWLEFT); _cimg_iskey_test2(ARROWDOWN); _cimg_iskey_test2(ARROWRIGHT);
  7150. _cimg_iskey_test2(PAD0); _cimg_iskey_test2(PAD1); _cimg_iskey_test2(PAD2);
  7151. _cimg_iskey_test2(PAD3); _cimg_iskey_test2(PAD4); _cimg_iskey_test2(PAD5);
  7152. _cimg_iskey_test2(PAD6); _cimg_iskey_test2(PAD7); _cimg_iskey_test2(PAD8);
  7153. _cimg_iskey_test2(PAD9); _cimg_iskey_test2(PADADD); _cimg_iskey_test2(PADSUB);
  7154. _cimg_iskey_test2(PADMUL); _cimg_iskey_test2(PADDIV);
  7155. return f;
  7156. }
  7157. //! Return \c true if specified key sequence has been typed on the associated window, \c false otherwise.
  7158. /**
  7159. \param keycodes_sequence Buffer of keycodes to test.
  7160. \param length Number of keys in the \c keycodes_sequence buffer.
  7161. \param remove_sequence Tells if the key sequence must be removed from the key history, if found.
  7162. \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
  7163. your code stay portable (see cimg::keyESC).
  7164. \par Example
  7165. \code
  7166. CImgDisplay disp(400,400);
  7167. const unsigned int key_seq[] = { cimg::keyCTRLLEFT, cimg::keyD };
  7168. while (!disp.is_closed()) {
  7169. if (disp.is_key_sequence(key_seq,2)) { ... } // Test for the 'CTRL+D' keyboard event.
  7170. disp.wait();
  7171. }
  7172. \endcode
  7173. **/
  7174. bool is_key_sequence(const unsigned int *const keycodes_sequence, const unsigned int length,
  7175. const bool remove_sequence=false) {
  7176. if (keycodes_sequence && length) {
  7177. const unsigned int
  7178. *const ps_end = keycodes_sequence + length - 1,
  7179. *const pk_end = (unsigned int*)_keys + 1 + 128 - length,
  7180. k = *ps_end;
  7181. for (unsigned int *pk = (unsigned int*)_keys; pk<pk_end; ) {
  7182. if (*(pk++)==k) {
  7183. bool res = true;
  7184. const unsigned int *ps = ps_end, *pk2 = pk;
  7185. for (unsigned int i = 1; i<length; ++i) res = (*(--ps)==*(pk2++));
  7186. if (res) {
  7187. if (remove_sequence) std::memset((void*)(pk - 1),0,sizeof(unsigned int)*length);
  7188. return true;
  7189. }
  7190. }
  7191. }
  7192. }
  7193. return false;
  7194. }
  7195. #define _cimg_iskey_def(k) \
  7196. bool is_key##k() const { \
  7197. return _is_key##k; \
  7198. }
  7199. //! Return \c true if the \c ESC key is being pressed on the associated window, \c false otherwise.
  7200. /**
  7201. \note Similar methods exist for all keys managed by \CImg (see cimg::keyESC).
  7202. **/
  7203. _cimg_iskey_def(ESC); _cimg_iskey_def(F1); _cimg_iskey_def(F2); _cimg_iskey_def(F3);
  7204. _cimg_iskey_def(F4); _cimg_iskey_def(F5); _cimg_iskey_def(F6); _cimg_iskey_def(F7);
  7205. _cimg_iskey_def(F8); _cimg_iskey_def(F9); _cimg_iskey_def(F10); _cimg_iskey_def(F11);
  7206. _cimg_iskey_def(F12); _cimg_iskey_def(PAUSE); _cimg_iskey_def(1); _cimg_iskey_def(2);
  7207. _cimg_iskey_def(3); _cimg_iskey_def(4); _cimg_iskey_def(5); _cimg_iskey_def(6);
  7208. _cimg_iskey_def(7); _cimg_iskey_def(8); _cimg_iskey_def(9); _cimg_iskey_def(0);
  7209. _cimg_iskey_def(BACKSPACE); _cimg_iskey_def(INSERT); _cimg_iskey_def(HOME);
  7210. _cimg_iskey_def(PAGEUP); _cimg_iskey_def(TAB); _cimg_iskey_def(Q); _cimg_iskey_def(W);
  7211. _cimg_iskey_def(E); _cimg_iskey_def(R); _cimg_iskey_def(T); _cimg_iskey_def(Y);
  7212. _cimg_iskey_def(U); _cimg_iskey_def(I); _cimg_iskey_def(O); _cimg_iskey_def(P);
  7213. _cimg_iskey_def(DELETE); _cimg_iskey_def(END); _cimg_iskey_def(PAGEDOWN);
  7214. _cimg_iskey_def(CAPSLOCK); _cimg_iskey_def(A); _cimg_iskey_def(S); _cimg_iskey_def(D);
  7215. _cimg_iskey_def(F); _cimg_iskey_def(G); _cimg_iskey_def(H); _cimg_iskey_def(J);
  7216. _cimg_iskey_def(K); _cimg_iskey_def(L); _cimg_iskey_def(ENTER);
  7217. _cimg_iskey_def(SHIFTLEFT); _cimg_iskey_def(Z); _cimg_iskey_def(X); _cimg_iskey_def(C);
  7218. _cimg_iskey_def(V); _cimg_iskey_def(B); _cimg_iskey_def(N); _cimg_iskey_def(M);
  7219. _cimg_iskey_def(SHIFTRIGHT); _cimg_iskey_def(ARROWUP); _cimg_iskey_def(CTRLLEFT);
  7220. _cimg_iskey_def(APPLEFT); _cimg_iskey_def(ALT); _cimg_iskey_def(SPACE); _cimg_iskey_def(ALTGR);
  7221. _cimg_iskey_def(APPRIGHT); _cimg_iskey_def(MENU); _cimg_iskey_def(CTRLRIGHT);
  7222. _cimg_iskey_def(ARROWLEFT); _cimg_iskey_def(ARROWDOWN); _cimg_iskey_def(ARROWRIGHT);
  7223. _cimg_iskey_def(PAD0); _cimg_iskey_def(PAD1); _cimg_iskey_def(PAD2);
  7224. _cimg_iskey_def(PAD3); _cimg_iskey_def(PAD4); _cimg_iskey_def(PAD5);
  7225. _cimg_iskey_def(PAD6); _cimg_iskey_def(PAD7); _cimg_iskey_def(PAD8);
  7226. _cimg_iskey_def(PAD9); _cimg_iskey_def(PADADD); _cimg_iskey_def(PADSUB);
  7227. _cimg_iskey_def(PADMUL); _cimg_iskey_def(PADDIV);
  7228. //@}
  7229. //------------------------------------------
  7230. //
  7231. //! \name Instance Characteristics
  7232. //@{
  7233. //------------------------------------------
  7234. #if cimg_display==0
  7235. //! Return width of the screen (current resolution along the X-axis).
  7236. /**
  7237. **/
  7238. static int screen_width() {
  7239. _no_display_exception();
  7240. return 0;
  7241. }
  7242. //! Return height of the screen (current resolution along the Y-axis).
  7243. /**
  7244. **/
  7245. static int screen_height() {
  7246. _no_display_exception();
  7247. return 0;
  7248. }
  7249. #endif
  7250. //! Return display width.
  7251. /**
  7252. \note The width of the display (i.e. the width of the pixel data buffer associated to the CImgDisplay instance)
  7253. may be different from the actual width of the associated window.
  7254. **/
  7255. int width() const {
  7256. return (int)_width;
  7257. }
  7258. //! Return display height.
  7259. /**
  7260. \note The height of the display (i.e. the height of the pixel data buffer associated to the CImgDisplay instance)
  7261. may be different from the actual height of the associated window.
  7262. **/
  7263. int height() const {
  7264. return (int)_height;
  7265. }
  7266. //! Return normalization type of the display.
  7267. /**
  7268. The normalization type tells about how the values of an input image are normalized by the CImgDisplay to be
  7269. correctly displayed. The range of values for pixels displayed on screen is <tt>[0,255]</tt>.
  7270. If the range of values of the data to display is different, a normalization may be required for displaying
  7271. the data in a correct way. The normalization type can be one of:
  7272. - \c 0: Value normalization is disabled. It is then assumed that all input data to be displayed by the
  7273. CImgDisplay instance have values in range <tt>[0,255]</tt>.
  7274. - \c 1: Value normalization is always performed (this is the default behavior).
  7275. Before displaying an input image, its values will be (virtually) stretched
  7276. in range <tt>[0,255]</tt>, so that the contrast of the displayed pixels will be maximum.
  7277. Use this mode for images whose minimum and maximum values are not prescribed to known values
  7278. (e.g. float-valued images).
  7279. Note that when normalized versions of images are computed for display purposes, the actual values of these
  7280. images are not modified.
  7281. - \c 2: Value normalization is performed once (on the first image display), then the same normalization
  7282. coefficients are kept for next displayed frames.
  7283. - \c 3: Value normalization depends on the pixel type of the data to display. For integer pixel types,
  7284. the normalization is done regarding the minimum/maximum values of the type (no normalization occurs then
  7285. for <tt>unsigned char</tt>).
  7286. For float-valued pixel types, the normalization is done regarding the minimum/maximum value of the image
  7287. data instead.
  7288. **/
  7289. unsigned int normalization() const {
  7290. return _normalization;
  7291. }
  7292. //! Return title of the associated window as a C-string.
  7293. /**
  7294. \note Window title may be not visible, depending on the used window manager or if the current display is
  7295. in fullscreen mode.
  7296. **/
  7297. const char *title() const {
  7298. return _title?_title:"";
  7299. }
  7300. //! Return width of the associated window.
  7301. /**
  7302. \note The width of the display (i.e. the width of the pixel data buffer associated to the CImgDisplay instance)
  7303. may be different from the actual width of the associated window.
  7304. **/
  7305. int window_width() const {
  7306. return (int)_window_width;
  7307. }
  7308. //! Return height of the associated window.
  7309. /**
  7310. \note The height of the display (i.e. the height of the pixel data buffer associated to the CImgDisplay instance)
  7311. may be different from the actual height of the associated window.
  7312. **/
  7313. int window_height() const {
  7314. return (int)_window_height;
  7315. }
  7316. //! Return X-coordinate of the associated window.
  7317. /**
  7318. \note The returned coordinate corresponds to the location of the upper-left corner of the associated window.
  7319. **/
  7320. int window_x() const {
  7321. return _window_x;
  7322. }
  7323. //! Return Y-coordinate of the associated window.
  7324. /**
  7325. \note The returned coordinate corresponds to the location of the upper-left corner of the associated window.
  7326. **/
  7327. int window_y() const {
  7328. return _window_y;
  7329. }
  7330. //! Return X-coordinate of the mouse pointer.
  7331. /**
  7332. \note
  7333. - If the mouse pointer is outside window area, \c -1 is returned.
  7334. - Otherwise, the returned value is in the range [0,width()-1].
  7335. **/
  7336. int mouse_x() const {
  7337. return _mouse_x;
  7338. }
  7339. //! Return Y-coordinate of the mouse pointer.
  7340. /**
  7341. \note
  7342. - If the mouse pointer is outside window area, \c -1 is returned.
  7343. - Otherwise, the returned value is in the range [0,height()-1].
  7344. **/
  7345. int mouse_y() const {
  7346. return _mouse_y;
  7347. }
  7348. //! Return current state of the mouse buttons.
  7349. /**
  7350. \note Three mouse buttons can be managed. If one button is pressed, its corresponding bit in the returned
  7351. value is set:
  7352. - bit \c 0 (value \c 0x1): State of the left mouse button.
  7353. - bit \c 1 (value \c 0x2): State of the right mouse button.
  7354. - bit \c 2 (value \c 0x4): State of the middle mouse button.
  7355. Several bits can be activated if more than one button are pressed at the same time.
  7356. \par Example
  7357. \code
  7358. CImgDisplay disp(400,400);
  7359. while (!disp.is_closed()) {
  7360. if (disp.button()&1) { // Left button clicked.
  7361. ...
  7362. }
  7363. if (disp.button()&2) { // Right button clicked.
  7364. ...
  7365. }
  7366. if (disp.button()&4) { // Middle button clicked.
  7367. ...
  7368. }
  7369. disp.wait();
  7370. }
  7371. \endcode
  7372. **/
  7373. unsigned int button() const {
  7374. return _button;
  7375. }
  7376. //! Return current state of the mouse wheel.
  7377. /**
  7378. \note
  7379. - The returned value can be positive or negative depending on whether the mouse wheel has been scrolled
  7380. forward or backward.
  7381. - Scrolling the wheel forward add \c 1 to the wheel value.
  7382. - Scrolling the wheel backward substract \c 1 to the wheel value.
  7383. - The returned value cumulates the number of forward of backward scrolls since the creation of the display,
  7384. or since the last reset of the wheel value (using set_wheel()). It is strongly recommended to quickly reset
  7385. the wheel counter when an action has been performed regarding the current wheel value.
  7386. Otherwise, the returned wheel value may be for instance \c 0 despite the fact that many scrolls have been done
  7387. (as many in forward as in backward directions).
  7388. \par Example
  7389. \code
  7390. CImgDisplay disp(400,400);
  7391. while (!disp.is_closed()) {
  7392. if (disp.wheel()) {
  7393. int counter = disp.wheel(); // Read the state of the mouse wheel.
  7394. ... // Do what you want with 'counter'.
  7395. disp.set_wheel(); // Reset the wheel value to 0.
  7396. }
  7397. disp.wait();
  7398. }
  7399. \endcode
  7400. **/
  7401. int wheel() const {
  7402. return _wheel;
  7403. }
  7404. //! Return one entry from the pressed keys history.
  7405. /**
  7406. \param pos Indice to read from the pressed keys history (indice \c 0 corresponds to latest entry).
  7407. \return Keycode of a pressed key or \c 0 for a released key.
  7408. \note
  7409. - Each CImgDisplay stores a history of the pressed keys in a buffer of size \c 128. When a new key is pressed,
  7410. its keycode is stored in the pressed keys history. When a key is released, \c 0 is put instead.
  7411. This means that up to the 64 last pressed keys may be read from the pressed keys history.
  7412. When a new value is stored, the pressed keys history is shifted so that the latest entry is always
  7413. stored at position \c 0.
  7414. - Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
  7415. your code stay portable (see cimg::keyESC).
  7416. **/
  7417. unsigned int key(const unsigned int pos=0) const {
  7418. return pos<128?_keys[pos]:0;
  7419. }
  7420. //! Return one entry from the released keys history.
  7421. /**
  7422. \param pos Indice to read from the released keys history (indice \c 0 corresponds to latest entry).
  7423. \return Keycode of a released key or \c 0 for a pressed key.
  7424. \note
  7425. - Each CImgDisplay stores a history of the released keys in a buffer of size \c 128. When a new key is released,
  7426. its keycode is stored in the pressed keys history. When a key is pressed, \c 0 is put instead.
  7427. This means that up to the 64 last released keys may be read from the released keys history.
  7428. When a new value is stored, the released keys history is shifted so that the latest entry is always
  7429. stored at position \c 0.
  7430. - Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
  7431. your code stay portable (see cimg::keyESC).
  7432. **/
  7433. unsigned int released_key(const unsigned int pos=0) const {
  7434. return pos<128?_released_keys[pos]:0;
  7435. }
  7436. //! Return keycode corresponding to the specified string.
  7437. /**
  7438. \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
  7439. your code stay portable (see cimg::keyESC).
  7440. \par Example
  7441. \code
  7442. const unsigned int keyTAB = CImgDisplay::keycode("TAB"); // Return cimg::keyTAB.
  7443. \endcode
  7444. **/
  7445. static unsigned int keycode(const char *const keycode) {
  7446. #define _cimg_keycode(k) if (!cimg::strcasecmp(keycode,#k)) return cimg::key##k;
  7447. _cimg_keycode(ESC); _cimg_keycode(F1); _cimg_keycode(F2); _cimg_keycode(F3);
  7448. _cimg_keycode(F4); _cimg_keycode(F5); _cimg_keycode(F6); _cimg_keycode(F7);
  7449. _cimg_keycode(F8); _cimg_keycode(F9); _cimg_keycode(F10); _cimg_keycode(F11);
  7450. _cimg_keycode(F12); _cimg_keycode(PAUSE); _cimg_keycode(1); _cimg_keycode(2);
  7451. _cimg_keycode(3); _cimg_keycode(4); _cimg_keycode(5); _cimg_keycode(6);
  7452. _cimg_keycode(7); _cimg_keycode(8); _cimg_keycode(9); _cimg_keycode(0);
  7453. _cimg_keycode(BACKSPACE); _cimg_keycode(INSERT); _cimg_keycode(HOME);
  7454. _cimg_keycode(PAGEUP); _cimg_keycode(TAB); _cimg_keycode(Q); _cimg_keycode(W);
  7455. _cimg_keycode(E); _cimg_keycode(R); _cimg_keycode(T); _cimg_keycode(Y);
  7456. _cimg_keycode(U); _cimg_keycode(I); _cimg_keycode(O); _cimg_keycode(P);
  7457. _cimg_keycode(DELETE); _cimg_keycode(END); _cimg_keycode(PAGEDOWN);
  7458. _cimg_keycode(CAPSLOCK); _cimg_keycode(A); _cimg_keycode(S); _cimg_keycode(D);
  7459. _cimg_keycode(F); _cimg_keycode(G); _cimg_keycode(H); _cimg_keycode(J);
  7460. _cimg_keycode(K); _cimg_keycode(L); _cimg_keycode(ENTER);
  7461. _cimg_keycode(SHIFTLEFT); _cimg_keycode(Z); _cimg_keycode(X); _cimg_keycode(C);
  7462. _cimg_keycode(V); _cimg_keycode(B); _cimg_keycode(N); _cimg_keycode(M);
  7463. _cimg_keycode(SHIFTRIGHT); _cimg_keycode(ARROWUP); _cimg_keycode(CTRLLEFT);
  7464. _cimg_keycode(APPLEFT); _cimg_keycode(ALT); _cimg_keycode(SPACE); _cimg_keycode(ALTGR);
  7465. _cimg_keycode(APPRIGHT); _cimg_keycode(MENU); _cimg_keycode(CTRLRIGHT);
  7466. _cimg_keycode(ARROWLEFT); _cimg_keycode(ARROWDOWN); _cimg_keycode(ARROWRIGHT);
  7467. _cimg_keycode(PAD0); _cimg_keycode(PAD1); _cimg_keycode(PAD2);
  7468. _cimg_keycode(PAD3); _cimg_keycode(PAD4); _cimg_keycode(PAD5);
  7469. _cimg_keycode(PAD6); _cimg_keycode(PAD7); _cimg_keycode(PAD8);
  7470. _cimg_keycode(PAD9); _cimg_keycode(PADADD); _cimg_keycode(PADSUB);
  7471. _cimg_keycode(PADMUL); _cimg_keycode(PADDIV);
  7472. return 0;
  7473. }
  7474. //! Return the current refresh rate, in frames per second.
  7475. /**
  7476. \note Returns a significant value when the current instance is used to display successive frames.
  7477. It measures the delay between successive calls to frames_per_second().
  7478. **/
  7479. float frames_per_second() {
  7480. if (!_fps_timer) _fps_timer = cimg::time();
  7481. const float delta = (cimg::time() - _fps_timer)/1000.0f;
  7482. ++_fps_frames;
  7483. if (delta>=1) {
  7484. _fps_fps = _fps_frames/delta;
  7485. _fps_frames = 0;
  7486. _fps_timer = cimg::time();
  7487. }
  7488. return _fps_fps;
  7489. }
  7490. //@}
  7491. //---------------------------------------
  7492. //
  7493. //! \name Window Manipulation
  7494. //@{
  7495. //---------------------------------------
  7496. #if cimg_display==0
  7497. //! Display image on associated window.
  7498. /**
  7499. \param img Input image to display.
  7500. \note This method returns immediately.
  7501. **/
  7502. template<typename T>
  7503. CImgDisplay& display(const CImg<T>& img) {
  7504. return assign(img);
  7505. }
  7506. #endif
  7507. //! Display list of images on associated window.
  7508. /**
  7509. \param list List of images to display.
  7510. \param axis Axis used to append the images along, for the visualization (can be \c x, \c y, \c z or \c c).
  7511. \param align Relative position of aligned images when displaying lists with images of different sizes
  7512. (\c 0 for upper-left, \c 0.5 for centering and \c 1 for lower-right).
  7513. \note This method returns immediately.
  7514. **/
  7515. template<typename T>
  7516. CImgDisplay& display(const CImgList<T>& list, const char axis='x', const float align=0) {
  7517. if (list._width==1) {
  7518. const CImg<T>& img = list[0];
  7519. if (img._depth==1 && (img._spectrum==1 || img._spectrum>=3) && _normalization!=1) return display(img);
  7520. }
  7521. CImgList<typename CImg<T>::ucharT> visu(list._width);
  7522. unsigned int dims = 0;
  7523. cimglist_for(list,l) {
  7524. const CImg<T>& img = list._data[l];
  7525. img.__get_select(*this,_normalization,(img._width - 1)/2,(img._height - 1)/2,
  7526. (img._depth - 1)/2).move_to(visu[l]);
  7527. dims = std::max(dims,visu[l]._spectrum);
  7528. }
  7529. cimglist_for(list,l) if (visu[l]._spectrum<dims) visu[l].resize(-100,-100,-100,dims,1);
  7530. visu.get_append(axis,align).display(*this);
  7531. return *this;
  7532. }
  7533. #if cimg_display==0
  7534. //! Show (closed) associated window on the screen.
  7535. /**
  7536. \note
  7537. - Force the associated window of a display to be visible on the screen, even if it has been closed before.
  7538. - Using show() on a visible display does nothing.
  7539. **/
  7540. CImgDisplay& show() {
  7541. return assign();
  7542. }
  7543. //! Close (visible) associated window and make it disappear from the screen.
  7544. /**
  7545. \note
  7546. - A closed display only means the associated window is not visible anymore. This does not mean the display has
  7547. been destroyed.
  7548. Use show() to make the associated window reappear.
  7549. - Using close() on a closed display does nothing.
  7550. **/
  7551. CImgDisplay& close() {
  7552. return assign();
  7553. }
  7554. //! Move associated window to a new location.
  7555. /**
  7556. \param pos_x X-coordinate of the new window location.
  7557. \param pos_y Y-coordinate of the new window location.
  7558. \note Depending on the window manager behavior, this method may not succeed (no exceptions are thrown
  7559. nevertheless).
  7560. **/
  7561. CImgDisplay& move(const int pos_x, const int pos_y) {
  7562. return assign(pos_x,pos_y);
  7563. }
  7564. #endif
  7565. //! Resize display to the size of the associated window.
  7566. /**
  7567. \param force_redraw Tells if the previous window content must be updated and refreshed as well.
  7568. \note
  7569. - Calling this method ensures that width() and window_width() become equal, as well as height() and
  7570. window_height().
  7571. - The associated window is also resized to specified dimensions.
  7572. **/
  7573. CImgDisplay& resize(const bool force_redraw=true) {
  7574. resize(window_width(),window_height(),force_redraw);
  7575. return *this;
  7576. }
  7577. #if cimg_display==0
  7578. //! Resize display to the specified size.
  7579. /**
  7580. \param width Requested display width.
  7581. \param height Requested display height.
  7582. \param force_redraw Tells if the previous window content must be updated and refreshed as well.
  7583. \note The associated window is also resized to specified dimensions.
  7584. **/
  7585. CImgDisplay& resize(const int width, const int height, const bool force_redraw=true) {
  7586. return assign(width,height,0,3,force_redraw);
  7587. }
  7588. #endif
  7589. //! Resize display to the size of an input image.
  7590. /**
  7591. \param img Input image to take size from.
  7592. \param force_redraw Tells if the previous window content must be resized and updated as well.
  7593. \note
  7594. - Calling this method ensures that width() and <tt>img.width()</tt> become equal, as well as height() and
  7595. <tt>img.height()</tt>.
  7596. - The associated window is also resized to specified dimensions.
  7597. **/
  7598. template<typename T>
  7599. CImgDisplay& resize(const CImg<T>& img, const bool force_redraw=true) {
  7600. return resize(img._width,img._height,force_redraw);
  7601. }
  7602. //! Resize display to the size of another CImgDisplay instance.
  7603. /**
  7604. \param disp Input display to take size from.
  7605. \param force_redraw Tells if the previous window content must be resized and updated as well.
  7606. \note
  7607. - Calling this method ensures that width() and <tt>disp.width()</tt> become equal, as well as height() and
  7608. <tt>disp.height()</tt>.
  7609. - The associated window is also resized to specified dimensions.
  7610. **/
  7611. CImgDisplay& resize(const CImgDisplay& disp, const bool force_redraw=true) {
  7612. return resize(disp.width(),disp.height(),force_redraw);
  7613. }
  7614. // [internal] Render pixel buffer with size (wd,hd) from source buffer of size (ws,hs).
  7615. template<typename t, typename T>
  7616. static void _render_resize(const T *ptrs, const unsigned int ws, const unsigned int hs,
  7617. t *ptrd, const unsigned int wd, const unsigned int hd) {
  7618. unsigned int *const offx = new unsigned int[wd], *const offy = new unsigned int[hd + 1], *poffx, *poffy;
  7619. float s, curr, old;
  7620. s = (float)ws/wd;
  7621. poffx = offx; curr = 0; for (unsigned int x = 0; x<wd; ++x) {
  7622. old = curr; curr+=s; *(poffx++) = (unsigned int)curr - (unsigned int)old;
  7623. }
  7624. s = (float)hs/hd;
  7625. poffy = offy; curr = 0; for (unsigned int y = 0; y<hd; ++y) {
  7626. old = curr; curr+=s; *(poffy++) = ws*((unsigned int)curr - (unsigned int)old);
  7627. }
  7628. *poffy = 0;
  7629. poffy = offy;
  7630. for (unsigned int y = 0; y<hd; ) {
  7631. const T *ptr = ptrs;
  7632. poffx = offx;
  7633. for (unsigned int x = 0; x<wd; ++x) { *(ptrd++) = *ptr; ptr+=*(poffx++); }
  7634. ++y;
  7635. unsigned int dy = *(poffy++);
  7636. for ( ; !dy && y<hd; std::memcpy(ptrd,ptrd - wd,sizeof(t)*wd), ++y, ptrd+=wd, dy = *(poffy++)) {}
  7637. ptrs+=dy;
  7638. }
  7639. delete[] offx; delete[] offy;
  7640. }
  7641. //! Set normalization type.
  7642. /**
  7643. \param normalization New normalization mode.
  7644. **/
  7645. CImgDisplay& set_normalization(const unsigned int normalization) {
  7646. _normalization = normalization;
  7647. _min = _max = 0;
  7648. return *this;
  7649. }
  7650. #if cimg_display==0
  7651. //! Set title of the associated window.
  7652. /**
  7653. \param format C-string containing the format of the title, as with <tt>std::printf()</tt>.
  7654. \warning As the first argument is a format string, it is highly recommended to write
  7655. \code
  7656. disp.set_title("%s",window_title);
  7657. \endcode
  7658. instead of
  7659. \code
  7660. disp.set_title(window_title);
  7661. \endcode
  7662. if \c window_title can be arbitrary, to prevent nasty memory access.
  7663. **/
  7664. CImgDisplay& set_title(const char *const format, ...) {
  7665. return assign(0,0,format);
  7666. }
  7667. #endif
  7668. //! Enable or disable fullscreen mode.
  7669. /**
  7670. \param is_fullscreen Tells is the fullscreen mode must be activated or not.
  7671. \param force_redraw Tells if the previous window content must be displayed as well.
  7672. \note
  7673. - When the fullscreen mode is enabled, the associated window fills the entire screen but the size of the
  7674. current display is not modified.
  7675. - The screen resolution may be switched to fit the associated window size and ensure it appears the largest
  7676. as possible.
  7677. For X-Window (X11) users, the configuration flag \c cimg_use_xrandr has to be set to allow the screen
  7678. resolution change (requires the X11 extensions to be enabled).
  7679. **/
  7680. CImgDisplay& set_fullscreen(const bool is_fullscreen, const bool force_redraw=true) {
  7681. if (is_empty() || _is_fullscreen==is_fullscreen) return *this;
  7682. return toggle_fullscreen(force_redraw);
  7683. }
  7684. #if cimg_display==0
  7685. //! Toggle fullscreen mode.
  7686. /**
  7687. \param force_redraw Tells if the previous window content must be displayed as well.
  7688. \note Enable fullscreen mode if it was not enabled, and disable it otherwise.
  7689. **/
  7690. CImgDisplay& toggle_fullscreen(const bool force_redraw=true) {
  7691. return assign(_width,_height,0,3,force_redraw);
  7692. }
  7693. //! Show mouse pointer.
  7694. /**
  7695. \note Depending on the window manager behavior, this method may not succeed
  7696. (no exceptions are thrown nevertheless).
  7697. **/
  7698. CImgDisplay& show_mouse() {
  7699. return assign();
  7700. }
  7701. //! Hide mouse pointer.
  7702. /**
  7703. \note Depending on the window manager behavior, this method may not succeed
  7704. (no exceptions are thrown nevertheless).
  7705. **/
  7706. CImgDisplay& hide_mouse() {
  7707. return assign();
  7708. }
  7709. //! Move mouse pointer to a specified location.
  7710. /**
  7711. \note Depending on the window manager behavior, this method may not succeed
  7712. (no exceptions are thrown nevertheless).
  7713. **/
  7714. CImgDisplay& set_mouse(const int pos_x, const int pos_y) {
  7715. return assign(pos_x,pos_y);
  7716. }
  7717. #endif
  7718. //! Simulate a mouse button release event.
  7719. /**
  7720. \note All mouse buttons are considered released at the same time.
  7721. **/
  7722. CImgDisplay& set_button() {
  7723. _button = 0;
  7724. _is_event = true;
  7725. #if cimg_display==1
  7726. pthread_cond_broadcast(&cimg::X11_attr().wait_event);
  7727. #elif cimg_display==2
  7728. SetEvent(cimg::Win32_attr().wait_event);
  7729. #endif
  7730. return *this;
  7731. }
  7732. //! Simulate a mouse button press or release event.
  7733. /**
  7734. \param button Buttons event code, where each button is associated to a single bit.
  7735. \param is_pressed Tells if the mouse button is considered as pressed or released.
  7736. **/
  7737. CImgDisplay& set_button(const unsigned int button, const bool is_pressed=true) {
  7738. const unsigned int buttoncode = button==1U?1U:button==2U?2U:button==3U?4U:0U;
  7739. if (is_pressed) _button |= buttoncode; else _button &= ~buttoncode;
  7740. _is_event = buttoncode?true:false;
  7741. if (buttoncode) {
  7742. #if cimg_display==1
  7743. pthread_cond_broadcast(&cimg::X11_attr().wait_event);
  7744. #elif cimg_display==2
  7745. SetEvent(cimg::Win32_attr().wait_event);
  7746. #endif
  7747. }
  7748. return *this;
  7749. }
  7750. //! Flush all mouse wheel events.
  7751. /**
  7752. \note Make wheel() to return \c 0, if called afterwards.
  7753. **/
  7754. CImgDisplay& set_wheel() {
  7755. _wheel = 0;
  7756. _is_event = true;
  7757. #if cimg_display==1
  7758. pthread_cond_broadcast(&cimg::X11_attr().wait_event);
  7759. #elif cimg_display==2
  7760. SetEvent(cimg::Win32_attr().wait_event);
  7761. #endif
  7762. return *this;
  7763. }
  7764. //! Simulate a wheel event.
  7765. /**
  7766. \param amplitude Amplitude of the wheel scrolling to simulate.
  7767. \note Make wheel() to return \c amplitude, if called afterwards.
  7768. **/
  7769. CImgDisplay& set_wheel(const int amplitude) {
  7770. _wheel+=amplitude;
  7771. _is_event = amplitude?true:false;
  7772. if (amplitude) {
  7773. #if cimg_display==1
  7774. pthread_cond_broadcast(&cimg::X11_attr().wait_event);
  7775. #elif cimg_display==2
  7776. SetEvent(cimg::Win32_attr().wait_event);
  7777. #endif
  7778. }
  7779. return *this;
  7780. }
  7781. //! Flush all key events.
  7782. /**
  7783. \note Make key() to return \c 0, if called afterwards.
  7784. **/
  7785. CImgDisplay& set_key() {
  7786. std::memset((void*)_keys,0,128*sizeof(unsigned int));
  7787. std::memset((void*)_released_keys,0,128*sizeof(unsigned int));
  7788. _is_keyESC = _is_keyF1 = _is_keyF2 = _is_keyF3 = _is_keyF4 = _is_keyF5 = _is_keyF6 = _is_keyF7 = _is_keyF8 =
  7789. _is_keyF9 = _is_keyF10 = _is_keyF11 = _is_keyF12 = _is_keyPAUSE = _is_key1 = _is_key2 = _is_key3 = _is_key4 =
  7790. _is_key5 = _is_key6 = _is_key7 = _is_key8 = _is_key9 = _is_key0 = _is_keyBACKSPACE = _is_keyINSERT =
  7791. _is_keyHOME = _is_keyPAGEUP = _is_keyTAB = _is_keyQ = _is_keyW = _is_keyE = _is_keyR = _is_keyT = _is_keyY =
  7792. _is_keyU = _is_keyI = _is_keyO = _is_keyP = _is_keyDELETE = _is_keyEND = _is_keyPAGEDOWN = _is_keyCAPSLOCK =
  7793. _is_keyA = _is_keyS = _is_keyD = _is_keyF = _is_keyG = _is_keyH = _is_keyJ = _is_keyK = _is_keyL =
  7794. _is_keyENTER = _is_keySHIFTLEFT = _is_keyZ = _is_keyX = _is_keyC = _is_keyV = _is_keyB = _is_keyN =
  7795. _is_keyM = _is_keySHIFTRIGHT = _is_keyARROWUP = _is_keyCTRLLEFT = _is_keyAPPLEFT = _is_keyALT = _is_keySPACE =
  7796. _is_keyALTGR = _is_keyAPPRIGHT = _is_keyMENU = _is_keyCTRLRIGHT = _is_keyARROWLEFT = _is_keyARROWDOWN =
  7797. _is_keyARROWRIGHT = _is_keyPAD0 = _is_keyPAD1 = _is_keyPAD2 = _is_keyPAD3 = _is_keyPAD4 = _is_keyPAD5 =
  7798. _is_keyPAD6 = _is_keyPAD7 = _is_keyPAD8 = _is_keyPAD9 = _is_keyPADADD = _is_keyPADSUB = _is_keyPADMUL =
  7799. _is_keyPADDIV = false;
  7800. _is_event = true;
  7801. #if cimg_display==1
  7802. pthread_cond_broadcast(&cimg::X11_attr().wait_event);
  7803. #elif cimg_display==2
  7804. SetEvent(cimg::Win32_attr().wait_event);
  7805. #endif
  7806. return *this;
  7807. }
  7808. //! Simulate a keyboard press/release event.
  7809. /**
  7810. \param keycode Keycode of the associated key.
  7811. \param is_pressed Tells if the key is considered as pressed or released.
  7812. \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
  7813. your code stay portable (see cimg::keyESC).
  7814. **/
  7815. CImgDisplay& set_key(const unsigned int keycode, const bool is_pressed=true) {
  7816. #define _cimg_set_key(k) if (keycode==cimg::key##k) _is_key##k = is_pressed;
  7817. _cimg_set_key(ESC); _cimg_set_key(F1); _cimg_set_key(F2); _cimg_set_key(F3);
  7818. _cimg_set_key(F4); _cimg_set_key(F5); _cimg_set_key(F6); _cimg_set_key(F7);
  7819. _cimg_set_key(F8); _cimg_set_key(F9); _cimg_set_key(F10); _cimg_set_key(F11);
  7820. _cimg_set_key(F12); _cimg_set_key(PAUSE); _cimg_set_key(1); _cimg_set_key(2);
  7821. _cimg_set_key(3); _cimg_set_key(4); _cimg_set_key(5); _cimg_set_key(6);
  7822. _cimg_set_key(7); _cimg_set_key(8); _cimg_set_key(9); _cimg_set_key(0);
  7823. _cimg_set_key(BACKSPACE); _cimg_set_key(INSERT); _cimg_set_key(HOME);
  7824. _cimg_set_key(PAGEUP); _cimg_set_key(TAB); _cimg_set_key(Q); _cimg_set_key(W);
  7825. _cimg_set_key(E); _cimg_set_key(R); _cimg_set_key(T); _cimg_set_key(Y);
  7826. _cimg_set_key(U); _cimg_set_key(I); _cimg_set_key(O); _cimg_set_key(P);
  7827. _cimg_set_key(DELETE); _cimg_set_key(END); _cimg_set_key(PAGEDOWN);
  7828. _cimg_set_key(CAPSLOCK); _cimg_set_key(A); _cimg_set_key(S); _cimg_set_key(D);
  7829. _cimg_set_key(F); _cimg_set_key(G); _cimg_set_key(H); _cimg_set_key(J);
  7830. _cimg_set_key(K); _cimg_set_key(L); _cimg_set_key(ENTER);
  7831. _cimg_set_key(SHIFTLEFT); _cimg_set_key(Z); _cimg_set_key(X); _cimg_set_key(C);
  7832. _cimg_set_key(V); _cimg_set_key(B); _cimg_set_key(N); _cimg_set_key(M);
  7833. _cimg_set_key(SHIFTRIGHT); _cimg_set_key(ARROWUP); _cimg_set_key(CTRLLEFT);
  7834. _cimg_set_key(APPLEFT); _cimg_set_key(ALT); _cimg_set_key(SPACE); _cimg_set_key(ALTGR);
  7835. _cimg_set_key(APPRIGHT); _cimg_set_key(MENU); _cimg_set_key(CTRLRIGHT);
  7836. _cimg_set_key(ARROWLEFT); _cimg_set_key(ARROWDOWN); _cimg_set_key(ARROWRIGHT);
  7837. _cimg_set_key(PAD0); _cimg_set_key(PAD1); _cimg_set_key(PAD2);
  7838. _cimg_set_key(PAD3); _cimg_set_key(PAD4); _cimg_set_key(PAD5);
  7839. _cimg_set_key(PAD6); _cimg_set_key(PAD7); _cimg_set_key(PAD8);
  7840. _cimg_set_key(PAD9); _cimg_set_key(PADADD); _cimg_set_key(PADSUB);
  7841. _cimg_set_key(PADMUL); _cimg_set_key(PADDIV);
  7842. if (is_pressed) {
  7843. if (*_keys)
  7844. std::memmove((void*)(_keys + 1),(void*)_keys,127*sizeof(unsigned int));
  7845. *_keys = keycode;
  7846. if (*_released_keys) {
  7847. std::memmove((void*)(_released_keys + 1),(void*)_released_keys,127*sizeof(unsigned int));
  7848. *_released_keys = 0;
  7849. }
  7850. } else {
  7851. if (*_keys) {
  7852. std::memmove((void*)(_keys + 1),(void*)_keys,127*sizeof(unsigned int));
  7853. *_keys = 0;
  7854. }
  7855. if (*_released_keys)
  7856. std::memmove((void*)(_released_keys + 1),(void*)_released_keys,127*sizeof(unsigned int));
  7857. *_released_keys = keycode;
  7858. }
  7859. _is_event = keycode?true:false;
  7860. if (keycode) {
  7861. #if cimg_display==1
  7862. pthread_cond_broadcast(&cimg::X11_attr().wait_event);
  7863. #elif cimg_display==2
  7864. SetEvent(cimg::Win32_attr().wait_event);
  7865. #endif
  7866. }
  7867. return *this;
  7868. }
  7869. //! Flush all display events.
  7870. /**
  7871. \note Remove all passed events from the current display.
  7872. **/
  7873. CImgDisplay& flush() {
  7874. set_key().set_button().set_wheel();
  7875. _is_resized = _is_moved = _is_event = false;
  7876. _fps_timer = _fps_frames = _timer = 0;
  7877. _fps_fps = 0;
  7878. return *this;
  7879. }
  7880. //! Wait for any user event occuring on the current display.
  7881. CImgDisplay& wait() {
  7882. wait(*this);
  7883. return *this;
  7884. }
  7885. //! Wait for a given number of milliseconds since the last call to wait().
  7886. /**
  7887. \param milliseconds Number of milliseconds to wait for.
  7888. \note Similar to cimg::wait().
  7889. **/
  7890. CImgDisplay& wait(const unsigned int milliseconds) {
  7891. cimg::_wait(milliseconds,_timer);
  7892. return *this;
  7893. }
  7894. //! Wait for any event occuring on the display \c disp1.
  7895. static void wait(CImgDisplay& disp1) {
  7896. disp1._is_event = false;
  7897. while (!disp1._is_closed && !disp1._is_event) wait_all();
  7898. }
  7899. //! Wait for any event occuring either on the display \c disp1 or \c disp2.
  7900. static void wait(CImgDisplay& disp1, CImgDisplay& disp2) {
  7901. disp1._is_event = disp2._is_event = false;
  7902. while ((!disp1._is_closed || !disp2._is_closed) &&
  7903. !disp1._is_event && !disp2._is_event) wait_all();
  7904. }
  7905. //! Wait for any event occuring either on the display \c disp1, \c disp2 or \c disp3.
  7906. static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3) {
  7907. disp1._is_event = disp2._is_event = disp3._is_event = false;
  7908. while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed) &&
  7909. !disp1._is_event && !disp2._is_event && !disp3._is_event) wait_all();
  7910. }
  7911. //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3 or \c disp4.
  7912. static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4) {
  7913. disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = false;
  7914. while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed) &&
  7915. !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event) wait_all();
  7916. }
  7917. //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4 or \c disp5.
  7918. static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4,
  7919. CImgDisplay& disp5) {
  7920. disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = false;
  7921. while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed) &&
  7922. !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event)
  7923. wait_all();
  7924. }
  7925. //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp6.
  7926. static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
  7927. CImgDisplay& disp6) {
  7928. disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
  7929. disp6._is_event = false;
  7930. while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
  7931. !disp6._is_closed) &&
  7932. !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
  7933. !disp6._is_event) wait_all();
  7934. }
  7935. //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp7.
  7936. static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
  7937. CImgDisplay& disp6, CImgDisplay& disp7) {
  7938. disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
  7939. disp6._is_event = disp7._is_event = false;
  7940. while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
  7941. !disp6._is_closed || !disp7._is_closed) &&
  7942. !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
  7943. !disp6._is_event && !disp7._is_event) wait_all();
  7944. }
  7945. //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp8.
  7946. static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
  7947. CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8) {
  7948. disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
  7949. disp6._is_event = disp7._is_event = disp8._is_event = false;
  7950. while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
  7951. !disp6._is_closed || !disp7._is_closed || !disp8._is_closed) &&
  7952. !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
  7953. !disp6._is_event && !disp7._is_event && !disp8._is_event) wait_all();
  7954. }
  7955. //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp9.
  7956. static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
  7957. CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9) {
  7958. disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
  7959. disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = false;
  7960. while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
  7961. !disp6._is_closed || !disp7._is_closed || !disp8._is_closed || !disp9._is_closed) &&
  7962. !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
  7963. !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event) wait_all();
  7964. }
  7965. //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp10.
  7966. static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
  7967. CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9,
  7968. CImgDisplay& disp10) {
  7969. disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
  7970. disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = disp10._is_event = false;
  7971. while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
  7972. !disp6._is_closed || !disp7._is_closed || !disp8._is_closed || !disp9._is_closed || !disp10._is_closed) &&
  7973. !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
  7974. !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event && !disp10._is_event)
  7975. wait_all();
  7976. }
  7977. #if cimg_display==0
  7978. //! Wait for any window event occuring in any opened CImgDisplay.
  7979. static void wait_all() {
  7980. return _no_display_exception();
  7981. }
  7982. //! Render image into internal display buffer.
  7983. /**
  7984. \param img Input image data to render.
  7985. \note
  7986. - Convert image data representation into the internal display buffer (architecture-dependent structure).
  7987. - The content of the associated window is not modified, until paint() is called.
  7988. - Should not be used for common CImgDisplay uses, since display() is more useful.
  7989. **/
  7990. template<typename T>
  7991. CImgDisplay& render(const CImg<T>& img) {
  7992. return assign(img);
  7993. }
  7994. //! Paint internal display buffer on associated window.
  7995. /**
  7996. \note
  7997. - Update the content of the associated window with the internal display buffer, e.g. after a render() call.
  7998. - Should not be used for common CImgDisplay uses, since display() is more useful.
  7999. **/
  8000. CImgDisplay& paint() {
  8001. return assign();
  8002. }
  8003. //! Take a snapshot of the current screen content.
  8004. /**
  8005. \param x0 X-coordinate of the upper left corner.
  8006. \param y0 Y-coordinate of the upper left corner.
  8007. \param x1 X-coordinate of the lower right corner.
  8008. \param y1 Y-coordinate of the lower right corner.
  8009. \param[out] img Output screenshot. Can be empty on input
  8010. **/
  8011. template<typename T>
  8012. static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg<T>& img) {
  8013. cimg::unused(x0,y0,x1,y1,&img);
  8014. _no_display_exception();
  8015. }
  8016. //! Take a snapshot of the associated window content.
  8017. /**
  8018. \param[out] img Output snapshot. Can be empty on input.
  8019. **/
  8020. template<typename T>
  8021. const CImgDisplay& snapshot(CImg<T>& img) const {
  8022. cimg::unused(img);
  8023. _no_display_exception();
  8024. return *this;
  8025. }
  8026. #endif
  8027. // X11-based implementation
  8028. //--------------------------
  8029. #if cimg_display==1
  8030. Atom _wm_window_atom, _wm_protocol_atom;
  8031. Window _window, _background_window;
  8032. Colormap _colormap;
  8033. XImage *_image;
  8034. void *_data;
  8035. #ifdef cimg_use_xshm
  8036. XShmSegmentInfo *_shminfo;
  8037. #endif
  8038. static int screen_width() {
  8039. Display *const dpy = cimg::X11_attr().display;
  8040. int res = 0;
  8041. if (!dpy) {
  8042. Display *const _dpy = XOpenDisplay(0);
  8043. if (!_dpy)
  8044. throw CImgDisplayException("CImgDisplay::screen_width(): Failed to open X11 display.");
  8045. res = DisplayWidth(_dpy,DefaultScreen(_dpy));
  8046. XCloseDisplay(_dpy);
  8047. } else {
  8048. #ifdef cimg_use_xrandr
  8049. if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution)
  8050. res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width;
  8051. else res = DisplayWidth(dpy,DefaultScreen(dpy));
  8052. #else
  8053. res = DisplayWidth(dpy,DefaultScreen(dpy));
  8054. #endif
  8055. }
  8056. return res;
  8057. }
  8058. static int screen_height() {
  8059. Display *const dpy = cimg::X11_attr().display;
  8060. int res = 0;
  8061. if (!dpy) {
  8062. Display *const _dpy = XOpenDisplay(0);
  8063. if (!_dpy)
  8064. throw CImgDisplayException("CImgDisplay::screen_height(): Failed to open X11 display.");
  8065. res = DisplayHeight(_dpy,DefaultScreen(_dpy));
  8066. XCloseDisplay(_dpy);
  8067. } else {
  8068. #ifdef cimg_use_xrandr
  8069. if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution)
  8070. res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height;
  8071. else res = DisplayHeight(dpy,DefaultScreen(dpy));
  8072. #else
  8073. res = DisplayHeight(dpy,DefaultScreen(dpy));
  8074. #endif
  8075. }
  8076. return res;
  8077. }
  8078. static void wait_all() {
  8079. if (!cimg::X11_attr().display) return;
  8080. pthread_mutex_lock(&cimg::X11_attr().wait_event_mutex);
  8081. pthread_cond_wait(&cimg::X11_attr().wait_event,&cimg::X11_attr().wait_event_mutex);
  8082. pthread_mutex_unlock(&cimg::X11_attr().wait_event_mutex);
  8083. }
  8084. void _handle_events(const XEvent *const pevent) {
  8085. Display *const dpy = cimg::X11_attr().display;
  8086. XEvent event = *pevent;
  8087. switch (event.type) {
  8088. case ClientMessage : {
  8089. if ((int)event.xclient.message_type==(int)_wm_protocol_atom &&
  8090. (int)event.xclient.data.l[0]==(int)_wm_window_atom) {
  8091. XUnmapWindow(cimg::X11_attr().display,_window);
  8092. _is_closed = _is_event = true;
  8093. pthread_cond_broadcast(&cimg::X11_attr().wait_event);
  8094. }
  8095. } break;
  8096. case ConfigureNotify : {
  8097. while (XCheckWindowEvent(dpy,_window,StructureNotifyMask,&event)) {}
  8098. const unsigned int nw = event.xconfigure.width, nh = event.xconfigure.height;
  8099. const int nx = event.xconfigure.x, ny = event.xconfigure.y;
  8100. if (nw && nh && (nw!=_window_width || nh!=_window_height)) {
  8101. _window_width = nw; _window_height = nh; _mouse_x = _mouse_y = -1;
  8102. XResizeWindow(dpy,_window,_window_width,_window_height);
  8103. _is_resized = _is_event = true;
  8104. pthread_cond_broadcast(&cimg::X11_attr().wait_event);
  8105. }
  8106. if (nx!=_window_x || ny!=_window_y) {
  8107. _window_x = nx; _window_y = ny; _is_moved = _is_event = true;
  8108. pthread_cond_broadcast(&cimg::X11_attr().wait_event);
  8109. }
  8110. } break;
  8111. case Expose : {
  8112. while (XCheckWindowEvent(dpy,_window,ExposureMask,&event)) {}
  8113. _paint(false);
  8114. if (_is_fullscreen) {
  8115. XWindowAttributes attr;
  8116. XGetWindowAttributes(dpy,_window,&attr);
  8117. while (attr.map_state!=IsViewable) XSync(dpy,0);
  8118. XSetInputFocus(dpy,_window,RevertToParent,CurrentTime);
  8119. }
  8120. } break;
  8121. case ButtonPress : {
  8122. do {
  8123. _mouse_x = event.xmotion.x; _mouse_y = event.xmotion.y;
  8124. if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
  8125. switch (event.xbutton.button) {
  8126. case 1 : set_button(1); break;
  8127. case 3 : set_button(2); break;
  8128. case 2 : set_button(3); break;
  8129. }
  8130. } while (XCheckWindowEvent(dpy,_window,ButtonPressMask,&event));
  8131. } break;
  8132. case ButtonRelease : {
  8133. do {
  8134. _mouse_x = event.xmotion.x; _mouse_y = event.xmotion.y;
  8135. if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
  8136. switch (event.xbutton.button) {
  8137. case 1 : set_button(1,false); break;
  8138. case 3 : set_button(2,false); break;
  8139. case 2 : set_button(3,false); break;
  8140. case 4 : set_wheel(1); break;
  8141. case 5 : set_wheel(-1); break;
  8142. }
  8143. } while (XCheckWindowEvent(dpy,_window,ButtonReleaseMask,&event));
  8144. } break;
  8145. case KeyPress : {
  8146. char tmp = 0; KeySym ksym;
  8147. XLookupString(&event.xkey,&tmp,1,&ksym,0);
  8148. set_key((unsigned int)ksym,true);
  8149. } break;
  8150. case KeyRelease : {
  8151. char keys_return[32]; // Check that the key has been physically unpressed.
  8152. XQueryKeymap(dpy,keys_return);
  8153. const unsigned int kc = event.xkey.keycode, kc1 = kc/8, kc2 = kc%8;
  8154. const bool is_key_pressed = kc1>=32?false:(keys_return[kc1]>>kc2)&1;
  8155. if (!is_key_pressed) {
  8156. char tmp = 0; KeySym ksym;
  8157. XLookupString(&event.xkey,&tmp,1,&ksym,0);
  8158. set_key((unsigned int)ksym,false);
  8159. }
  8160. } break;
  8161. case EnterNotify: {
  8162. while (XCheckWindowEvent(dpy,_window,EnterWindowMask,&event)) {}
  8163. _mouse_x = event.xmotion.x;
  8164. _mouse_y = event.xmotion.y;
  8165. if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
  8166. } break;
  8167. case LeaveNotify : {
  8168. while (XCheckWindowEvent(dpy,_window,LeaveWindowMask,&event)) {}
  8169. _mouse_x = _mouse_y = -1; _is_event = true;
  8170. pthread_cond_broadcast(&cimg::X11_attr().wait_event);
  8171. } break;
  8172. case MotionNotify : {
  8173. while (XCheckWindowEvent(dpy,_window,PointerMotionMask,&event)) {}
  8174. _mouse_x = event.xmotion.x;
  8175. _mouse_y = event.xmotion.y;
  8176. if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
  8177. _is_event = true;
  8178. pthread_cond_broadcast(&cimg::X11_attr().wait_event);
  8179. } break;
  8180. }
  8181. }
  8182. static void* _events_thread(void *arg) { // Thread to manage events for all opened display windows.
  8183. Display *const dpy = cimg::X11_attr().display;
  8184. XEvent event;
  8185. pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,0);
  8186. pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,0);
  8187. if (!arg) for ( ; ; ) {
  8188. cimg_lock_display();
  8189. bool event_flag = XCheckTypedEvent(dpy,ClientMessage,&event);
  8190. if (!event_flag) event_flag = XCheckMaskEvent(dpy,
  8191. ExposureMask | StructureNotifyMask | ButtonPressMask |
  8192. KeyPressMask | PointerMotionMask | EnterWindowMask |
  8193. LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask,&event);
  8194. if (event_flag)
  8195. for (unsigned int i = 0; i<cimg::X11_attr().nb_wins; ++i)
  8196. if (!cimg::X11_attr().wins[i]->_is_closed && event.xany.window==cimg::X11_attr().wins[i]->_window)
  8197. cimg::X11_attr().wins[i]->_handle_events(&event);
  8198. cimg_unlock_display();
  8199. pthread_testcancel();
  8200. cimg::sleep(8);
  8201. }
  8202. return 0;
  8203. }
  8204. void _set_colormap(Colormap& _colormap, const unsigned int dim) {
  8205. XColor *const colormap = new XColor[256];
  8206. switch (dim) {
  8207. case 1 : { // colormap for greyscale images
  8208. for (unsigned int index = 0; index<256; ++index) {
  8209. colormap[index].pixel = index;
  8210. colormap[index].red = colormap[index].green = colormap[index].blue = (unsigned short)(index<<8);
  8211. colormap[index].flags = DoRed | DoGreen | DoBlue;
  8212. }
  8213. } break;
  8214. case 2 : { // colormap for RG images
  8215. for (unsigned int index = 0, r = 8; r<256; r+=16)
  8216. for (unsigned int g = 8; g<256; g+=16) {
  8217. colormap[index].pixel = index;
  8218. colormap[index].red = colormap[index].blue = (unsigned short)(r<<8);
  8219. colormap[index].green = (unsigned short)(g<<8);
  8220. colormap[index++].flags = DoRed | DoGreen | DoBlue;
  8221. }
  8222. } break;
  8223. default : { // colormap for RGB images
  8224. for (unsigned int index = 0, r = 16; r<256; r+=32)
  8225. for (unsigned int g = 16; g<256; g+=32)
  8226. for (unsigned int b = 32; b<256; b+=64) {
  8227. colormap[index].pixel = index;
  8228. colormap[index].red = (unsigned short)(r<<8);
  8229. colormap[index].green = (unsigned short)(g<<8);
  8230. colormap[index].blue = (unsigned short)(b<<8);
  8231. colormap[index++].flags = DoRed | DoGreen | DoBlue;
  8232. }
  8233. }
  8234. }
  8235. XStoreColors(cimg::X11_attr().display,_colormap,colormap,256);
  8236. delete[] colormap;
  8237. }
  8238. void _map_window() {
  8239. Display *const dpy = cimg::X11_attr().display;
  8240. bool is_exposed = false, is_mapped = false;
  8241. XWindowAttributes attr;
  8242. XEvent event;
  8243. XMapRaised(dpy,_window);
  8244. do { // Wait for the window to be mapped.
  8245. XWindowEvent(dpy,_window,StructureNotifyMask | ExposureMask,&event);
  8246. switch (event.type) {
  8247. case MapNotify : is_mapped = true; break;
  8248. case Expose : is_exposed = true; break;
  8249. }
  8250. } while (!is_exposed || !is_mapped);
  8251. do { // Wait for the window to be visible.
  8252. XGetWindowAttributes(dpy,_window,&attr);
  8253. if (attr.map_state!=IsViewable) { XSync(dpy,0); cimg::sleep(10); }
  8254. } while (attr.map_state!=IsViewable);
  8255. _window_x = attr.x;
  8256. _window_y = attr.y;
  8257. }
  8258. void _paint(const bool wait_expose=true) {
  8259. if (_is_closed || !_image) return;
  8260. Display *const dpy = cimg::X11_attr().display;
  8261. if (wait_expose) { // Send an expose event sticked to display window to force repaint.
  8262. XEvent event;
  8263. event.xexpose.type = Expose;
  8264. event.xexpose.serial = 0;
  8265. event.xexpose.send_event = 1;
  8266. event.xexpose.display = dpy;
  8267. event.xexpose.window = _window;
  8268. event.xexpose.x = 0;
  8269. event.xexpose.y = 0;
  8270. event.xexpose.width = width();
  8271. event.xexpose.height = height();
  8272. event.xexpose.count = 0;
  8273. XSendEvent(dpy,_window,0,0,&event);
  8274. } else { // Repaint directly (may be called from the expose event).
  8275. GC gc = DefaultGC(dpy,DefaultScreen(dpy));
  8276. #ifdef cimg_use_xshm
  8277. if (_shminfo) XShmPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height,1);
  8278. else XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height);
  8279. #else
  8280. XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height);
  8281. #endif
  8282. }
  8283. }
  8284. template<typename T>
  8285. void _resize(T pixel_type, const unsigned int ndimx, const unsigned int ndimy, const bool force_redraw) {
  8286. Display *const dpy = cimg::X11_attr().display;
  8287. cimg::unused(pixel_type);
  8288. #ifdef cimg_use_xshm
  8289. if (_shminfo) {
  8290. XShmSegmentInfo *const nshminfo = new XShmSegmentInfo;
  8291. XImage *const nimage = XShmCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),
  8292. cimg::X11_attr().nb_bits,ZPixmap,0,nshminfo,ndimx,ndimy);
  8293. if (!nimage) { delete nshminfo; return; }
  8294. else {
  8295. nshminfo->shmid = shmget(IPC_PRIVATE,ndimx*ndimy*sizeof(T),IPC_CREAT | 0777);
  8296. if (nshminfo->shmid==-1) { XDestroyImage(nimage); delete nshminfo; return; }
  8297. else {
  8298. nshminfo->shmaddr = nimage->data = (char*)shmat(nshminfo->shmid,0,0);
  8299. if (nshminfo->shmaddr==(char*)-1) {
  8300. shmctl(nshminfo->shmid,IPC_RMID,0); XDestroyImage(nimage); delete nshminfo; return;
  8301. } else {
  8302. nshminfo->readOnly = 0;
  8303. cimg::X11_attr().is_shm_enabled = true;
  8304. XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm);
  8305. XShmAttach(dpy,nshminfo);
  8306. XFlush(dpy);
  8307. XSetErrorHandler(oldXErrorHandler);
  8308. if (!cimg::X11_attr().is_shm_enabled) {
  8309. shmdt(nshminfo->shmaddr);
  8310. shmctl(nshminfo->shmid,IPC_RMID,0);
  8311. XDestroyImage(nimage);
  8312. delete nshminfo;
  8313. return;
  8314. } else {
  8315. T *const ndata = (T*)nimage->data;
  8316. if (force_redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy);
  8317. else std::memset(ndata,0,sizeof(T)*ndimx*ndimy);
  8318. XShmDetach(dpy,_shminfo);
  8319. XDestroyImage(_image);
  8320. shmdt(_shminfo->shmaddr);
  8321. shmctl(_shminfo->shmid,IPC_RMID,0);
  8322. delete _shminfo;
  8323. _shminfo = nshminfo;
  8324. _image = nimage;
  8325. _data = (void*)ndata;
  8326. }
  8327. }
  8328. }
  8329. }
  8330. } else
  8331. #endif
  8332. {
  8333. T *ndata = (T*)std::malloc(ndimx*ndimy*sizeof(T));
  8334. if (force_redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy);
  8335. else std::memset(ndata,0,sizeof(T)*ndimx*ndimy);
  8336. _data = (void*)ndata;
  8337. XDestroyImage(_image);
  8338. _image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),
  8339. cimg::X11_attr().nb_bits,ZPixmap,0,(char*)_data,ndimx,ndimy,8,0);
  8340. }
  8341. }
  8342. void _init_fullscreen() {
  8343. if (!_is_fullscreen || _is_closed) return;
  8344. Display *const dpy = cimg::X11_attr().display;
  8345. _background_window = 0;
  8346. #ifdef cimg_use_xrandr
  8347. int foo;
  8348. if (XRRQueryExtension(dpy,&foo,&foo)) {
  8349. XRRRotations(dpy,DefaultScreen(dpy),&cimg::X11_attr().curr_rotation);
  8350. if (!cimg::X11_attr().resolutions) {
  8351. cimg::X11_attr().resolutions = XRRSizes(dpy,DefaultScreen(dpy),&foo);
  8352. cimg::X11_attr().nb_resolutions = (unsigned int)foo;
  8353. }
  8354. if (cimg::X11_attr().resolutions) {
  8355. cimg::X11_attr().curr_resolution = 0;
  8356. for (unsigned int i = 0; i<cimg::X11_attr().nb_resolutions; ++i) {
  8357. const unsigned int
  8358. nw = (unsigned int)(cimg::X11_attr().resolutions[i].width),
  8359. nh = (unsigned int)(cimg::X11_attr().resolutions[i].height);
  8360. if (nw>=_width && nh>=_height &&
  8361. nw<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width) &&
  8362. nh<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height))
  8363. cimg::X11_attr().curr_resolution = i;
  8364. }
  8365. if (cimg::X11_attr().curr_resolution>0) {
  8366. XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy));
  8367. XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy),
  8368. cimg::X11_attr().curr_resolution,cimg::X11_attr().curr_rotation,CurrentTime);
  8369. XRRFreeScreenConfigInfo(config);
  8370. XSync(dpy,0);
  8371. }
  8372. }
  8373. }
  8374. if (!cimg::X11_attr().resolutions)
  8375. cimg::warn(_cimgdisplay_instance
  8376. "init_fullscreen(): Xrandr extension not supported by the X server.",
  8377. cimgdisplay_instance);
  8378. #endif
  8379. const unsigned int sx = screen_width(), sy = screen_height();
  8380. if (sx==_width && sy==_height) return;
  8381. XSetWindowAttributes winattr;
  8382. winattr.override_redirect = 1;
  8383. _background_window = XCreateWindow(dpy,DefaultRootWindow(dpy),0,0,sx,sy,0,0,
  8384. InputOutput,CopyFromParent,CWOverrideRedirect,&winattr);
  8385. const cimg_ulong buf_size = (cimg_ulong)sx*sy*(cimg::X11_attr().nb_bits==8?1:
  8386. (cimg::X11_attr().nb_bits==16?2:4));
  8387. void *background_data = std::malloc(buf_size);
  8388. std::memset(background_data,0,buf_size);
  8389. XImage *background_image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits,
  8390. ZPixmap,0,(char*)background_data,sx,sy,8,0);
  8391. XEvent event;
  8392. XSelectInput(dpy,_background_window,StructureNotifyMask);
  8393. XMapRaised(dpy,_background_window);
  8394. do XWindowEvent(dpy,_background_window,StructureNotifyMask,&event);
  8395. while (event.type!=MapNotify);
  8396. GC gc = DefaultGC(dpy,DefaultScreen(dpy));
  8397. #ifdef cimg_use_xshm
  8398. if (_shminfo) XShmPutImage(dpy,_background_window,gc,background_image,0,0,0,0,sx,sy,0);
  8399. else XPutImage(dpy,_background_window,gc,background_image,0,0,0,0,sx,sy);
  8400. #else
  8401. XPutImage(dpy,_background_window,gc,background_image,0,0,0,0,sx,sy);
  8402. #endif
  8403. XWindowAttributes attr;
  8404. XGetWindowAttributes(dpy,_background_window,&attr);
  8405. while (attr.map_state!=IsViewable) XSync(dpy,0);
  8406. XDestroyImage(background_image);
  8407. }
  8408. void _desinit_fullscreen() {
  8409. if (!_is_fullscreen) return;
  8410. Display *const dpy = cimg::X11_attr().display;
  8411. XUngrabKeyboard(dpy,CurrentTime);
  8412. #ifdef cimg_use_xrandr
  8413. if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution) {
  8414. XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy));
  8415. XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy),0,cimg::X11_attr().curr_rotation,CurrentTime);
  8416. XRRFreeScreenConfigInfo(config);
  8417. XSync(dpy,0);
  8418. cimg::X11_attr().curr_resolution = 0;
  8419. }
  8420. #endif
  8421. if (_background_window) XDestroyWindow(dpy,_background_window);
  8422. _background_window = 0;
  8423. _is_fullscreen = false;
  8424. }
  8425. static int _assign_xshm(Display *dpy, XErrorEvent *error) {
  8426. cimg::unused(dpy,error);
  8427. cimg::X11_attr().is_shm_enabled = false;
  8428. return 0;
  8429. }
  8430. void _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0,
  8431. const unsigned int normalization_type=3,
  8432. const bool fullscreen_flag=false, const bool closed_flag=false) {
  8433. cimg::mutex(14);
  8434. // Allocate space for window title
  8435. const char *const nptitle = ptitle?ptitle:"";
  8436. const unsigned int s = (unsigned int)std::strlen(nptitle) + 1;
  8437. char *const tmp_title = s?new char[s]:0;
  8438. if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char));
  8439. // Destroy previous display window if existing
  8440. if (!is_empty()) assign();
  8441. // Open X11 display and retrieve graphical properties.
  8442. Display* &dpy = cimg::X11_attr().display;
  8443. if (!dpy) {
  8444. dpy = XOpenDisplay(0);
  8445. if (!dpy)
  8446. throw CImgDisplayException(_cimgdisplay_instance
  8447. "assign(): Failed to open X11 display.",
  8448. cimgdisplay_instance);
  8449. cimg::X11_attr().nb_bits = DefaultDepth(dpy,DefaultScreen(dpy));
  8450. if (cimg::X11_attr().nb_bits!=8 && cimg::X11_attr().nb_bits!=16 &&
  8451. cimg::X11_attr().nb_bits!=24 && cimg::X11_attr().nb_bits!=32)
  8452. throw CImgDisplayException(_cimgdisplay_instance
  8453. "assign(): Invalid %u bits screen mode detected "
  8454. "(only 8, 16, 24 and 32 bits modes are managed).",
  8455. cimgdisplay_instance,
  8456. cimg::X11_attr().nb_bits);
  8457. XVisualInfo vtemplate;
  8458. vtemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy,DefaultScreen(dpy)));
  8459. int nb_visuals;
  8460. XVisualInfo *vinfo = XGetVisualInfo(dpy,VisualIDMask,&vtemplate,&nb_visuals);
  8461. if (vinfo && vinfo->red_mask<vinfo->blue_mask) cimg::X11_attr().is_blue_first = true;
  8462. cimg::X11_attr().byte_order = ImageByteOrder(dpy);
  8463. XFree(vinfo);
  8464. cimg_lock_display();
  8465. cimg::X11_attr().events_thread = new pthread_t;
  8466. pthread_create(cimg::X11_attr().events_thread,0,_events_thread,0);
  8467. } else cimg_lock_display();
  8468. // Set display variables.
  8469. _width = std::min(dimw,(unsigned int)screen_width());
  8470. _height = std::min(dimh,(unsigned int)screen_height());
  8471. _normalization = normalization_type<4?normalization_type:3;
  8472. _is_fullscreen = fullscreen_flag;
  8473. _window_x = _window_y = 0;
  8474. _is_closed = closed_flag;
  8475. _title = tmp_title;
  8476. flush();
  8477. // Create X11 window (and LUT, if 8bits display)
  8478. if (_is_fullscreen) {
  8479. if (!_is_closed) _init_fullscreen();
  8480. const unsigned int sx = screen_width(), sy = screen_height();
  8481. XSetWindowAttributes winattr;
  8482. winattr.override_redirect = 1;
  8483. _window = XCreateWindow(dpy,DefaultRootWindow(dpy),(sx - _width)/2,(sy - _height)/2,_width,_height,0,0,
  8484. InputOutput,CopyFromParent,CWOverrideRedirect,&winattr);
  8485. } else
  8486. _window = XCreateSimpleWindow(dpy,DefaultRootWindow(dpy),0,0,_width,_height,0,0L,0L);
  8487. XSelectInput(dpy,_window,
  8488. ExposureMask | StructureNotifyMask | ButtonPressMask | KeyPressMask | PointerMotionMask |
  8489. EnterWindowMask | LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask);
  8490. XStoreName(dpy,_window,_title?_title:" ");
  8491. if (cimg::X11_attr().nb_bits==8) {
  8492. _colormap = XCreateColormap(dpy,_window,DefaultVisual(dpy,DefaultScreen(dpy)),AllocAll);
  8493. _set_colormap(_colormap,3);
  8494. XSetWindowColormap(dpy,_window,_colormap);
  8495. }
  8496. static const char *const _window_class = cimg_appname;
  8497. XClassHint *const window_class = XAllocClassHint();
  8498. window_class->res_name = (char*)_window_class;
  8499. window_class->res_class = (char*)_window_class;
  8500. XSetClassHint(dpy,_window,window_class);
  8501. XFree(window_class);
  8502. _window_width = _width;
  8503. _window_height = _height;
  8504. // Create XImage
  8505. #ifdef cimg_use_xshm
  8506. _shminfo = 0;
  8507. if (XShmQueryExtension(dpy)) {
  8508. _shminfo = new XShmSegmentInfo;
  8509. _image = XShmCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits,
  8510. ZPixmap,0,_shminfo,_width,_height);
  8511. if (!_image) { delete _shminfo; _shminfo = 0; }
  8512. else {
  8513. _shminfo->shmid = shmget(IPC_PRIVATE,_image->bytes_per_line*_image->height,IPC_CREAT|0777);
  8514. if (_shminfo->shmid==-1) { XDestroyImage(_image); delete _shminfo; _shminfo = 0; }
  8515. else {
  8516. _shminfo->shmaddr = _image->data = (char*)(_data = shmat(_shminfo->shmid,0,0));
  8517. if (_shminfo->shmaddr==(char*)-1) {
  8518. shmctl(_shminfo->shmid,IPC_RMID,0); XDestroyImage(_image); delete _shminfo; _shminfo = 0;
  8519. } else {
  8520. _shminfo->readOnly = 0;
  8521. cimg::X11_attr().is_shm_enabled = true;
  8522. XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm);
  8523. XShmAttach(dpy,_shminfo);
  8524. XSync(dpy,0);
  8525. XSetErrorHandler(oldXErrorHandler);
  8526. if (!cimg::X11_attr().is_shm_enabled) {
  8527. shmdt(_shminfo->shmaddr); shmctl(_shminfo->shmid,IPC_RMID,0); XDestroyImage(_image);
  8528. delete _shminfo; _shminfo = 0;
  8529. }
  8530. }
  8531. }
  8532. }
  8533. }
  8534. if (!_shminfo)
  8535. #endif
  8536. {
  8537. const cimg_ulong buf_size = (cimg_ulong)_width*_height*(cimg::X11_attr().nb_bits==8?1:
  8538. (cimg::X11_attr().nb_bits==16?2:4));
  8539. _data = std::malloc(buf_size);
  8540. _image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits,
  8541. ZPixmap,0,(char*)_data,_width,_height,8,0);
  8542. }
  8543. _wm_window_atom = XInternAtom(dpy,"WM_DELETE_WINDOW",0);
  8544. _wm_protocol_atom = XInternAtom(dpy,"WM_PROTOCOLS",0);
  8545. XSetWMProtocols(dpy,_window,&_wm_window_atom,1);
  8546. if (_is_fullscreen) XGrabKeyboard(dpy,_window,1,GrabModeAsync,GrabModeAsync,CurrentTime);
  8547. cimg::X11_attr().wins[cimg::X11_attr().nb_wins++]=this;
  8548. if (!_is_closed) _map_window(); else { _window_x = _window_y = cimg::type<int>::min(); }
  8549. cimg_unlock_display();
  8550. cimg::mutex(14,0);
  8551. }
  8552. CImgDisplay& assign() {
  8553. if (is_empty()) return flush();
  8554. Display *const dpy = cimg::X11_attr().display;
  8555. cimg_lock_display();
  8556. // Remove display window from event thread list.
  8557. unsigned int i;
  8558. for (i = 0; i<cimg::X11_attr().nb_wins && cimg::X11_attr().wins[i]!=this; ++i) {}
  8559. for ( ; i<cimg::X11_attr().nb_wins - 1; ++i) cimg::X11_attr().wins[i] = cimg::X11_attr().wins[i + 1];
  8560. --cimg::X11_attr().nb_wins;
  8561. // Destroy window, image, colormap and title.
  8562. if (_is_fullscreen && !_is_closed) _desinit_fullscreen();
  8563. XDestroyWindow(dpy,_window);
  8564. _window = 0;
  8565. #ifdef cimg_use_xshm
  8566. if (_shminfo) {
  8567. XShmDetach(dpy,_shminfo);
  8568. XDestroyImage(_image);
  8569. shmdt(_shminfo->shmaddr);
  8570. shmctl(_shminfo->shmid,IPC_RMID,0);
  8571. delete _shminfo;
  8572. _shminfo = 0;
  8573. } else
  8574. #endif
  8575. XDestroyImage(_image);
  8576. _data = 0; _image = 0;
  8577. if (cimg::X11_attr().nb_bits==8) XFreeColormap(dpy,_colormap);
  8578. _colormap = 0;
  8579. XSync(dpy,0);
  8580. // Reset display variables.
  8581. delete[] _title;
  8582. _width = _height = _normalization = _window_width = _window_height = 0;
  8583. _window_x = _window_y = 0;
  8584. _is_fullscreen = false;
  8585. _is_closed = true;
  8586. _min = _max = 0;
  8587. _title = 0;
  8588. flush();
  8589. cimg_unlock_display();
  8590. return *this;
  8591. }
  8592. CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *const title=0,
  8593. const unsigned int normalization_type=3,
  8594. const bool fullscreen_flag=false, const bool closed_flag=false) {
  8595. if (!dimw || !dimh) return assign();
  8596. _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag);
  8597. _min = _max = 0;
  8598. std::memset(_data,0,(cimg::X11_attr().nb_bits==8?sizeof(unsigned char):
  8599. (cimg::X11_attr().nb_bits==16?sizeof(unsigned short):sizeof(unsigned int)))*
  8600. (size_t)_width*_height);
  8601. return paint();
  8602. }
  8603. template<typename T>
  8604. CImgDisplay& assign(const CImg<T>& img, const char *const title=0,
  8605. const unsigned int normalization_type=3,
  8606. const bool fullscreen_flag=false, const bool closed_flag=false) {
  8607. if (!img) return assign();
  8608. CImg<T> tmp;
  8609. const CImg<T>& nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2,
  8610. (img._height - 1)/2,
  8611. (img._depth - 1)/2));
  8612. _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
  8613. if (_normalization==2) _min = (float)nimg.min_max(_max);
  8614. return render(nimg).paint();
  8615. }
  8616. template<typename T>
  8617. CImgDisplay& assign(const CImgList<T>& list, const char *const title=0,
  8618. const unsigned int normalization_type=3,
  8619. const bool fullscreen_flag=false, const bool closed_flag=false) {
  8620. if (!list) return assign();
  8621. CImg<T> tmp;
  8622. const CImg<T> img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2,
  8623. (img._height - 1)/2,
  8624. (img._depth - 1)/2));
  8625. _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
  8626. if (_normalization==2) _min = (float)nimg.min_max(_max);
  8627. return render(nimg).paint();
  8628. }
  8629. CImgDisplay& assign(const CImgDisplay& disp) {
  8630. if (!disp) return assign();
  8631. _assign(disp._width,disp._height,disp._title,disp._normalization,disp._is_fullscreen,disp._is_closed);
  8632. std::memcpy(_data,disp._data,(cimg::X11_attr().nb_bits==8?sizeof(unsigned char):
  8633. cimg::X11_attr().nb_bits==16?sizeof(unsigned short):
  8634. sizeof(unsigned int))*(size_t)_width*_height);
  8635. return paint();
  8636. }
  8637. CImgDisplay& resize(const int nwidth, const int nheight, const bool force_redraw=true) {
  8638. if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign();
  8639. if (is_empty()) return assign(nwidth,nheight);
  8640. Display *const dpy = cimg::X11_attr().display;
  8641. const unsigned int
  8642. tmpdimx = (nwidth>0)?nwidth:(-nwidth*width()/100),
  8643. tmpdimy = (nheight>0)?nheight:(-nheight*height()/100),
  8644. dimx = tmpdimx?tmpdimx:1,
  8645. dimy = tmpdimy?tmpdimy:1;
  8646. if (_width!=dimx || _height!=dimy || _window_width!=dimx || _window_height!=dimy) {
  8647. show();
  8648. cimg_lock_display();
  8649. if (_window_width!=dimx || _window_height!=dimy) {
  8650. XWindowAttributes attr;
  8651. for (unsigned int i = 0; i<10; ++i) {
  8652. XResizeWindow(dpy,_window,dimx,dimy);
  8653. XGetWindowAttributes(dpy,_window,&attr);
  8654. if (attr.width==(int)dimx && attr.height==(int)dimy) break;
  8655. cimg::wait(5);
  8656. }
  8657. }
  8658. if (_width!=dimx || _height!=dimy) switch (cimg::X11_attr().nb_bits) {
  8659. case 8 : { unsigned char pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break;
  8660. case 16 : { unsigned short pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break;
  8661. default : { unsigned int pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); }
  8662. }
  8663. _window_width = _width = dimx; _window_height = _height = dimy;
  8664. cimg_unlock_display();
  8665. }
  8666. _is_resized = false;
  8667. if (_is_fullscreen) move((screen_width() - _width)/2,(screen_height() - _height)/2);
  8668. if (force_redraw) return paint();
  8669. return *this;
  8670. }
  8671. CImgDisplay& toggle_fullscreen(const bool force_redraw=true) {
  8672. if (is_empty()) return *this;
  8673. if (force_redraw) {
  8674. const cimg_ulong buf_size = (cimg_ulong)_width*_height*
  8675. (cimg::X11_attr().nb_bits==8?1:(cimg::X11_attr().nb_bits==16?2:4));
  8676. void *image_data = std::malloc(buf_size);
  8677. std::memcpy(image_data,_data,buf_size);
  8678. assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
  8679. std::memcpy(_data,image_data,buf_size);
  8680. std::free(image_data);
  8681. return paint();
  8682. }
  8683. return assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
  8684. }
  8685. CImgDisplay& show() {
  8686. if (is_empty() || !_is_closed) return *this;
  8687. cimg_lock_display();
  8688. if (_is_fullscreen) _init_fullscreen();
  8689. _map_window();
  8690. _is_closed = false;
  8691. cimg_unlock_display();
  8692. return paint();
  8693. }
  8694. CImgDisplay& close() {
  8695. if (is_empty() || _is_closed) return *this;
  8696. Display *const dpy = cimg::X11_attr().display;
  8697. cimg_lock_display();
  8698. if (_is_fullscreen) _desinit_fullscreen();
  8699. XUnmapWindow(dpy,_window);
  8700. _window_x = _window_y = -1;
  8701. _is_closed = true;
  8702. cimg_unlock_display();
  8703. return *this;
  8704. }
  8705. CImgDisplay& move(const int posx, const int posy) {
  8706. if (is_empty()) return *this;
  8707. if (_window_x!=posx || _window_y!=posy) {
  8708. show();
  8709. Display *const dpy = cimg::X11_attr().display;
  8710. cimg_lock_display();
  8711. XMoveWindow(dpy,_window,posx,posy);
  8712. _window_x = posx; _window_y = posy;
  8713. cimg_unlock_display();
  8714. }
  8715. _is_moved = false;
  8716. return paint();
  8717. }
  8718. CImgDisplay& show_mouse() {
  8719. if (is_empty()) return *this;
  8720. Display *const dpy = cimg::X11_attr().display;
  8721. cimg_lock_display();
  8722. XUndefineCursor(dpy,_window);
  8723. cimg_unlock_display();
  8724. return *this;
  8725. }
  8726. CImgDisplay& hide_mouse() {
  8727. if (is_empty()) return *this;
  8728. Display *const dpy = cimg::X11_attr().display;
  8729. cimg_lock_display();
  8730. static const char pix_data[8] = { 0 };
  8731. XColor col;
  8732. col.red = col.green = col.blue = 0;
  8733. Pixmap pix = XCreateBitmapFromData(dpy,_window,pix_data,8,8);
  8734. Cursor cur = XCreatePixmapCursor(dpy,pix,pix,&col,&col,0,0);
  8735. XFreePixmap(dpy,pix);
  8736. XDefineCursor(dpy,_window,cur);
  8737. cimg_unlock_display();
  8738. return *this;
  8739. }
  8740. CImgDisplay& set_mouse(const int posx, const int posy) {
  8741. if (is_empty() || _is_closed) return *this;
  8742. Display *const dpy = cimg::X11_attr().display;
  8743. cimg_lock_display();
  8744. XWarpPointer(dpy,0L,_window,0,0,0,0,posx,posy);
  8745. _mouse_x = posx; _mouse_y = posy;
  8746. _is_moved = false;
  8747. XSync(dpy,0);
  8748. cimg_unlock_display();
  8749. return *this;
  8750. }
  8751. CImgDisplay& set_title(const char *const format, ...) {
  8752. if (is_empty()) return *this;
  8753. char *const tmp = new char[1024];
  8754. va_list ap;
  8755. va_start(ap, format);
  8756. cimg_vsnprintf(tmp,1024,format,ap);
  8757. va_end(ap);
  8758. if (!std::strcmp(_title,tmp)) { delete[] tmp; return *this; }
  8759. delete[] _title;
  8760. const unsigned int s = (unsigned int)std::strlen(tmp) + 1;
  8761. _title = new char[s];
  8762. std::memcpy(_title,tmp,s*sizeof(char));
  8763. Display *const dpy = cimg::X11_attr().display;
  8764. cimg_lock_display();
  8765. XStoreName(dpy,_window,tmp);
  8766. cimg_unlock_display();
  8767. delete[] tmp;
  8768. return *this;
  8769. }
  8770. template<typename T>
  8771. CImgDisplay& display(const CImg<T>& img) {
  8772. if (!img)
  8773. throw CImgArgumentException(_cimgdisplay_instance
  8774. "display(): Empty specified image.",
  8775. cimgdisplay_instance);
  8776. if (is_empty()) return assign(img);
  8777. return render(img).paint(false);
  8778. }
  8779. CImgDisplay& paint(const bool wait_expose=true) {
  8780. if (is_empty()) return *this;
  8781. cimg_lock_display();
  8782. _paint(wait_expose);
  8783. cimg_unlock_display();
  8784. return *this;
  8785. }
  8786. template<typename T>
  8787. CImgDisplay& render(const CImg<T>& img, const bool flag8=false) {
  8788. if (!img)
  8789. throw CImgArgumentException(_cimgdisplay_instance
  8790. "render(): Empty specified image.",
  8791. cimgdisplay_instance);
  8792. if (is_empty()) return *this;
  8793. if (img._depth!=1) return render(img.get_projections2d((img._width - 1)/2,(img._height - 1)/2,
  8794. (img._depth - 1)/2));
  8795. if (cimg::X11_attr().nb_bits==8 && (img._width!=_width || img._height!=_height))
  8796. return render(img.get_resize(_width,_height,1,-100,1));
  8797. if (cimg::X11_attr().nb_bits==8 && !flag8 && img._spectrum==3) {
  8798. static const CImg<typename CImg<T>::ucharT> default_colormap = CImg<typename CImg<T>::ucharT>::default_LUT256();
  8799. return render(img.get_index(default_colormap,1,false));
  8800. }
  8801. const T
  8802. *data1 = img._data,
  8803. *data2 = (img._spectrum>1)?img.data(0,0,0,1):data1,
  8804. *data3 = (img._spectrum>2)?img.data(0,0,0,2):data1;
  8805. if (cimg::X11_attr().is_blue_first) cimg::swap(data1,data3);
  8806. cimg_lock_display();
  8807. if (!_normalization || (_normalization==3 && cimg::type<T>::string()==cimg::type<unsigned char>::string())) {
  8808. _min = _max = 0;
  8809. switch (cimg::X11_attr().nb_bits) {
  8810. case 8 : { // 256 colormap, no normalization
  8811. _set_colormap(_colormap,img._spectrum);
  8812. unsigned char
  8813. *const ndata = (img._width==_width && img._height==_height)?(unsigned char*)_data:
  8814. new unsigned char[(size_t)img._width*img._height],
  8815. *ptrd = (unsigned char*)ndata;
  8816. switch (img._spectrum) {
  8817. case 1 :
  8818. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
  8819. (*ptrd++) = (unsigned char)*(data1++);
  8820. break;
  8821. case 2 : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  8822. const unsigned char
  8823. R = (unsigned char)*(data1++),
  8824. G = (unsigned char)*(data2++);
  8825. (*ptrd++) = (R&0xf0) | (G>>4);
  8826. } break;
  8827. default : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  8828. const unsigned char
  8829. R = (unsigned char)*(data1++),
  8830. G = (unsigned char)*(data2++),
  8831. B = (unsigned char)*(data3++);
  8832. (*ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6);
  8833. }
  8834. }
  8835. if (ndata!=_data) {
  8836. _render_resize(ndata,img._width,img._height,(unsigned char*)_data,_width,_height);
  8837. delete[] ndata;
  8838. }
  8839. } break;
  8840. case 16 : { // 16 bits colors, no normalization
  8841. unsigned short *const ndata = (img._width==_width && img._height==_height)?(unsigned short*)_data:
  8842. new unsigned short[(size_t)img._width*img._height];
  8843. unsigned char *ptrd = (unsigned char*)ndata;
  8844. const unsigned int M = 248;
  8845. switch (img._spectrum) {
  8846. case 1 :
  8847. if (cimg::X11_attr().byte_order)
  8848. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  8849. const unsigned char val = (unsigned char)*(data1++), G = val>>2;
  8850. ptrd[0] = (val&M) | (G>>3);
  8851. ptrd[1] = (G<<5) | (G>>1);
  8852. ptrd+=2;
  8853. } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  8854. const unsigned char val = (unsigned char)*(data1++), G = val>>2;
  8855. ptrd[0] = (G<<5) | (G>>1);
  8856. ptrd[1] = (val&M) | (G>>3);
  8857. ptrd+=2;
  8858. }
  8859. break;
  8860. case 2 :
  8861. if (cimg::X11_attr().byte_order)
  8862. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  8863. const unsigned char G = (unsigned char)*(data2++)>>2;
  8864. ptrd[0] = ((unsigned char)*(data1++)&M) | (G>>3);
  8865. ptrd[1] = (G<<5);
  8866. ptrd+=2;
  8867. } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  8868. const unsigned char G = (unsigned char)*(data2++)>>2;
  8869. ptrd[0] = (G<<5);
  8870. ptrd[1] = ((unsigned char)*(data1++)&M) | (G>>3);
  8871. ptrd+=2;
  8872. }
  8873. break;
  8874. default :
  8875. if (cimg::X11_attr().byte_order)
  8876. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  8877. const unsigned char G = (unsigned char)*(data2++)>>2;
  8878. ptrd[0] = ((unsigned char)*(data1++)&M) | (G>>3);
  8879. ptrd[1] = (G<<5) | ((unsigned char)*(data3++)>>3);
  8880. ptrd+=2;
  8881. } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  8882. const unsigned char G = (unsigned char)*(data2++)>>2;
  8883. ptrd[0] = (G<<5) | ((unsigned char)*(data3++)>>3);
  8884. ptrd[1] = ((unsigned char)*(data1++)&M) | (G>>3);
  8885. ptrd+=2;
  8886. }
  8887. }
  8888. if (ndata!=_data) {
  8889. _render_resize(ndata,img._width,img._height,(unsigned short*)_data,_width,_height);
  8890. delete[] ndata;
  8891. }
  8892. } break;
  8893. default : { // 24 bits colors, no normalization
  8894. unsigned int *const ndata = (img._width==_width && img._height==_height)?(unsigned int*)_data:
  8895. new unsigned int[(size_t)img._width*img._height];
  8896. if (sizeof(int)==4) { // 32 bits int uses optimized version
  8897. unsigned int *ptrd = ndata;
  8898. switch (img._spectrum) {
  8899. case 1 :
  8900. if (cimg::X11_attr().byte_order==cimg::endianness())
  8901. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  8902. const unsigned char val = (unsigned char)*(data1++);
  8903. *(ptrd++) = (val<<16) | (val<<8) | val;
  8904. }
  8905. else
  8906. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  8907. const unsigned char val = (unsigned char)*(data1++);
  8908. *(ptrd++) = (val<<16) | (val<<8) | val;
  8909. }
  8910. break;
  8911. case 2 :
  8912. if (cimg::X11_attr().byte_order==cimg::endianness())
  8913. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
  8914. *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8);
  8915. else
  8916. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
  8917. *(ptrd++) = ((unsigned char)*(data2++)<<16) | ((unsigned char)*(data1++)<<8);
  8918. break;
  8919. default :
  8920. if (cimg::X11_attr().byte_order==cimg::endianness())
  8921. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
  8922. *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8) |
  8923. (unsigned char)*(data3++);
  8924. else
  8925. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
  8926. *(ptrd++) = ((unsigned char)*(data3++)<<24) | ((unsigned char)*(data2++)<<16) |
  8927. ((unsigned char)*(data1++)<<8);
  8928. }
  8929. } else {
  8930. unsigned char *ptrd = (unsigned char*)ndata;
  8931. switch (img._spectrum) {
  8932. case 1 :
  8933. if (cimg::X11_attr().byte_order)
  8934. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  8935. ptrd[0] = 0;
  8936. ptrd[1] = (unsigned char)*(data1++);
  8937. ptrd[2] = 0;
  8938. ptrd[3] = 0;
  8939. ptrd+=4;
  8940. } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  8941. ptrd[0] = 0;
  8942. ptrd[1] = 0;
  8943. ptrd[2] = (unsigned char)*(data1++);
  8944. ptrd[3] = 0;
  8945. ptrd+=4;
  8946. }
  8947. break;
  8948. case 2 :
  8949. if (cimg::X11_attr().byte_order) cimg::swap(data1,data2);
  8950. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  8951. ptrd[0] = 0;
  8952. ptrd[1] = (unsigned char)*(data2++);
  8953. ptrd[2] = (unsigned char)*(data1++);
  8954. ptrd[3] = 0;
  8955. ptrd+=4;
  8956. }
  8957. break;
  8958. default :
  8959. if (cimg::X11_attr().byte_order)
  8960. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  8961. ptrd[0] = 0;
  8962. ptrd[1] = (unsigned char)*(data1++);
  8963. ptrd[2] = (unsigned char)*(data2++);
  8964. ptrd[3] = (unsigned char)*(data3++);
  8965. ptrd+=4;
  8966. } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  8967. ptrd[0] = (unsigned char)*(data3++);
  8968. ptrd[1] = (unsigned char)*(data2++);
  8969. ptrd[2] = (unsigned char)*(data1++);
  8970. ptrd[3] = 0;
  8971. ptrd+=4;
  8972. }
  8973. }
  8974. }
  8975. if (ndata!=_data) {
  8976. _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height);
  8977. delete[] ndata;
  8978. }
  8979. }
  8980. }
  8981. } else {
  8982. if (_normalization==3) {
  8983. if (cimg::type<T>::is_float()) _min = (float)img.min_max(_max);
  8984. else { _min = (float)cimg::type<T>::min(); _max = (float)cimg::type<T>::max(); }
  8985. } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max);
  8986. const float delta = _max - _min, mm = 255/(delta?delta:1.0f);
  8987. switch (cimg::X11_attr().nb_bits) {
  8988. case 8 : { // 256 colormap, with normalization
  8989. _set_colormap(_colormap,img._spectrum);
  8990. unsigned char *const ndata = (img._width==_width && img._height==_height)?(unsigned char*)_data:
  8991. new unsigned char[(size_t)img._width*img._height];
  8992. unsigned char *ptrd = (unsigned char*)ndata;
  8993. switch (img._spectrum) {
  8994. case 1 : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  8995. const unsigned char R = (unsigned char)((*(data1++) - _min)*mm);
  8996. *(ptrd++) = R;
  8997. } break;
  8998. case 2 : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  8999. const unsigned char
  9000. R = (unsigned char)((*(data1++) - _min)*mm),
  9001. G = (unsigned char)((*(data2++) - _min)*mm);
  9002. (*ptrd++) = (R&0xf0) | (G>>4);
  9003. } break;
  9004. default :
  9005. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  9006. const unsigned char
  9007. R = (unsigned char)((*(data1++) - _min)*mm),
  9008. G = (unsigned char)((*(data2++) - _min)*mm),
  9009. B = (unsigned char)((*(data3++) - _min)*mm);
  9010. *(ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6);
  9011. }
  9012. }
  9013. if (ndata!=_data) {
  9014. _render_resize(ndata,img._width,img._height,(unsigned char*)_data,_width,_height);
  9015. delete[] ndata;
  9016. }
  9017. } break;
  9018. case 16 : { // 16 bits colors, with normalization
  9019. unsigned short *const ndata = (img._width==_width && img._height==_height)?(unsigned short*)_data:
  9020. new unsigned short[(size_t)img._width*img._height];
  9021. unsigned char *ptrd = (unsigned char*)ndata;
  9022. const unsigned int M = 248;
  9023. switch (img._spectrum) {
  9024. case 1 :
  9025. if (cimg::X11_attr().byte_order)
  9026. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  9027. const unsigned char val = (unsigned char)((*(data1++) - _min)*mm), G = val>>2;
  9028. ptrd[0] = (val&M) | (G>>3);
  9029. ptrd[1] = (G<<5) | (val>>3);
  9030. ptrd+=2;
  9031. } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  9032. const unsigned char val = (unsigned char)((*(data1++) - _min)*mm), G = val>>2;
  9033. ptrd[0] = (G<<5) | (val>>3);
  9034. ptrd[1] = (val&M) | (G>>3);
  9035. ptrd+=2;
  9036. }
  9037. break;
  9038. case 2 :
  9039. if (cimg::X11_attr().byte_order)
  9040. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  9041. const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2;
  9042. ptrd[0] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3);
  9043. ptrd[1] = (G<<5);
  9044. ptrd+=2;
  9045. } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  9046. const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2;
  9047. ptrd[0] = (G<<5);
  9048. ptrd[1] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3);
  9049. ptrd+=2;
  9050. }
  9051. break;
  9052. default :
  9053. if (cimg::X11_attr().byte_order)
  9054. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  9055. const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2;
  9056. ptrd[0] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3);
  9057. ptrd[1] = (G<<5) | ((unsigned char)((*(data3++) - _min)*mm)>>3);
  9058. ptrd+=2;
  9059. } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  9060. const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2;
  9061. ptrd[0] = (G<<5) | ((unsigned char)((*(data3++) - _min)*mm)>>3);
  9062. ptrd[1] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3);
  9063. ptrd+=2;
  9064. }
  9065. }
  9066. if (ndata!=_data) {
  9067. _render_resize(ndata,img._width,img._height,(unsigned short*)_data,_width,_height);
  9068. delete[] ndata;
  9069. }
  9070. } break;
  9071. default : { // 24 bits colors, with normalization
  9072. unsigned int *const ndata = (img._width==_width && img._height==_height)?(unsigned int*)_data:
  9073. new unsigned int[(size_t)img._width*img._height];
  9074. if (sizeof(int)==4) { // 32 bits int uses optimized version
  9075. unsigned int *ptrd = ndata;
  9076. switch (img._spectrum) {
  9077. case 1 :
  9078. if (cimg::X11_attr().byte_order==cimg::endianness())
  9079. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  9080. const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
  9081. *(ptrd++) = (val<<16) | (val<<8) | val;
  9082. }
  9083. else
  9084. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  9085. const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
  9086. *(ptrd++) = (val<<24) | (val<<16) | (val<<8);
  9087. }
  9088. break;
  9089. case 2 :
  9090. if (cimg::X11_attr().byte_order==cimg::endianness())
  9091. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
  9092. *(ptrd++) =
  9093. ((unsigned char)((*(data1++) - _min)*mm)<<16) |
  9094. ((unsigned char)((*(data2++) - _min)*mm)<<8);
  9095. else
  9096. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
  9097. *(ptrd++) =
  9098. ((unsigned char)((*(data2++) - _min)*mm)<<16) |
  9099. ((unsigned char)((*(data1++) - _min)*mm)<<8);
  9100. break;
  9101. default :
  9102. if (cimg::X11_attr().byte_order==cimg::endianness())
  9103. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
  9104. *(ptrd++) =
  9105. ((unsigned char)((*(data1++) - _min)*mm)<<16) |
  9106. ((unsigned char)((*(data2++) - _min)*mm)<<8) |
  9107. (unsigned char)((*(data3++) - _min)*mm);
  9108. else
  9109. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
  9110. *(ptrd++) =
  9111. ((unsigned char)((*(data3++) - _min)*mm)<<24) |
  9112. ((unsigned char)((*(data2++) - _min)*mm)<<16) |
  9113. ((unsigned char)((*(data1++) - _min)*mm)<<8);
  9114. }
  9115. } else {
  9116. unsigned char *ptrd = (unsigned char*)ndata;
  9117. switch (img._spectrum) {
  9118. case 1 :
  9119. if (cimg::X11_attr().byte_order)
  9120. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  9121. const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
  9122. ptrd[0] = 0;
  9123. ptrd[1] = val;
  9124. ptrd[2] = val;
  9125. ptrd[3] = val;
  9126. ptrd+=4;
  9127. } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  9128. const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
  9129. ptrd[0] = val;
  9130. ptrd[1] = val;
  9131. ptrd[2] = val;
  9132. ptrd[3] = 0;
  9133. ptrd+=4;
  9134. }
  9135. break;
  9136. case 2 :
  9137. if (cimg::X11_attr().byte_order) cimg::swap(data1,data2);
  9138. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  9139. ptrd[0] = 0;
  9140. ptrd[1] = (unsigned char)((*(data2++) - _min)*mm);
  9141. ptrd[2] = (unsigned char)((*(data1++) - _min)*mm);
  9142. ptrd[3] = 0;
  9143. ptrd+=4;
  9144. }
  9145. break;
  9146. default :
  9147. if (cimg::X11_attr().byte_order)
  9148. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  9149. ptrd[0] = 0;
  9150. ptrd[1] = (unsigned char)((*(data1++) - _min)*mm);
  9151. ptrd[2] = (unsigned char)((*(data2++) - _min)*mm);
  9152. ptrd[3] = (unsigned char)((*(data3++) - _min)*mm);
  9153. ptrd+=4;
  9154. } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  9155. ptrd[0] = (unsigned char)((*(data3++) - _min)*mm);
  9156. ptrd[1] = (unsigned char)((*(data2++) - _min)*mm);
  9157. ptrd[2] = (unsigned char)((*(data1++) - _min)*mm);
  9158. ptrd[3] = 0;
  9159. ptrd+=4;
  9160. }
  9161. }
  9162. }
  9163. if (ndata!=_data) {
  9164. _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height);
  9165. delete[] ndata;
  9166. }
  9167. }
  9168. }
  9169. }
  9170. cimg_unlock_display();
  9171. return *this;
  9172. }
  9173. template<typename T>
  9174. static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg<T>& img) {
  9175. img.assign();
  9176. Display *dpy = cimg::X11_attr().display;
  9177. cimg_lock_display();
  9178. if (!dpy) {
  9179. dpy = XOpenDisplay(0);
  9180. if (!dpy)
  9181. throw CImgDisplayException("CImgDisplay::screenshot(): Failed to open X11 display.");
  9182. }
  9183. Window root = DefaultRootWindow(dpy);
  9184. XWindowAttributes gwa;
  9185. XGetWindowAttributes(dpy,root,&gwa);
  9186. const int width = gwa.width, height = gwa.height;
  9187. int _x0 = x0, _y0 = y0, _x1 = x1, _y1 = y1;
  9188. if (_x0>_x1) cimg::swap(_x0,_x1);
  9189. if (_y0>_y1) cimg::swap(_y0,_y1);
  9190. XImage *image = 0;
  9191. if (_x1>=0 && _x0<width && _y1>=0 && _y0<height) {
  9192. _x0 = std::max(_x0,0);
  9193. _y0 = std::max(_y0,0);
  9194. _x1 = std::min(_x1,width - 1);
  9195. _y1 = std::min(_y1,height - 1);
  9196. image = XGetImage(dpy,root,_x0,_y0,_x1 - _x0 + 1,_y1 - _y0 + 1,AllPlanes,ZPixmap);
  9197. if (image) {
  9198. const unsigned long
  9199. red_mask = image->red_mask,
  9200. green_mask = image->green_mask,
  9201. blue_mask = image->blue_mask;
  9202. img.assign(image->width,image->height,1,3);
  9203. T *pR = img.data(0,0,0,0), *pG = img.data(0,0,0,1), *pB = img.data(0,0,0,2);
  9204. cimg_forXY(img,x,y) {
  9205. const unsigned long pixel = XGetPixel(image,x,y);
  9206. *(pR++) = (T)((pixel & red_mask)>>16);
  9207. *(pG++) = (T)((pixel & green_mask)>>8);
  9208. *(pB++) = (T)(pixel & blue_mask);
  9209. }
  9210. XDestroyImage(image);
  9211. }
  9212. }
  9213. if (!cimg::X11_attr().display) XCloseDisplay(dpy);
  9214. cimg_unlock_display();
  9215. if (img.is_empty())
  9216. throw CImgDisplayException("CImgDisplay::screenshot(): Failed to take screenshot "
  9217. "with coordinates (%d,%d)-(%d,%d).",
  9218. x0,y0,x1,y1);
  9219. }
  9220. template<typename T>
  9221. const CImgDisplay& snapshot(CImg<T>& img) const {
  9222. if (is_empty()) { img.assign(); return *this; }
  9223. const unsigned char *ptrs = (unsigned char*)_data;
  9224. img.assign(_width,_height,1,3);
  9225. T
  9226. *data1 = img.data(0,0,0,0),
  9227. *data2 = img.data(0,0,0,1),
  9228. *data3 = img.data(0,0,0,2);
  9229. if (cimg::X11_attr().is_blue_first) cimg::swap(data1,data3);
  9230. switch (cimg::X11_attr().nb_bits) {
  9231. case 8 : {
  9232. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  9233. const unsigned char val = *(ptrs++);
  9234. *(data1++) = (T)(val&0xe0);
  9235. *(data2++) = (T)((val&0x1c)<<3);
  9236. *(data3++) = (T)(val<<6);
  9237. }
  9238. } break;
  9239. case 16 : {
  9240. if (cimg::X11_attr().byte_order) for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  9241. const unsigned char
  9242. val0 = ptrs[0],
  9243. val1 = ptrs[1];
  9244. ptrs+=2;
  9245. *(data1++) = (T)(val0&0xf8);
  9246. *(data2++) = (T)((val0<<5) | ((val1&0xe0)>>5));
  9247. *(data3++) = (T)(val1<<3);
  9248. } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  9249. const unsigned short
  9250. val0 = ptrs[0],
  9251. val1 = ptrs[1];
  9252. ptrs+=2;
  9253. *(data1++) = (T)(val1&0xf8);
  9254. *(data2++) = (T)((val1<<5) | ((val0&0xe0)>>5));
  9255. *(data3++) = (T)(val0<<3);
  9256. }
  9257. } break;
  9258. default : {
  9259. if (cimg::X11_attr().byte_order) for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  9260. ++ptrs;
  9261. *(data1++) = (T)ptrs[0];
  9262. *(data2++) = (T)ptrs[1];
  9263. *(data3++) = (T)ptrs[2];
  9264. ptrs+=3;
  9265. } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  9266. *(data3++) = (T)ptrs[0];
  9267. *(data2++) = (T)ptrs[1];
  9268. *(data1++) = (T)ptrs[2];
  9269. ptrs+=3;
  9270. ++ptrs;
  9271. }
  9272. }
  9273. }
  9274. return *this;
  9275. }
  9276. // Windows-based implementation.
  9277. //-------------------------------
  9278. #elif cimg_display==2
  9279. bool _is_mouse_tracked, _is_cursor_visible;
  9280. HANDLE _thread, _is_created, _mutex;
  9281. HWND _window, _background_window;
  9282. CLIENTCREATESTRUCT _ccs;
  9283. unsigned int *_data;
  9284. DEVMODE _curr_mode;
  9285. BITMAPINFO _bmi;
  9286. HDC _hdc;
  9287. static int screen_width() {
  9288. DEVMODE mode;
  9289. mode.dmSize = sizeof(DEVMODE);
  9290. mode.dmDriverExtra = 0;
  9291. EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode);
  9292. return (int)mode.dmPelsWidth;
  9293. }
  9294. static int screen_height() {
  9295. DEVMODE mode;
  9296. mode.dmSize = sizeof(DEVMODE);
  9297. mode.dmDriverExtra = 0;
  9298. EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode);
  9299. return (int)mode.dmPelsHeight;
  9300. }
  9301. static void wait_all() {
  9302. WaitForSingleObject(cimg::Win32_attr().wait_event,INFINITE);
  9303. }
  9304. static LRESULT APIENTRY _handle_events(HWND window, UINT msg, WPARAM wParam, LPARAM lParam) {
  9305. #ifdef _WIN64
  9306. CImgDisplay *const disp = (CImgDisplay*)GetWindowLongPtr(window,GWLP_USERDATA);
  9307. #else
  9308. CImgDisplay *const disp = (CImgDisplay*)GetWindowLong(window,GWL_USERDATA);
  9309. #endif
  9310. MSG st_msg;
  9311. switch (msg) {
  9312. case WM_CLOSE :
  9313. disp->_mouse_x = disp->_mouse_y = -1;
  9314. disp->_window_x = disp->_window_y = 0;
  9315. disp->set_button().set_key(0).set_key(0,false)._is_closed = true;
  9316. ReleaseMutex(disp->_mutex);
  9317. ShowWindow(disp->_window,SW_HIDE);
  9318. disp->_is_event = true;
  9319. SetEvent(cimg::Win32_attr().wait_event);
  9320. return 0;
  9321. case WM_SIZE : {
  9322. while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {}
  9323. WaitForSingleObject(disp->_mutex,INFINITE);
  9324. const unsigned int nw = LOWORD(lParam),nh = HIWORD(lParam);
  9325. if (nw && nh && (nw!=disp->_width || nh!=disp->_height)) {
  9326. disp->_window_width = nw;
  9327. disp->_window_height = nh;
  9328. disp->_mouse_x = disp->_mouse_y = -1;
  9329. disp->_is_resized = disp->_is_event = true;
  9330. SetEvent(cimg::Win32_attr().wait_event);
  9331. }
  9332. ReleaseMutex(disp->_mutex);
  9333. } break;
  9334. case WM_MOVE : {
  9335. while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {}
  9336. WaitForSingleObject(disp->_mutex,INFINITE);
  9337. const int nx = (int)(short)(LOWORD(lParam)), ny = (int)(short)(HIWORD(lParam));
  9338. if (nx!=disp->_window_x || ny!=disp->_window_y) {
  9339. disp->_window_x = nx;
  9340. disp->_window_y = ny;
  9341. disp->_is_moved = disp->_is_event = true;
  9342. SetEvent(cimg::Win32_attr().wait_event);
  9343. }
  9344. ReleaseMutex(disp->_mutex);
  9345. } break;
  9346. case WM_PAINT :
  9347. disp->paint();
  9348. cimg::mutex(15);
  9349. if (disp->_is_cursor_visible) while (ShowCursor(TRUE)<0); else while (ShowCursor(FALSE)>=0);
  9350. cimg::mutex(15,0);
  9351. break;
  9352. case WM_ERASEBKGND :
  9353. // return 0;
  9354. break;
  9355. case WM_KEYDOWN :
  9356. disp->set_key((unsigned int)wParam);
  9357. SetEvent(cimg::Win32_attr().wait_event);
  9358. break;
  9359. case WM_KEYUP :
  9360. disp->set_key((unsigned int)wParam,false);
  9361. SetEvent(cimg::Win32_attr().wait_event);
  9362. break;
  9363. case WM_MOUSEMOVE : {
  9364. while (PeekMessage(&st_msg,window,WM_MOUSEMOVE,WM_MOUSEMOVE,PM_REMOVE)) {}
  9365. disp->_mouse_x = LOWORD(lParam);
  9366. disp->_mouse_y = HIWORD(lParam);
  9367. #if (_WIN32_WINNT>=0x0400) && !defined(NOTRACKMOUSEEVENT)
  9368. if (!disp->_is_mouse_tracked) {
  9369. TRACKMOUSEEVENT tme;
  9370. tme.cbSize = sizeof(TRACKMOUSEEVENT);
  9371. tme.dwFlags = TME_LEAVE;
  9372. tme.hwndTrack = disp->_window;
  9373. if (TrackMouseEvent(&tme)) disp->_is_mouse_tracked = true;
  9374. }
  9375. #endif
  9376. if (disp->_mouse_x<0 || disp->_mouse_y<0 || disp->_mouse_x>=disp->width() || disp->_mouse_y>=disp->height())
  9377. disp->_mouse_x = disp->_mouse_y = -1;
  9378. disp->_is_event = true;
  9379. SetEvent(cimg::Win32_attr().wait_event);
  9380. cimg::mutex(15);
  9381. if (disp->_is_cursor_visible) while (ShowCursor(TRUE)<0); else while (ShowCursor(FALSE)>=0);
  9382. cimg::mutex(15,0);
  9383. } break;
  9384. case WM_MOUSELEAVE : {
  9385. disp->_mouse_x = disp->_mouse_y = -1;
  9386. disp->_is_mouse_tracked = false;
  9387. cimg::mutex(15);
  9388. while (ShowCursor(TRUE)<0) {}
  9389. cimg::mutex(15,0);
  9390. } break;
  9391. case WM_LBUTTONDOWN :
  9392. disp->set_button(1);
  9393. SetEvent(cimg::Win32_attr().wait_event);
  9394. break;
  9395. case WM_RBUTTONDOWN :
  9396. disp->set_button(2);
  9397. SetEvent(cimg::Win32_attr().wait_event);
  9398. break;
  9399. case WM_MBUTTONDOWN :
  9400. disp->set_button(3);
  9401. SetEvent(cimg::Win32_attr().wait_event);
  9402. break;
  9403. case WM_LBUTTONUP :
  9404. disp->set_button(1,false);
  9405. SetEvent(cimg::Win32_attr().wait_event);
  9406. break;
  9407. case WM_RBUTTONUP :
  9408. disp->set_button(2,false);
  9409. SetEvent(cimg::Win32_attr().wait_event);
  9410. break;
  9411. case WM_MBUTTONUP :
  9412. disp->set_button(3,false);
  9413. SetEvent(cimg::Win32_attr().wait_event);
  9414. break;
  9415. case 0x020A : // WM_MOUSEWHEEL:
  9416. disp->set_wheel((int)((short)HIWORD(wParam))/120);
  9417. SetEvent(cimg::Win32_attr().wait_event);
  9418. }
  9419. return DefWindowProc(window,msg,wParam,lParam);
  9420. }
  9421. static DWORD WINAPI _events_thread(void* arg) {
  9422. CImgDisplay *const disp = (CImgDisplay*)(((void**)arg)[0]);
  9423. const char *const title = (const char*)(((void**)arg)[1]);
  9424. MSG msg;
  9425. delete[] (void**)arg;
  9426. disp->_bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  9427. disp->_bmi.bmiHeader.biWidth = disp->width();
  9428. disp->_bmi.bmiHeader.biHeight = -disp->height();
  9429. disp->_bmi.bmiHeader.biPlanes = 1;
  9430. disp->_bmi.bmiHeader.biBitCount = 32;
  9431. disp->_bmi.bmiHeader.biCompression = BI_RGB;
  9432. disp->_bmi.bmiHeader.biSizeImage = 0;
  9433. disp->_bmi.bmiHeader.biXPelsPerMeter = 1;
  9434. disp->_bmi.bmiHeader.biYPelsPerMeter = 1;
  9435. disp->_bmi.bmiHeader.biClrUsed = 0;
  9436. disp->_bmi.bmiHeader.biClrImportant = 0;
  9437. disp->_data = new unsigned int[(size_t)disp->_width*disp->_height];
  9438. if (!disp->_is_fullscreen) { // Normal window
  9439. RECT rect;
  9440. rect.left = rect.top = 0; rect.right = (LONG)disp->_width - 1; rect.bottom = (LONG)disp->_height - 1;
  9441. AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
  9442. const int
  9443. border1 = (int)((rect.right - rect.left + 1 - disp->_width)/2),
  9444. border2 = (int)(rect.bottom - rect.top + 1 - disp->_height - border1);
  9445. disp->_window = CreateWindowA("MDICLIENT",title?title:" ",
  9446. WS_OVERLAPPEDWINDOW | (disp->_is_closed?0:WS_VISIBLE), CW_USEDEFAULT,CW_USEDEFAULT,
  9447. disp->_width + 2*border1, disp->_height + border1 + border2,
  9448. 0,0,0,&(disp->_ccs));
  9449. if (!disp->_is_closed) {
  9450. GetWindowRect(disp->_window,&rect);
  9451. disp->_window_x = rect.left + border1;
  9452. disp->_window_y = rect.top + border2;
  9453. } else disp->_window_x = disp->_window_y = 0;
  9454. } else { // Fullscreen window
  9455. const unsigned int
  9456. sx = (unsigned int)screen_width(),
  9457. sy = (unsigned int)screen_height();
  9458. disp->_window = CreateWindowA("MDICLIENT",title?title:" ",
  9459. WS_POPUP | (disp->_is_closed?0:WS_VISIBLE),
  9460. (sx - disp->_width)/2,
  9461. (sy - disp->_height)/2,
  9462. disp->_width,disp->_height,0,0,0,&(disp->_ccs));
  9463. disp->_window_x = disp->_window_y = 0;
  9464. }
  9465. SetForegroundWindow(disp->_window);
  9466. disp->_hdc = GetDC(disp->_window);
  9467. disp->_window_width = disp->_width;
  9468. disp->_window_height = disp->_height;
  9469. disp->flush();
  9470. #ifdef _WIN64
  9471. SetWindowLongPtr(disp->_window,GWLP_USERDATA,(LONG_PTR)disp);
  9472. SetWindowLongPtr(disp->_window,GWLP_WNDPROC,(LONG_PTR)_handle_events);
  9473. #else
  9474. SetWindowLong(disp->_window,GWL_USERDATA,(LONG)disp);
  9475. SetWindowLong(disp->_window,GWL_WNDPROC,(LONG)_handle_events);
  9476. #endif
  9477. SetEvent(disp->_is_created);
  9478. while (GetMessage(&msg,0,0,0)) DispatchMessage(&msg);
  9479. return 0;
  9480. }
  9481. CImgDisplay& _update_window_pos() {
  9482. if (_is_closed) _window_x = _window_y = -1;
  9483. else {
  9484. RECT rect;
  9485. rect.left = rect.top = 0; rect.right = (LONG)_width - 1; rect.bottom = (LONG)_height - 1;
  9486. AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
  9487. const int
  9488. border1 = (int)((rect.right - rect.left + 1 - _width)/2),
  9489. border2 = (int)(rect.bottom - rect.top + 1 - _height - border1);
  9490. GetWindowRect(_window,&rect);
  9491. _window_x = rect.left + border1;
  9492. _window_y = rect.top + border2;
  9493. }
  9494. return *this;
  9495. }
  9496. void _init_fullscreen() {
  9497. _background_window = 0;
  9498. if (!_is_fullscreen || _is_closed) _curr_mode.dmSize = 0;
  9499. else {
  9500. DEVMODE mode;
  9501. unsigned int imode = 0, ibest = 0, bestbpp = 0, bw = ~0U, bh = ~0U;
  9502. for (mode.dmSize = sizeof(DEVMODE), mode.dmDriverExtra = 0; EnumDisplaySettings(0,imode,&mode); ++imode) {
  9503. const unsigned int nw = mode.dmPelsWidth, nh = mode.dmPelsHeight;
  9504. if (nw>=_width && nh>=_height && mode.dmBitsPerPel>=bestbpp && nw<=bw && nh<=bh) {
  9505. bestbpp = mode.dmBitsPerPel;
  9506. ibest = imode;
  9507. bw = nw; bh = nh;
  9508. }
  9509. }
  9510. if (bestbpp) {
  9511. _curr_mode.dmSize = sizeof(DEVMODE); _curr_mode.dmDriverExtra = 0;
  9512. EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&_curr_mode);
  9513. EnumDisplaySettings(0,ibest,&mode);
  9514. ChangeDisplaySettings(&mode,0);
  9515. } else _curr_mode.dmSize = 0;
  9516. const unsigned int
  9517. sx = (unsigned int)screen_width(),
  9518. sy = (unsigned int)screen_height();
  9519. if (sx!=_width || sy!=_height) {
  9520. CLIENTCREATESTRUCT background_ccs;
  9521. _background_window = CreateWindowA("MDICLIENT","",WS_POPUP | WS_VISIBLE, 0,0,sx,sy,0,0,0,&background_ccs);
  9522. SetForegroundWindow(_background_window);
  9523. }
  9524. }
  9525. }
  9526. void _desinit_fullscreen() {
  9527. if (!_is_fullscreen) return;
  9528. if (_background_window) DestroyWindow(_background_window);
  9529. _background_window = 0;
  9530. if (_curr_mode.dmSize) ChangeDisplaySettings(&_curr_mode,0);
  9531. _is_fullscreen = false;
  9532. }
  9533. CImgDisplay& _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0,
  9534. const unsigned int normalization_type=3,
  9535. const bool fullscreen_flag=false, const bool closed_flag=false) {
  9536. // Allocate space for window title
  9537. const char *const nptitle = ptitle?ptitle:"";
  9538. const unsigned int s = (unsigned int)std::strlen(nptitle) + 1;
  9539. char *const tmp_title = s?new char[s]:0;
  9540. if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char));
  9541. // Destroy previous window if existing
  9542. if (!is_empty()) assign();
  9543. // Set display variables
  9544. _width = std::min(dimw,(unsigned int)screen_width());
  9545. _height = std::min(dimh,(unsigned int)screen_height());
  9546. _normalization = normalization_type<4?normalization_type:3;
  9547. _is_fullscreen = fullscreen_flag;
  9548. _window_x = _window_y = 0;
  9549. _is_closed = closed_flag;
  9550. _is_cursor_visible = true;
  9551. _is_mouse_tracked = false;
  9552. _title = tmp_title;
  9553. flush();
  9554. if (_is_fullscreen) _init_fullscreen();
  9555. // Create event thread
  9556. void *const arg = (void*)(new void*[2]);
  9557. ((void**)arg)[0] = (void*)this;
  9558. ((void**)arg)[1] = (void*)_title;
  9559. _mutex = CreateMutex(0,FALSE,0);
  9560. _is_created = CreateEvent(0,FALSE,FALSE,0);
  9561. _thread = CreateThread(0,0,_events_thread,arg,0,0);
  9562. WaitForSingleObject(_is_created,INFINITE);
  9563. return *this;
  9564. }
  9565. CImgDisplay& assign() {
  9566. if (is_empty()) return flush();
  9567. DestroyWindow(_window);
  9568. TerminateThread(_thread,0);
  9569. delete[] _data;
  9570. delete[] _title;
  9571. _data = 0;
  9572. _title = 0;
  9573. if (_is_fullscreen) _desinit_fullscreen();
  9574. _width = _height = _normalization = _window_width = _window_height = 0;
  9575. _window_x = _window_y = 0;
  9576. _is_fullscreen = false;
  9577. _is_closed = true;
  9578. _min = _max = 0;
  9579. _title = 0;
  9580. flush();
  9581. return *this;
  9582. }
  9583. CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *const title=0,
  9584. const unsigned int normalization_type=3,
  9585. const bool fullscreen_flag=false, const bool closed_flag=false) {
  9586. if (!dimw || !dimh) return assign();
  9587. _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag);
  9588. _min = _max = 0;
  9589. std::memset(_data,0,sizeof(unsigned int)*_width*_height);
  9590. return paint();
  9591. }
  9592. template<typename T>
  9593. CImgDisplay& assign(const CImg<T>& img, const char *const title=0,
  9594. const unsigned int normalization_type=3,
  9595. const bool fullscreen_flag=false, const bool closed_flag=false) {
  9596. if (!img) return assign();
  9597. CImg<T> tmp;
  9598. const CImg<T>& nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2,
  9599. (img._height - 1)/2,
  9600. (img._depth - 1)/2));
  9601. _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
  9602. if (_normalization==2) _min = (float)nimg.min_max(_max);
  9603. return display(nimg);
  9604. }
  9605. template<typename T>
  9606. CImgDisplay& assign(const CImgList<T>& list, const char *const title=0,
  9607. const unsigned int normalization_type=3,
  9608. const bool fullscreen_flag=false, const bool closed_flag=false) {
  9609. if (!list) return assign();
  9610. CImg<T> tmp;
  9611. const CImg<T> img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2,
  9612. (img._height - 1)/2,
  9613. (img._depth - 1)/2));
  9614. _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
  9615. if (_normalization==2) _min = (float)nimg.min_max(_max);
  9616. return display(nimg);
  9617. }
  9618. CImgDisplay& assign(const CImgDisplay& disp) {
  9619. if (!disp) return assign();
  9620. _assign(disp._width,disp._height,disp._title,disp._normalization,disp._is_fullscreen,disp._is_closed);
  9621. std::memcpy(_data,disp._data,sizeof(unsigned int)*_width*_height);
  9622. return paint();
  9623. }
  9624. CImgDisplay& resize(const int nwidth, const int nheight, const bool force_redraw=true) {
  9625. if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign();
  9626. if (is_empty()) return assign(nwidth,nheight);
  9627. const unsigned int
  9628. tmpdimx = (nwidth>0)?nwidth:(-nwidth*_width/100),
  9629. tmpdimy = (nheight>0)?nheight:(-nheight*_height/100),
  9630. dimx = tmpdimx?tmpdimx:1,
  9631. dimy = tmpdimy?tmpdimy:1;
  9632. if (_width!=dimx || _height!=dimy || _window_width!=dimx || _window_height!=dimy) {
  9633. if (_window_width!=dimx || _window_height!=dimy) {
  9634. RECT rect; rect.left = rect.top = 0; rect.right = (LONG)dimx - 1; rect.bottom = (LONG)dimy - 1;
  9635. AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
  9636. const int cwidth = rect.right - rect.left + 1, cheight = rect.bottom - rect.top + 1;
  9637. SetWindowPos(_window,0,0,0,cwidth,cheight,SWP_NOMOVE | SWP_NOZORDER | SWP_NOCOPYBITS);
  9638. }
  9639. if (_width!=dimx || _height!=dimy) {
  9640. unsigned int *const ndata = new unsigned int[dimx*dimy];
  9641. if (force_redraw) _render_resize(_data,_width,_height,ndata,dimx,dimy);
  9642. else std::memset(ndata,0x80,sizeof(unsigned int)*dimx*dimy);
  9643. delete[] _data;
  9644. _data = ndata;
  9645. _bmi.bmiHeader.biWidth = (LONG)dimx;
  9646. _bmi.bmiHeader.biHeight = -(int)dimy;
  9647. _width = dimx;
  9648. _height = dimy;
  9649. }
  9650. _window_width = dimx; _window_height = dimy;
  9651. show();
  9652. }
  9653. _is_resized = false;
  9654. if (_is_fullscreen) move((screen_width() - width())/2,(screen_height() - height())/2);
  9655. if (force_redraw) return paint();
  9656. return *this;
  9657. }
  9658. CImgDisplay& toggle_fullscreen(const bool force_redraw=true) {
  9659. if (is_empty()) return *this;
  9660. if (force_redraw) {
  9661. const cimg_ulong buf_size = (cimg_ulong)_width*_height*4;
  9662. void *odata = std::malloc(buf_size);
  9663. if (odata) {
  9664. std::memcpy(odata,_data,buf_size);
  9665. assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
  9666. std::memcpy(_data,odata,buf_size);
  9667. std::free(odata);
  9668. }
  9669. return paint();
  9670. }
  9671. return assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
  9672. }
  9673. CImgDisplay& show() {
  9674. if (is_empty() || !_is_closed) return *this;
  9675. _is_closed = false;
  9676. if (_is_fullscreen) _init_fullscreen();
  9677. ShowWindow(_window,SW_SHOW);
  9678. _update_window_pos();
  9679. return paint();
  9680. }
  9681. CImgDisplay& close() {
  9682. if (is_empty() || _is_closed) return *this;
  9683. _is_closed = true;
  9684. if (_is_fullscreen) _desinit_fullscreen();
  9685. ShowWindow(_window,SW_HIDE);
  9686. _window_x = _window_y = 0;
  9687. return *this;
  9688. }
  9689. CImgDisplay& move(const int posx, const int posy) {
  9690. if (is_empty()) return *this;
  9691. if (_window_x!=posx || _window_y!=posy) {
  9692. if (!_is_fullscreen) {
  9693. RECT rect;
  9694. rect.left = rect.top = 0; rect.right = (LONG)_window_width - 1; rect.bottom = (LONG)_window_height - 1;
  9695. AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
  9696. const int
  9697. border1 = (int)((rect.right - rect.left + 1 -_width)/2),
  9698. border2 = (int)(rect.bottom - rect.top + 1 - _height - border1);
  9699. SetWindowPos(_window,0,posx - border1,posy - border2,0,0,SWP_NOSIZE | SWP_NOZORDER);
  9700. } else SetWindowPos(_window,0,posx,posy,0,0,SWP_NOSIZE | SWP_NOZORDER);
  9701. _window_x = posx;
  9702. _window_y = posy;
  9703. show();
  9704. }
  9705. _is_moved = false;
  9706. return *this;
  9707. }
  9708. CImgDisplay& show_mouse() {
  9709. if (is_empty()) return *this;
  9710. _is_cursor_visible = true;
  9711. return *this;
  9712. }
  9713. CImgDisplay& hide_mouse() {
  9714. if (is_empty()) return *this;
  9715. _is_cursor_visible = false;
  9716. return *this;
  9717. }
  9718. CImgDisplay& set_mouse(const int posx, const int posy) {
  9719. if (is_empty() || _is_closed || posx<0 || posy<0) return *this;
  9720. _update_window_pos();
  9721. const int res = (int)SetCursorPos(_window_x + posx,_window_y + posy);
  9722. if (res) { _mouse_x = posx; _mouse_y = posy; }
  9723. return *this;
  9724. }
  9725. CImgDisplay& set_title(const char *const format, ...) {
  9726. if (is_empty()) return *this;
  9727. char *const tmp = new char[1024];
  9728. va_list ap;
  9729. va_start(ap, format);
  9730. cimg_vsnprintf(tmp,1024,format,ap);
  9731. va_end(ap);
  9732. if (!std::strcmp(_title,tmp)) { delete[] tmp; return *this; }
  9733. delete[] _title;
  9734. const unsigned int s = (unsigned int)std::strlen(tmp) + 1;
  9735. _title = new char[s];
  9736. std::memcpy(_title,tmp,s*sizeof(char));
  9737. SetWindowTextA(_window, tmp);
  9738. delete[] tmp;
  9739. return *this;
  9740. }
  9741. template<typename T>
  9742. CImgDisplay& display(const CImg<T>& img) {
  9743. if (!img)
  9744. throw CImgArgumentException(_cimgdisplay_instance
  9745. "display(): Empty specified image.",
  9746. cimgdisplay_instance);
  9747. if (is_empty()) return assign(img);
  9748. return render(img).paint();
  9749. }
  9750. CImgDisplay& paint() {
  9751. if (_is_closed) return *this;
  9752. WaitForSingleObject(_mutex,INFINITE);
  9753. SetDIBitsToDevice(_hdc,0,0,_width,_height,0,0,0,_height,_data,&_bmi,DIB_RGB_COLORS);
  9754. ReleaseMutex(_mutex);
  9755. return *this;
  9756. }
  9757. template<typename T>
  9758. CImgDisplay& render(const CImg<T>& img) {
  9759. if (!img)
  9760. throw CImgArgumentException(_cimgdisplay_instance
  9761. "render(): Empty specified image.",
  9762. cimgdisplay_instance);
  9763. if (is_empty()) return *this;
  9764. if (img._depth!=1) return render(img.get_projections2d((img._width - 1)/2,(img._height - 1)/2,
  9765. (img._depth - 1)/2));
  9766. const T
  9767. *data1 = img._data,
  9768. *data2 = (img._spectrum>=2)?img.data(0,0,0,1):data1,
  9769. *data3 = (img._spectrum>=3)?img.data(0,0,0,2):data1;
  9770. WaitForSingleObject(_mutex,INFINITE);
  9771. unsigned int
  9772. *const ndata = (img._width==_width && img._height==_height)?_data:
  9773. new unsigned int[(size_t)img._width*img._height],
  9774. *ptrd = ndata;
  9775. if (!_normalization || (_normalization==3 && cimg::type<T>::string()==cimg::type<unsigned char>::string())) {
  9776. _min = _max = 0;
  9777. switch (img._spectrum) {
  9778. case 1 : {
  9779. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  9780. const unsigned char val = (unsigned char)*(data1++);
  9781. *(ptrd++) = (unsigned int)((val<<16) | (val<<8) | val);
  9782. }
  9783. } break;
  9784. case 2 : {
  9785. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  9786. const unsigned char
  9787. R = (unsigned char)*(data1++),
  9788. G = (unsigned char)*(data2++);
  9789. *(ptrd++) = (unsigned int)((R<<16) | (G<<8));
  9790. }
  9791. } break;
  9792. default : {
  9793. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  9794. const unsigned char
  9795. R = (unsigned char)*(data1++),
  9796. G = (unsigned char)*(data2++),
  9797. B = (unsigned char)*(data3++);
  9798. *(ptrd++) = (unsigned int)((R<<16) | (G<<8) | B);
  9799. }
  9800. }
  9801. }
  9802. } else {
  9803. if (_normalization==3) {
  9804. if (cimg::type<T>::is_float()) _min = (float)img.min_max(_max);
  9805. else { _min = (float)cimg::type<T>::min(); _max = (float)cimg::type<T>::max(); }
  9806. } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max);
  9807. const float delta = _max - _min, mm = 255/(delta?delta:1.0f);
  9808. switch (img._spectrum) {
  9809. case 1 : {
  9810. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  9811. const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
  9812. *(ptrd++) = (unsigned int)((val<<16) | (val<<8) | val);
  9813. }
  9814. } break;
  9815. case 2 : {
  9816. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  9817. const unsigned char
  9818. R = (unsigned char)((*(data1++) - _min)*mm),
  9819. G = (unsigned char)((*(data2++) - _min)*mm);
  9820. *(ptrd++) = (unsigned int)((R<<16) | (G<<8));
  9821. }
  9822. } break;
  9823. default : {
  9824. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  9825. const unsigned char
  9826. R = (unsigned char)((*(data1++) - _min)*mm),
  9827. G = (unsigned char)((*(data2++) - _min)*mm),
  9828. B = (unsigned char)((*(data3++) - _min)*mm);
  9829. *(ptrd++) = (unsigned int)((R<<16) | (G<<8) | B);
  9830. }
  9831. }
  9832. }
  9833. }
  9834. if (ndata!=_data) { _render_resize(ndata,img._width,img._height,_data,_width,_height); delete[] ndata; }
  9835. ReleaseMutex(_mutex);
  9836. return *this;
  9837. }
  9838. template<typename T>
  9839. static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg<T>& img) {
  9840. img.assign();
  9841. HDC hScreen = GetDC(GetDesktopWindow());
  9842. if (hScreen) {
  9843. const int
  9844. width = GetDeviceCaps(hScreen,HORZRES),
  9845. height = GetDeviceCaps(hScreen,VERTRES);
  9846. int _x0 = x0, _y0 = y0, _x1 = x1, _y1 = y1;
  9847. if (_x0>_x1) cimg::swap(_x0,_x1);
  9848. if (_y0>_y1) cimg::swap(_y0,_y1);
  9849. if (_x1>=0 && _x0<width && _y1>=0 && _y0<height) {
  9850. _x0 = std::max(_x0,0);
  9851. _y0 = std::max(_y0,0);
  9852. _x1 = std::min(_x1,width - 1);
  9853. _y1 = std::min(_y1,height - 1);
  9854. const int bw = _x1 - _x0 + 1, bh = _y1 - _y0 + 1;
  9855. HDC hdcMem = CreateCompatibleDC(hScreen);
  9856. if (hdcMem) {
  9857. HBITMAP hBitmap = CreateCompatibleBitmap(hScreen,bw,bh);
  9858. if (hBitmap) {
  9859. HGDIOBJ hOld = SelectObject(hdcMem,hBitmap);
  9860. if (hOld && BitBlt(hdcMem,0,0,bw,bh,hScreen,_x0,_y0,SRCCOPY) && SelectObject(hdcMem,hOld)) {
  9861. BITMAPINFOHEADER bmi;
  9862. bmi.biSize = sizeof(BITMAPINFOHEADER);
  9863. bmi.biWidth = bw;
  9864. bmi.biHeight = -bh;
  9865. bmi.biPlanes = 1;
  9866. bmi.biBitCount = 32;
  9867. bmi.biCompression = BI_RGB;
  9868. bmi.biSizeImage = 0;
  9869. bmi.biXPelsPerMeter = bmi.biYPelsPerMeter = 0;
  9870. bmi.biClrUsed = bmi.biClrImportant = 0;
  9871. unsigned char *buf = new unsigned char[4*bw*bh];
  9872. if (GetDIBits(hdcMem,hBitmap,0,bh,buf,(BITMAPINFO*)&bmi,DIB_RGB_COLORS)) {
  9873. img.assign(bw,bh,1,3);
  9874. const unsigned char *ptrs = buf;
  9875. T *pR = img.data(0,0,0,0), *pG = img.data(0,0,0,1), *pB = img.data(0,0,0,2);
  9876. cimg_forXY(img,x,y) {
  9877. *(pR++) = (T)ptrs[2];
  9878. *(pG++) = (T)ptrs[1];
  9879. *(pB++) = (T)ptrs[0];
  9880. ptrs+=4;
  9881. }
  9882. }
  9883. delete[] buf;
  9884. }
  9885. DeleteObject(hBitmap);
  9886. }
  9887. DeleteDC(hdcMem);
  9888. }
  9889. }
  9890. ReleaseDC(GetDesktopWindow(),hScreen);
  9891. }
  9892. if (img.is_empty())
  9893. throw CImgDisplayException("CImgDisplay::screenshot(): Failed to take screenshot "
  9894. "with coordinates (%d,%d)-(%d,%d).",
  9895. x0,y0,x1,y1);
  9896. }
  9897. template<typename T>
  9898. const CImgDisplay& snapshot(CImg<T>& img) const {
  9899. if (is_empty()) { img.assign(); return *this; }
  9900. const unsigned int *ptrs = _data;
  9901. img.assign(_width,_height,1,3);
  9902. T
  9903. *data1 = img.data(0,0,0,0),
  9904. *data2 = img.data(0,0,0,1),
  9905. *data3 = img.data(0,0,0,2);
  9906. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  9907. const unsigned int val = *(ptrs++);
  9908. *(data1++) = (T)(unsigned char)(val>>16);
  9909. *(data2++) = (T)(unsigned char)((val>>8)&0xFF);
  9910. *(data3++) = (T)(unsigned char)(val&0xFF);
  9911. }
  9912. return *this;
  9913. }
  9914. #endif
  9915. //@}
  9916. };
  9917. /*
  9918. #--------------------------------------
  9919. #
  9920. #
  9921. #
  9922. # Definition of the CImg<T> structure
  9923. #
  9924. #
  9925. #
  9926. #--------------------------------------
  9927. */
  9928. //! Class representing an image (up to 4 dimensions wide), each pixel being of type \c T.
  9929. /**
  9930. This is the main class of the %CImg Library. It declares and constructs
  9931. an image, allows access to its pixel values, and is able to perform various image operations.
  9932. \par Image representation
  9933. A %CImg image is defined as an instance of the container \c CImg<T>, which contains a regular grid of pixels,
  9934. each pixel value being of type \c T. The image grid can have up to 4 dimensions: width, height, depth
  9935. and number of channels.
  9936. Usually, the three first dimensions are used to describe spatial coordinates <tt>(x,y,z)</tt>,
  9937. while the number of channels is rather used as a vector-valued dimension
  9938. (it may describe the R,G,B color channels for instance).
  9939. If you need a fifth dimension, you can use image lists \c CImgList<T> rather than simple images \c CImg<T>.
  9940. Thus, the \c CImg<T> class is able to represent volumetric images of vector-valued pixels,
  9941. as well as images with less dimensions (1d scalar signal, 2d color images, ...).
  9942. Most member functions of the class CImg<\c T> are designed to handle this maximum case of (3+1) dimensions.
  9943. Concerning the pixel value type \c T:
  9944. fully supported template types are the basic C++ types: <tt>unsigned char, char, short, unsigned int, int,
  9945. unsigned long, long, float, double, ... </tt>.
  9946. Typically, fast image display can be done using <tt>CImg<unsigned char></tt> images,
  9947. while complex image processing algorithms may be rather coded using <tt>CImg<float></tt> or <tt>CImg<double></tt>
  9948. images that have floating-point pixel values. The default value for the template T is \c float.
  9949. Using your own template types may be possible. However, you will certainly have to define the complete set
  9950. of arithmetic and logical operators for your class.
  9951. \par Image structure
  9952. The \c CImg<T> structure contains \e six fields:
  9953. - \c _width defines the number of \a columns of the image (size along the X-axis).
  9954. - \c _height defines the number of \a rows of the image (size along the Y-axis).
  9955. - \c _depth defines the number of \a slices of the image (size along the Z-axis).
  9956. - \c _spectrum defines the number of \a channels of the image (size along the C-axis).
  9957. - \c _data defines a \a pointer to the \a pixel \a data (of type \c T).
  9958. - \c _is_shared is a boolean that tells if the memory buffer \c data is shared with
  9959. another image.
  9960. You can access these fields publicly although it is recommended to use the dedicated functions
  9961. width(), height(), depth(), spectrum() and ptr() to do so.
  9962. Image dimensions are not limited to a specific range (as long as you got enough available memory).
  9963. A value of \e 1 usually means that the corresponding dimension is \a flat.
  9964. If one of the dimensions is \e 0, or if the data pointer is null, the image is considered as \e empty.
  9965. Empty images should not contain any pixel data and thus, will not be processed by CImg member functions
  9966. (a CImgInstanceException will be thrown instead).
  9967. Pixel data are stored in memory, in a non interlaced mode (See \ref cimg_storage).
  9968. \par Image declaration and construction
  9969. Declaring an image can be done by using one of the several available constructors.
  9970. Here is a list of the most used:
  9971. - Construct images from arbitrary dimensions:
  9972. - <tt>CImg<char> img;</tt> declares an empty image.
  9973. - <tt>CImg<unsigned char> img(128,128);</tt> declares a 128x128 greyscale image with
  9974. \c unsigned \c char pixel values.
  9975. - <tt>CImg<double> img(3,3);</tt> declares a 3x3 matrix with \c double coefficients.
  9976. - <tt>CImg<unsigned char> img(256,256,1,3);</tt> declares a 256x256x1x3 (color) image
  9977. (colors are stored as an image with three channels).
  9978. - <tt>CImg<double> img(128,128,128);</tt> declares a 128x128x128 volumetric and greyscale image
  9979. (with \c double pixel values).
  9980. - <tt>CImg<> img(128,128,128,3);</tt> declares a 128x128x128 volumetric color image
  9981. (with \c float pixels, which is the default value of the template parameter \c T).
  9982. - \b Note: images pixels are <b>not automatically initialized to 0</b>. You may use the function \c fill() to
  9983. do it, or use the specific constructor taking 5 parameters like this:
  9984. <tt>CImg<> img(128,128,128,3,0);</tt> declares a 128x128x128 volumetric color image with all pixel values to 0.
  9985. - Construct images from filenames:
  9986. - <tt>CImg<unsigned char> img("image.jpg");</tt> reads a JPEG color image from the file "image.jpg".
  9987. - <tt>CImg<float> img("analyze.hdr");</tt> reads a volumetric image (ANALYZE7.5 format) from the
  9988. file "analyze.hdr".
  9989. - \b Note: You need to install <a href="http://www.imagemagick.org">ImageMagick</a>
  9990. to be able to read common compressed image formats (JPG,PNG, ...) (See \ref cimg_files_io).
  9991. - Construct images from C-style arrays:
  9992. - <tt>CImg<int> img(data_buffer,256,256);</tt> constructs a 256x256 greyscale image from a \c int* buffer
  9993. \c data_buffer (of size 256x256=65536).
  9994. - <tt>CImg<unsigned char> img(data_buffer,256,256,1,3);</tt> constructs a 256x256 color image
  9995. from a \c unsigned \c char* buffer \c data_buffer (where R,G,B channels follow each others).
  9996. The complete list of constructors can be found <a href="#constructors">here</a>.
  9997. \par Most useful functions
  9998. The \c CImg<T> class contains a lot of functions that operates on images.
  9999. Some of the most useful are:
  10000. - operator()(): Read or write pixel values.
  10001. - display(): displays the image in a new window.
  10002. **/
  10003. template<typename T>
  10004. struct CImg {
  10005. unsigned int _width, _height, _depth, _spectrum;
  10006. bool _is_shared;
  10007. T *_data;
  10008. //! Simple iterator type, to loop through each pixel value of an image instance.
  10009. /**
  10010. \note
  10011. - The \c CImg<T>::iterator type is defined to be a <tt>T*</tt>.
  10012. - You will seldom have to use iterators in %CImg, most classical operations
  10013. being achieved (often in a faster way) using methods of \c CImg<T>.
  10014. \par Example
  10015. \code
  10016. CImg<float> img("reference.jpg"); // Load image from file.
  10017. // Set all pixels to '0', with a CImg iterator.
  10018. for (CImg<float>::iterator it = img.begin(), it<img.end(); ++it) *it = 0;
  10019. img.fill(0); // Do the same with a built-in method.
  10020. \endcode
  10021. **/
  10022. typedef T* iterator;
  10023. //! Simple const iterator type, to loop through each pixel value of a \c const image instance.
  10024. /**
  10025. \note
  10026. - The \c CImg<T>::const_iterator type is defined to be a \c const \c T*.
  10027. - You will seldom have to use iterators in %CImg, most classical operations
  10028. being achieved (often in a faster way) using methods of \c CImg<T>.
  10029. \par Example
  10030. \code
  10031. const CImg<float> img("reference.jpg"); // Load image from file.
  10032. float sum = 0;
  10033. // Compute sum of all pixel values, with a CImg iterator.
  10034. for (CImg<float>::iterator it = img.begin(), it<img.end(); ++it) sum+=*it;
  10035. const float sum2 = img.sum(); // Do the same with a built-in method.
  10036. \endcode
  10037. **/
  10038. typedef const T* const_iterator;
  10039. //! Pixel value type.
  10040. /**
  10041. Refer to the type of the pixel values of an image instance.
  10042. \note
  10043. - The \c CImg<T>::value_type type of a \c CImg<T> is defined to be a \c T.
  10044. - \c CImg<T>::value_type is actually not used in %CImg methods. It has been mainly defined for
  10045. compatibility with STL naming conventions.
  10046. **/
  10047. typedef T value_type;
  10048. // Define common types related to template type T.
  10049. typedef typename cimg::superset<T,bool>::type Tbool;
  10050. typedef typename cimg::superset<T,unsigned char>::type Tuchar;
  10051. typedef typename cimg::superset<T,char>::type Tchar;
  10052. typedef typename cimg::superset<T,unsigned short>::type Tushort;
  10053. typedef typename cimg::superset<T,short>::type Tshort;
  10054. typedef typename cimg::superset<T,unsigned int>::type Tuint;
  10055. typedef typename cimg::superset<T,int>::type Tint;
  10056. typedef typename cimg::superset<T,cimg_ulong>::type Tulong;
  10057. typedef typename cimg::superset<T,cimg_long>::type Tlong;
  10058. typedef typename cimg::superset<T,float>::type Tfloat;
  10059. typedef typename cimg::superset<T,double>::type Tdouble;
  10060. typedef typename cimg::last<T,bool>::type boolT;
  10061. typedef typename cimg::last<T,unsigned char>::type ucharT;
  10062. typedef typename cimg::last<T,char>::type charT;
  10063. typedef typename cimg::last<T,unsigned short>::type ushortT;
  10064. typedef typename cimg::last<T,short>::type shortT;
  10065. typedef typename cimg::last<T,unsigned int>::type uintT;
  10066. typedef typename cimg::last<T,int>::type intT;
  10067. typedef typename cimg::last<T,cimg_ulong>::type ulongT;
  10068. typedef typename cimg::last<T,cimg_long>::type longT;
  10069. typedef typename cimg::last<T,cimg_uint64>::type uint64T;
  10070. typedef typename cimg::last<T,cimg_int64>::type int64T;
  10071. typedef typename cimg::last<T,float>::type floatT;
  10072. typedef typename cimg::last<T,double>::type doubleT;
  10073. //@}
  10074. //---------------------------
  10075. //
  10076. //! \name Plugins
  10077. //@{
  10078. //---------------------------
  10079. #ifdef cimg_plugin
  10080. #include cimg_plugin
  10081. #endif
  10082. #ifdef cimg_plugin1
  10083. #include cimg_plugin1
  10084. #endif
  10085. #ifdef cimg_plugin2
  10086. #include cimg_plugin2
  10087. #endif
  10088. #ifdef cimg_plugin3
  10089. #include cimg_plugin3
  10090. #endif
  10091. #ifdef cimg_plugin4
  10092. #include cimg_plugin4
  10093. #endif
  10094. #ifdef cimg_plugin5
  10095. #include cimg_plugin5
  10096. #endif
  10097. #ifdef cimg_plugin6
  10098. #include cimg_plugin6
  10099. #endif
  10100. #ifdef cimg_plugin7
  10101. #include cimg_plugin7
  10102. #endif
  10103. #ifdef cimg_plugin8
  10104. #include cimg_plugin8
  10105. #endif
  10106. //@}
  10107. //---------------------------------------------------------
  10108. //
  10109. //! \name Constructors / Destructor / Instance Management
  10110. //@{
  10111. //---------------------------------------------------------
  10112. //! Destroy image.
  10113. /**
  10114. \note
  10115. - The pixel buffer data() is deallocated if necessary, e.g. for non-empty and non-shared image instances.
  10116. - Destroying an empty or shared image does nothing actually.
  10117. \warning
  10118. - When destroying a non-shared image, make sure that you will \e not operate on a remaining shared image
  10119. that shares its buffer with the destroyed instance, in order to avoid further invalid memory access
  10120. (to a deallocated buffer).
  10121. **/
  10122. ~CImg() {
  10123. if (!_is_shared) delete[] _data;
  10124. }
  10125. //! Construct empty image.
  10126. /**
  10127. \note
  10128. - An empty image has no pixel data and all of its dimensions width(), height(), depth(), spectrum()
  10129. are set to \c 0, as well as its pixel buffer pointer data().
  10130. - An empty image may be re-assigned afterwards, e.g. with the family of
  10131. assign(unsigned int,unsigned int,unsigned int,unsigned int) methods,
  10132. or by operator=(const CImg<t>&). In all cases, the type of pixels stays \c T.
  10133. - An empty image is never shared.
  10134. \par Example
  10135. \code
  10136. CImg<float> img1, img2; // Construct two empty images.
  10137. img1.assign(256,256,1,3); // Re-assign 'img1' to be a 256x256x1x3 (color) image.
  10138. img2 = img1.get_rand(0,255); // Re-assign 'img2' to be a random-valued version of 'img1'.
  10139. img2.assign(); // Re-assign 'img2' to be an empty image again.
  10140. \endcode
  10141. **/
  10142. CImg():_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {}
  10143. //! Construct image with specified size.
  10144. /**
  10145. \param size_x Image width().
  10146. \param size_y Image height().
  10147. \param size_z Image depth().
  10148. \param size_c Image spectrum() (number of channels).
  10149. \note
  10150. - It is able to create only \e non-shared images, and allocates thus a pixel buffer data()
  10151. for each constructed image instance.
  10152. - Setting one dimension \c size_x,\c size_y,\c size_z or \c size_c to \c 0 leads to the construction of
  10153. an \e empty image.
  10154. - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated
  10155. (e.g. when requested size is too big for available memory).
  10156. \warning
  10157. - The allocated pixel buffer is \e not filled with a default value, and is likely to contain garbage values.
  10158. In order to initialize pixel values during construction (e.g. with \c 0), use constructor
  10159. CImg(unsigned int,unsigned int,unsigned int,unsigned int,T) instead.
  10160. \par Example
  10161. \code
  10162. CImg<float> img1(256,256,1,3); // Construct a 256x256x1x3 (color) image, filled with garbage values.
  10163. CImg<float> img2(256,256,1,3,0); // Construct a 256x256x1x3 (color) image, filled with value '0'.
  10164. \endcode
  10165. **/
  10166. explicit CImg(const unsigned int size_x, const unsigned int size_y=1,
  10167. const unsigned int size_z=1, const unsigned int size_c=1):
  10168. _is_shared(false) {
  10169. size_t siz = (size_t)size_x*size_y*size_z*size_c;
  10170. if (siz) {
  10171. _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
  10172. try { _data = new T[siz]; } catch (...) {
  10173. _width = _height = _depth = _spectrum = 0; _data = 0;
  10174. throw CImgInstanceException(_cimg_instance
  10175. "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
  10176. cimg_instance,
  10177. cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
  10178. size_x,size_y,size_z,size_c);
  10179. }
  10180. } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
  10181. }
  10182. //! Construct image with specified size and initialize pixel values.
  10183. /**
  10184. \param size_x Image width().
  10185. \param size_y Image height().
  10186. \param size_z Image depth().
  10187. \param size_c Image spectrum() (number of channels).
  10188. \param value Initialization value.
  10189. \note
  10190. - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int),
  10191. but it also fills the pixel buffer with the specified \c value.
  10192. \warning
  10193. - It cannot be used to construct a vector-valued image and initialize it with \e vector-valued pixels
  10194. (e.g. RGB vector, for color images).
  10195. For this task, you may use fillC() after construction.
  10196. **/
  10197. CImg(const unsigned int size_x, const unsigned int size_y,
  10198. const unsigned int size_z, const unsigned int size_c, const T& value):
  10199. _is_shared(false) {
  10200. const size_t siz = (size_t)size_x*size_y*size_z*size_c;
  10201. if (siz) {
  10202. _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
  10203. try { _data = new T[siz]; } catch (...) {
  10204. _width = _height = _depth = _spectrum = 0; _data = 0;
  10205. throw CImgInstanceException(_cimg_instance
  10206. "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
  10207. cimg_instance,
  10208. cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
  10209. size_x,size_y,size_z,size_c);
  10210. }
  10211. fill(value);
  10212. } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
  10213. }
  10214. //! Construct image with specified size and initialize pixel values from a sequence of integers.
  10215. /**
  10216. Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c,
  10217. with pixels of type \c T, and initialize pixel
  10218. values from the specified sequence of integers \c value0,\c value1,\c ...
  10219. \param size_x Image width().
  10220. \param size_y Image height().
  10221. \param size_z Image depth().
  10222. \param size_c Image spectrum() (number of channels).
  10223. \param value0 First value of the initialization sequence (must be an \e integer).
  10224. \param value1 Second value of the initialization sequence (must be an \e integer).
  10225. \param ...
  10226. \note
  10227. - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills
  10228. the pixel buffer with a sequence of specified integer values.
  10229. \warning
  10230. - You must specify \e exactly \c size_x*\c size_y*\c size_z*\c size_c integers in the initialization sequence.
  10231. Otherwise, the constructor may crash or fill your image pixels with garbage.
  10232. \par Example
  10233. \code
  10234. const CImg<float> img(2,2,1,3, // Construct a 2x2 color (RGB) image.
  10235. 0,255,0,255, // Set the 4 values for the red component.
  10236. 0,0,255,255, // Set the 4 values for the green component.
  10237. 64,64,64,64); // Set the 4 values for the blue component.
  10238. img.resize(150,150).display();
  10239. \endcode
  10240. \image html ref_constructor1.jpg
  10241. **/
  10242. CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
  10243. const int value0, const int value1, ...):
  10244. _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
  10245. #define _CImg_stdarg(img,a0,a1,N,t) { \
  10246. size_t _siz = (size_t)N; \
  10247. if (_siz--) { \
  10248. va_list ap; \
  10249. va_start(ap,a1); \
  10250. T *ptrd = (img)._data; \
  10251. *(ptrd++) = (T)a0; \
  10252. if (_siz--) { \
  10253. *(ptrd++) = (T)a1; \
  10254. for ( ; _siz; --_siz) *(ptrd++) = (T)va_arg(ap,t); \
  10255. } \
  10256. va_end(ap); \
  10257. } \
  10258. }
  10259. assign(size_x,size_y,size_z,size_c);
  10260. _CImg_stdarg(*this,value0,value1,(size_t)size_x*size_y*size_z*size_c,int);
  10261. }
  10262. #if cimg_use_cpp11==1
  10263. //! Construct image with specified size and initialize pixel values from an initializer list of integers.
  10264. /**
  10265. Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c,
  10266. with pixels of type \c T, and initialize pixel
  10267. values from the specified initializer list of integers { \c value0,\c value1,\c ... }
  10268. \param size_x Image width().
  10269. \param size_y Image height().
  10270. \param size_z Image depth().
  10271. \param size_c Image spectrum() (number of channels).
  10272. \param { value0, value1, ... } Initialization list
  10273. \param repeat_values Tells if the value filling process is repeated over the image.
  10274. \note
  10275. - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills
  10276. the pixel buffer with a sequence of specified integer values.
  10277. \par Example
  10278. \code
  10279. const CImg<float> img(2,2,1,3, // Construct a 2x2 color (RGB) image.
  10280. { 0,255,0,255, // Set the 4 values for the red component.
  10281. 0,0,255,255, // Set the 4 values for the green component.
  10282. 64,64,64,64 }); // Set the 4 values for the blue component.
  10283. img.resize(150,150).display();
  10284. \endcode
  10285. \image html ref_constructor1.jpg
  10286. **/
  10287. template<typename t>
  10288. CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
  10289. const std::initializer_list<t> values,
  10290. const bool repeat_values=true):
  10291. _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
  10292. #define _cimg_constructor_cpp11(repeat_values) \
  10293. auto it = values.begin(); \
  10294. size_t siz = size(); \
  10295. if (repeat_values) for (T *ptrd = _data; siz--; ) { \
  10296. *(ptrd++) = (T)(*(it++)); if (it==values.end()) it = values.begin(); } \
  10297. else { siz = std::min(siz,values.size()); for (T *ptrd = _data; siz--; ) *(ptrd++) = (T)(*(it++)); }
  10298. assign(size_x,size_y,size_z,size_c);
  10299. _cimg_constructor_cpp11(repeat_values);
  10300. }
  10301. template<typename t>
  10302. CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z,
  10303. std::initializer_list<t> values,
  10304. const bool repeat_values=true):
  10305. _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
  10306. assign(size_x,size_y,size_z);
  10307. _cimg_constructor_cpp11(repeat_values);
  10308. }
  10309. template<typename t>
  10310. CImg(const unsigned int size_x, const unsigned int size_y,
  10311. std::initializer_list<t> values,
  10312. const bool repeat_values=true):
  10313. _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
  10314. assign(size_x,size_y);
  10315. _cimg_constructor_cpp11(repeat_values);
  10316. }
  10317. template<typename t>
  10318. CImg(const unsigned int size_x,
  10319. std::initializer_list<t> values,
  10320. const bool repeat_values=true):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
  10321. assign(size_x);
  10322. _cimg_constructor_cpp11(repeat_values);
  10323. }
  10324. //! Construct single channel 1D image with pixel values and width obtained from an initializer list of integers.
  10325. /**
  10326. Construct a new image instance of size \c width x \c 1 x \c 1 x \c 1,
  10327. with pixels of type \c T, and initialize pixel
  10328. values from the specified initializer list of integers { \c value0,\c value1,\c ... }. Image width is
  10329. given by the size of the initializer list.
  10330. \param { value0, value1, ... } Initialization list
  10331. \note
  10332. - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int) with height=1, depth=1, and spectrum=1,
  10333. but it also fills the pixel buffer with a sequence of specified integer values.
  10334. \par Example
  10335. \code
  10336. const CImg<float> img = {10,20,30,20,10 }; // Construct a 5x1 image with one channel, and set its pixel values.
  10337. img.resize(150,150).display();
  10338. \endcode
  10339. \image html ref_constructor1.jpg
  10340. **/
  10341. template<typename t>
  10342. CImg(const std::initializer_list<t> values):
  10343. _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
  10344. assign(values.size(),1,1,1);
  10345. auto it = values.begin();
  10346. unsigned int siz = _width;
  10347. for (T *ptrd = _data; siz--; ) *(ptrd++) = (T)(*(it++));
  10348. }
  10349. template<typename t>
  10350. CImg<T> & operator=(std::initializer_list<t> values) {
  10351. _cimg_constructor_cpp11(siz>values.size());
  10352. return *this;
  10353. }
  10354. #endif
  10355. //! Construct image with specified size and initialize pixel values from a sequence of doubles.
  10356. /**
  10357. 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,
  10358. and initialize pixel values from the specified sequence of doubles \c value0,\c value1,\c ...
  10359. \param size_x Image width().
  10360. \param size_y Image height().
  10361. \param size_z Image depth().
  10362. \param size_c Image spectrum() (number of channels).
  10363. \param value0 First value of the initialization sequence (must be a \e double).
  10364. \param value1 Second value of the initialization sequence (must be a \e double).
  10365. \param ...
  10366. \note
  10367. - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...), but
  10368. takes a sequence of double values instead of integers.
  10369. \warning
  10370. - You must specify \e exactly \c dx*\c dy*\c dz*\c dc doubles in the initialization sequence.
  10371. Otherwise, the constructor may crash or fill your image with garbage.
  10372. For instance, the code below will probably crash on most platforms:
  10373. \code
  10374. const CImg<float> img(2,2,1,1, 0.5,0.5,255,255); // FAIL: The two last arguments are 'int', not 'double'!
  10375. \endcode
  10376. **/
  10377. CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
  10378. const double value0, const double value1, ...):
  10379. _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
  10380. assign(size_x,size_y,size_z,size_c);
  10381. _CImg_stdarg(*this,value0,value1,(size_t)size_x*size_y*size_z*size_c,double);
  10382. }
  10383. //! Construct image with specified size and initialize pixel values from a value string.
  10384. /**
  10385. 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,
  10386. and initializes pixel values from the specified string \c values.
  10387. \param size_x Image width().
  10388. \param size_y Image height().
  10389. \param size_z Image depth().
  10390. \param size_c Image spectrum() (number of channels).
  10391. \param values Value string describing the way pixel values are set.
  10392. \param repeat_values Tells if the value filling process is repeated over the image.
  10393. \note
  10394. - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills
  10395. the pixel buffer with values described in the value string \c values.
  10396. - Value string \c values may describe two different filling processes:
  10397. - Either \c values is a sequences of values assigned to the image pixels, as in <tt>"1,2,3,7,8,2"</tt>.
  10398. In this case, set \c repeat_values to \c true to periodically fill the image with the value sequence.
  10399. - Either, \c values is a formula, as in <tt>"cos(x/10)*sin(y/20)"</tt>.
  10400. In this case, parameter \c repeat_values is pointless.
  10401. - For both cases, specifying \c repeat_values is mandatory.
  10402. It disambiguates the possible overloading of constructor
  10403. CImg(unsigned int,unsigned int,unsigned int,unsigned int,T) with \c T being a <tt>const char*</tt>.
  10404. - A \c CImgArgumentException is thrown when an invalid value string \c values is specified.
  10405. \par Example
  10406. \code
  10407. const CImg<float> img1(129,129,1,3,"0,64,128,192,255",true), // Construct image from a value sequence.
  10408. img2(129,129,1,3,"if(c==0,255*abs(cos(x/10)),1.8*y)",false); // Construct image from a formula.
  10409. (img1,img2).display();
  10410. \endcode
  10411. \image html ref_constructor2.jpg
  10412. **/
  10413. CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
  10414. const char *const values, const bool repeat_values):_is_shared(false) {
  10415. const size_t siz = (size_t)size_x*size_y*size_z*size_c;
  10416. if (siz) {
  10417. _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
  10418. try { _data = new T[siz]; } catch (...) {
  10419. _width = _height = _depth = _spectrum = 0; _data = 0;
  10420. throw CImgInstanceException(_cimg_instance
  10421. "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
  10422. cimg_instance,
  10423. cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
  10424. size_x,size_y,size_z,size_c);
  10425. }
  10426. fill(values,repeat_values);
  10427. } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
  10428. }
  10429. //! Construct image with specified size and initialize pixel values from a memory buffer.
  10430. /**
  10431. 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,
  10432. and initializes pixel values from the specified \c t* memory buffer.
  10433. \param values Pointer to the input memory buffer.
  10434. \param size_x Image width().
  10435. \param size_y Image height().
  10436. \param size_z Image depth().
  10437. \param size_c Image spectrum() (number of channels).
  10438. \param is_shared Tells if input memory buffer must be shared by the current instance.
  10439. \note
  10440. - If \c is_shared is \c false, the image instance allocates its own pixel buffer,
  10441. and values from the specified input buffer are copied to the instance buffer.
  10442. If buffer types \c T and \c t are different, a regular static cast is performed during buffer copy.
  10443. - Otherwise, the image instance does \e not allocate a new buffer, and uses the input memory buffer as its
  10444. own pixel buffer. This case requires that types \c T and \c t are the same. Later, destroying such a shared
  10445. image will not deallocate the pixel buffer, this task being obviously charged to the initial buffer allocator.
  10446. - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated
  10447. (e.g. when requested size is too big for available memory).
  10448. \warning
  10449. - You must take care when operating on a shared image, since it may have an invalid pixel buffer pointer data()
  10450. (e.g. already deallocated).
  10451. \par Example
  10452. \code
  10453. unsigned char tab[256*256] = { 0 };
  10454. CImg<unsigned char> img1(tab,256,256,1,1,false), // Construct new non-shared image from buffer 'tab'.
  10455. img2(tab,256,256,1,1,true); // Construct new shared-image from buffer 'tab'.
  10456. tab[1024] = 255; // Here, 'img2' is indirectly modified, but not 'img1'.
  10457. \endcode
  10458. **/
  10459. template<typename t>
  10460. CImg(const t *const values, const unsigned int size_x, const unsigned int size_y=1,
  10461. const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false):_is_shared(false) {
  10462. if (is_shared) {
  10463. _width = _height = _depth = _spectrum = 0; _data = 0;
  10464. throw CImgArgumentException(_cimg_instance
  10465. "CImg(): Invalid construction request of a (%u,%u,%u,%u) shared instance "
  10466. "from a (%s*) buffer (pixel types are different).",
  10467. cimg_instance,
  10468. size_x,size_y,size_z,size_c,CImg<t>::pixel_type());
  10469. }
  10470. const size_t siz = (size_t)size_x*size_y*size_z*size_c;
  10471. if (values && siz) {
  10472. _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
  10473. try { _data = new T[siz]; } catch (...) {
  10474. _width = _height = _depth = _spectrum = 0; _data = 0;
  10475. throw CImgInstanceException(_cimg_instance
  10476. "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
  10477. cimg_instance,
  10478. cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
  10479. size_x,size_y,size_z,size_c);
  10480. }
  10481. const t *ptrs = values; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
  10482. } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
  10483. }
  10484. //! Construct image with specified size and initialize pixel values from a memory buffer \specialization.
  10485. CImg(const T *const values, const unsigned int size_x, const unsigned int size_y=1,
  10486. const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false) {
  10487. const size_t siz = (size_t)size_x*size_y*size_z*size_c;
  10488. if (values && siz) {
  10489. _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = is_shared;
  10490. if (_is_shared) _data = const_cast<T*>(values);
  10491. else {
  10492. try { _data = new T[siz]; } catch (...) {
  10493. _width = _height = _depth = _spectrum = 0; _data = 0;
  10494. throw CImgInstanceException(_cimg_instance
  10495. "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
  10496. cimg_instance,
  10497. cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
  10498. size_x,size_y,size_z,size_c);
  10499. }
  10500. std::memcpy(_data,values,siz*sizeof(T));
  10501. }
  10502. } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
  10503. }
  10504. //! Construct image from reading an image file.
  10505. /**
  10506. Construct a new image instance with pixels of type \c T, and initialize pixel values with the data read from
  10507. an image file.
  10508. \param filename Filename, as a C-string.
  10509. \note
  10510. - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it reads the image
  10511. dimensions and pixel values from the specified image file.
  10512. - The recognition of the image file format by %CImg higly depends on the tools installed on your system
  10513. and on the external libraries you used to link your code against.
  10514. - Considered pixel type \c T should better fit the file format specification, or data loss may occur during
  10515. file load (e.g. constructing a \c CImg<unsigned char> from a float-valued image file).
  10516. - A \c CImgIOException is thrown when the specified \c filename cannot be read, or if the file format is not
  10517. recognized.
  10518. \par Example
  10519. \code
  10520. const CImg<float> img("reference.jpg");
  10521. img.display();
  10522. \endcode
  10523. \image html ref_image.jpg
  10524. **/
  10525. explicit CImg(const char *const filename):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
  10526. assign(filename);
  10527. }
  10528. //! Construct image copy.
  10529. /**
  10530. Construct a new image instance with pixels of type \c T, as a copy of an existing \c CImg<t> instance.
  10531. \param img Input image to copy.
  10532. \note
  10533. - Constructed copy has the same size width() x height() x depth() x spectrum() and pixel values as the
  10534. input image \c img.
  10535. - If input image \c img is \e shared and if types \c T and \c t are the same, the constructed copy is also
  10536. \e shared, and shares its pixel buffer with \c img.
  10537. Modifying a pixel value in the constructed copy will thus also modifies it in the input image \c img.
  10538. This behavior is needful to allow functions to return shared images.
  10539. - Otherwise, the constructed copy allocates its own pixel buffer, and copies pixel values from the input
  10540. image \c img into its buffer. The copied pixel values may be eventually statically casted if types \c T and
  10541. \c t are different.
  10542. - Constructing a copy from an image \c img when types \c t and \c T are the same is significantly faster than
  10543. with different types.
  10544. - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated
  10545. (e.g. not enough available memory).
  10546. **/
  10547. template<typename t>
  10548. CImg(const CImg<t>& img):_is_shared(false) {
  10549. const size_t siz = (size_t)img.size();
  10550. if (img._data && siz) {
  10551. _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
  10552. try { _data = new T[siz]; } catch (...) {
  10553. _width = _height = _depth = _spectrum = 0; _data = 0;
  10554. throw CImgInstanceException(_cimg_instance
  10555. "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
  10556. cimg_instance,
  10557. cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
  10558. img._width,img._height,img._depth,img._spectrum);
  10559. }
  10560. const t *ptrs = img._data; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
  10561. } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
  10562. }
  10563. //! Construct image copy \specialization.
  10564. CImg(const CImg<T>& img) {
  10565. const size_t siz = (size_t)img.size();
  10566. if (img._data && siz) {
  10567. _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
  10568. _is_shared = img._is_shared;
  10569. if (_is_shared) _data = const_cast<T*>(img._data);
  10570. else {
  10571. try { _data = new T[siz]; } catch (...) {
  10572. _width = _height = _depth = _spectrum = 0; _data = 0;
  10573. throw CImgInstanceException(_cimg_instance
  10574. "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
  10575. cimg_instance,
  10576. cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
  10577. img._width,img._height,img._depth,img._spectrum);
  10578. }
  10579. std::memcpy(_data,img._data,siz*sizeof(T));
  10580. }
  10581. } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
  10582. }
  10583. //! Advanced copy constructor.
  10584. /**
  10585. Construct a new image instance with pixels of type \c T, as a copy of an existing \c CImg<t> instance,
  10586. while forcing the shared state of the constructed copy.
  10587. \param img Input image to copy.
  10588. \param is_shared Tells about the shared state of the constructed copy.
  10589. \note
  10590. - Similar to CImg(const CImg<t>&), except that it allows to decide the shared state of
  10591. the constructed image, which does not depend anymore on the shared state of the input image \c img:
  10592. - If \c is_shared is \c true, the constructed copy will share its pixel buffer with the input image \c img.
  10593. For that case, the pixel types \c T and \c t \e must be the same.
  10594. - If \c is_shared is \c false, the constructed copy will allocate its own pixel buffer, whether the input
  10595. image \c img is shared or not.
  10596. - A \c CImgArgumentException is thrown when a shared copy is requested with different pixel types \c T and \c t.
  10597. **/
  10598. template<typename t>
  10599. CImg(const CImg<t>& img, const bool is_shared):_is_shared(false) {
  10600. if (is_shared) {
  10601. _width = _height = _depth = _spectrum = 0; _data = 0;
  10602. throw CImgArgumentException(_cimg_instance
  10603. "CImg(): Invalid construction request of a shared instance from a "
  10604. "CImg<%s> image (%u,%u,%u,%u,%p) (pixel types are different).",
  10605. cimg_instance,
  10606. CImg<t>::pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data);
  10607. }
  10608. const size_t siz = (size_t)img.size();
  10609. if (img._data && siz) {
  10610. _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
  10611. try { _data = new T[siz]; } catch (...) {
  10612. _width = _height = _depth = _spectrum = 0; _data = 0;
  10613. throw CImgInstanceException(_cimg_instance
  10614. "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
  10615. cimg_instance,
  10616. cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
  10617. img._width,img._height,img._depth,img._spectrum);
  10618. }
  10619. const t *ptrs = img._data; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
  10620. } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
  10621. }
  10622. //! Advanced copy constructor \specialization.
  10623. CImg(const CImg<T>& img, const bool is_shared) {
  10624. const size_t siz = (size_t)img.size();
  10625. if (img._data && siz) {
  10626. _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
  10627. _is_shared = is_shared;
  10628. if (_is_shared) _data = const_cast<T*>(img._data);
  10629. else {
  10630. try { _data = new T[siz]; } catch (...) {
  10631. _width = _height = _depth = _spectrum = 0; _data = 0;
  10632. throw CImgInstanceException(_cimg_instance
  10633. "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
  10634. cimg_instance,
  10635. cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
  10636. img._width,img._height,img._depth,img._spectrum);
  10637. }
  10638. std::memcpy(_data,img._data,siz*sizeof(T));
  10639. }
  10640. } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
  10641. }
  10642. //! Construct image with dimensions borrowed from another image.
  10643. /**
  10644. Construct a new image instance with pixels of type \c T, and size get from some dimensions of an existing
  10645. \c CImg<t> instance.
  10646. \param img Input image from which dimensions are borrowed.
  10647. \param dimensions C-string describing the image size along the X,Y,Z and C-dimensions.
  10648. \note
  10649. - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it takes the image dimensions
  10650. (\e not its pixel values) from an existing \c CImg<t> instance.
  10651. - The allocated pixel buffer is \e not filled with a default value, and is likely to contain garbage values.
  10652. In order to initialize pixel values (e.g. with \c 0), use constructor CImg(const CImg<t>&,const char*,T)
  10653. instead.
  10654. \par Example
  10655. \code
  10656. const CImg<float> img1(256,128,1,3), // 'img1' is a 256x128x1x3 image.
  10657. img2(img1,"xyzc"), // 'img2' is a 256x128x1x3 image.
  10658. img3(img1,"y,x,z,c"), // 'img3' is a 128x256x1x3 image.
  10659. img4(img1,"c,x,y,3",0), // 'img4' is a 3x128x256x3 image (with pixels initialized to '0').
  10660. \endcode
  10661. **/
  10662. template<typename t>
  10663. CImg(const CImg<t>& img, const char *const dimensions):
  10664. _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
  10665. assign(img,dimensions);
  10666. }
  10667. //! Construct image with dimensions borrowed from another image and initialize pixel values.
  10668. /**
  10669. Construct a new image instance with pixels of type \c T, and size get from the dimensions of an existing
  10670. \c CImg<t> instance, and set all pixel values to specified \c value.
  10671. \param img Input image from which dimensions are borrowed.
  10672. \param dimensions String describing the image size along the X,Y,Z and V-dimensions.
  10673. \param value Value used for initialization.
  10674. \note
  10675. - Similar to CImg(const CImg<t>&,const char*), but it also fills the pixel buffer with the specified \c value.
  10676. **/
  10677. template<typename t>
  10678. CImg(const CImg<t>& img, const char *const dimensions, const T& value):
  10679. _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
  10680. assign(img,dimensions).fill(value);
  10681. }
  10682. //! Construct image from a display window.
  10683. /**
  10684. Construct a new image instance with pixels of type \c T, as a snapshot of an existing \c CImgDisplay instance.
  10685. \param disp Input display window.
  10686. \note
  10687. - The width() and height() of the constructed image instance are the same as the specified \c CImgDisplay.
  10688. - The depth() and spectrum() of the constructed image instance are respectively set to \c 1 and \c 3
  10689. (i.e. a 2d color image).
  10690. - The image pixels are read as 8-bits RGB values.
  10691. **/
  10692. explicit CImg(const CImgDisplay &disp):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
  10693. disp.snapshot(*this);
  10694. }
  10695. // Constructor and assignment operator for rvalue references (c++11).
  10696. // This avoids an additional image copy for methods returning new images. Can save RAM for big images !
  10697. #if cimg_use_cpp11==1
  10698. CImg(CImg<T>&& img):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
  10699. swap(img);
  10700. }
  10701. CImg<T>& operator=(CImg<T>&& img) {
  10702. if (_is_shared) return assign(img);
  10703. return img.swap(*this);
  10704. }
  10705. #endif
  10706. //! Construct empty image \inplace.
  10707. /**
  10708. In-place version of the default constructor CImg(). It simply resets the instance to an empty image.
  10709. **/
  10710. CImg<T>& assign() {
  10711. if (!_is_shared) delete[] _data;
  10712. _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0;
  10713. return *this;
  10714. }
  10715. //! Construct image with specified size \inplace.
  10716. /**
  10717. In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int).
  10718. **/
  10719. CImg<T>& assign(const unsigned int size_x, const unsigned int size_y=1,
  10720. const unsigned int size_z=1, const unsigned int size_c=1) {
  10721. const size_t siz = (size_t)size_x*size_y*size_z*size_c;
  10722. if (!siz) return assign();
  10723. const size_t curr_siz = (size_t)size();
  10724. if (siz!=curr_siz) {
  10725. if (_is_shared)
  10726. throw CImgArgumentException(_cimg_instance
  10727. "assign(): Invalid assignement request of shared instance from specified "
  10728. "image (%u,%u,%u,%u).",
  10729. cimg_instance,
  10730. size_x,size_y,size_z,size_c);
  10731. else {
  10732. delete[] _data;
  10733. try { _data = new T[siz]; } catch (...) {
  10734. _width = _height = _depth = _spectrum = 0; _data = 0;
  10735. throw CImgInstanceException(_cimg_instance
  10736. "assign(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
  10737. cimg_instance,
  10738. cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
  10739. size_x,size_y,size_z,size_c);
  10740. }
  10741. }
  10742. }
  10743. _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
  10744. return *this;
  10745. }
  10746. //! Construct image with specified size and initialize pixel values \inplace.
  10747. /**
  10748. In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,T).
  10749. **/
  10750. CImg<T>& assign(const unsigned int size_x, const unsigned int size_y,
  10751. const unsigned int size_z, const unsigned int size_c, const T& value) {
  10752. return assign(size_x,size_y,size_z,size_c).fill(value);
  10753. }
  10754. //! Construct image with specified size and initialize pixel values from a sequence of integers \inplace.
  10755. /**
  10756. In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...).
  10757. **/
  10758. CImg<T>& assign(const unsigned int size_x, const unsigned int size_y,
  10759. const unsigned int size_z, const unsigned int size_c,
  10760. const int value0, const int value1, ...) {
  10761. assign(size_x,size_y,size_z,size_c);
  10762. _CImg_stdarg(*this,value0,value1,(size_t)size_x*size_y*size_z*size_c,int);
  10763. return *this;
  10764. }
  10765. //! Construct image with specified size and initialize pixel values from a sequence of doubles \inplace.
  10766. /**
  10767. In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,double,double,...).
  10768. **/
  10769. CImg<T>& assign(const unsigned int size_x, const unsigned int size_y,
  10770. const unsigned int size_z, const unsigned int size_c,
  10771. const double value0, const double value1, ...) {
  10772. assign(size_x,size_y,size_z,size_c);
  10773. _CImg_stdarg(*this,value0,value1,(size_t)size_x*size_y*size_z*size_c,double);
  10774. return *this;
  10775. }
  10776. //! Construct image with specified size and initialize pixel values from a value string \inplace.
  10777. /**
  10778. In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,const char*,bool).
  10779. **/
  10780. CImg<T>& assign(const unsigned int size_x, const unsigned int size_y,
  10781. const unsigned int size_z, const unsigned int size_c,
  10782. const char *const values, const bool repeat_values) {
  10783. return assign(size_x,size_y,size_z,size_c).fill(values,repeat_values);
  10784. }
  10785. //! Construct image with specified size and initialize pixel values from a memory buffer \inplace.
  10786. /**
  10787. In-place version of the constructor CImg(const t*,unsigned int,unsigned int,unsigned int,unsigned int).
  10788. **/
  10789. template<typename t>
  10790. CImg<T>& assign(const t *const values, const unsigned int size_x, const unsigned int size_y=1,
  10791. const unsigned int size_z=1, const unsigned int size_c=1) {
  10792. const size_t siz = (size_t)size_x*size_y*size_z*size_c;
  10793. if (!values || !siz) return assign();
  10794. assign(size_x,size_y,size_z,size_c);
  10795. const t *ptrs = values; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
  10796. return *this;
  10797. }
  10798. //! Construct image with specified size and initialize pixel values from a memory buffer \specialization.
  10799. CImg<T>& assign(const T *const values, const unsigned int size_x, const unsigned int size_y=1,
  10800. const unsigned int size_z=1, const unsigned int size_c=1) {
  10801. const size_t siz = (size_t)size_x*size_y*size_z*size_c;
  10802. if (!values || !siz) return assign();
  10803. const size_t curr_siz = (size_t)size();
  10804. if (values==_data && siz==curr_siz) return assign(size_x,size_y,size_z,size_c);
  10805. if (_is_shared || values + siz<_data || values>=_data + size()) {
  10806. assign(size_x,size_y,size_z,size_c);
  10807. if (_is_shared) std::memmove((void*)_data,(void*)values,siz*sizeof(T));
  10808. else std::memcpy((void*)_data,(void*)values,siz*sizeof(T));
  10809. } else {
  10810. T *new_data = 0;
  10811. try { new_data = new T[siz]; } catch (...) {
  10812. _width = _height = _depth = _spectrum = 0; _data = 0;
  10813. throw CImgInstanceException(_cimg_instance
  10814. "assign(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
  10815. cimg_instance,
  10816. cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
  10817. size_x,size_y,size_z,size_c);
  10818. }
  10819. std::memcpy((void*)new_data,(void*)values,siz*sizeof(T));
  10820. delete[] _data; _data = new_data; _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
  10821. }
  10822. return *this;
  10823. }
  10824. //! Construct image with specified size and initialize pixel values from a memory buffer \overloading.
  10825. template<typename t>
  10826. CImg<T>& assign(const t *const values, const unsigned int size_x, const unsigned int size_y,
  10827. const unsigned int size_z, const unsigned int size_c, const bool is_shared) {
  10828. if (is_shared)
  10829. throw CImgArgumentException(_cimg_instance
  10830. "assign(): Invalid assignment request of shared instance from (%s*) buffer"
  10831. "(pixel types are different).",
  10832. cimg_instance,
  10833. CImg<t>::pixel_type());
  10834. return assign(values,size_x,size_y,size_z,size_c);
  10835. }
  10836. //! Construct image with specified size and initialize pixel values from a memory buffer \overloading.
  10837. CImg<T>& assign(const T *const values, const unsigned int size_x, const unsigned int size_y,
  10838. const unsigned int size_z, const unsigned int size_c, const bool is_shared) {
  10839. const size_t siz = (size_t)size_x*size_y*size_z*size_c;
  10840. if (!values || !siz) return assign();
  10841. if (!is_shared) { if (_is_shared) assign(); assign(values,size_x,size_y,size_z,size_c); }
  10842. else {
  10843. if (!_is_shared) {
  10844. if (values + siz<_data || values>=_data + size()) assign();
  10845. else cimg::warn(_cimg_instance
  10846. "assign(): Shared image instance has overlapping memory.",
  10847. cimg_instance);
  10848. }
  10849. _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = true;
  10850. _data = const_cast<T*>(values);
  10851. }
  10852. return *this;
  10853. }
  10854. //! Construct image from reading an image file \inplace.
  10855. /**
  10856. In-place version of the constructor CImg(const char*).
  10857. **/
  10858. CImg<T>& assign(const char *const filename) {
  10859. return load(filename);
  10860. }
  10861. //! Construct image copy \inplace.
  10862. /**
  10863. In-place version of the constructor CImg(const CImg<t>&).
  10864. **/
  10865. template<typename t>
  10866. CImg<T>& assign(const CImg<t>& img) {
  10867. return assign(img._data,img._width,img._height,img._depth,img._spectrum);
  10868. }
  10869. //! In-place version of the advanced copy constructor.
  10870. /**
  10871. In-place version of the constructor CImg(const CImg<t>&,bool).
  10872. **/
  10873. template<typename t>
  10874. CImg<T>& assign(const CImg<t>& img, const bool is_shared) {
  10875. return assign(img._data,img._width,img._height,img._depth,img._spectrum,is_shared);
  10876. }
  10877. //! Construct image with dimensions borrowed from another image \inplace.
  10878. /**
  10879. In-place version of the constructor CImg(const CImg<t>&,const char*).
  10880. **/
  10881. template<typename t>
  10882. CImg<T>& assign(const CImg<t>& img, const char *const dimensions) {
  10883. if (!dimensions || !*dimensions) return assign(img._width,img._height,img._depth,img._spectrum);
  10884. unsigned int siz[4] = { 0,1,1,1 }, k = 0;
  10885. CImg<charT> item(256);
  10886. for (const char *s = dimensions; *s && k<4; ++k) {
  10887. if (cimg_sscanf(s,"%255[^0-9%xyzvwhdcXYZVWHDC]",item._data)>0) s+=std::strlen(item);
  10888. if (*s) {
  10889. unsigned int val = 0; char sep = 0;
  10890. if (cimg_sscanf(s,"%u%c",&val,&sep)>0) {
  10891. if (sep=='%') siz[k] = val*(k==0?_width:k==1?_height:k==2?_depth:_spectrum)/100;
  10892. else siz[k] = val;
  10893. while (*s>='0' && *s<='9') ++s;
  10894. if (sep=='%') ++s;
  10895. } else switch (cimg::lowercase(*s)) {
  10896. case 'x' : case 'w' : siz[k] = img._width; ++s; break;
  10897. case 'y' : case 'h' : siz[k] = img._height; ++s; break;
  10898. case 'z' : case 'd' : siz[k] = img._depth; ++s; break;
  10899. case 'c' : case 's' : siz[k] = img._spectrum; ++s; break;
  10900. default :
  10901. throw CImgArgumentException(_cimg_instance
  10902. "assign(): Invalid character '%c' detected in specified dimension string '%s'.",
  10903. cimg_instance,
  10904. *s,dimensions);
  10905. }
  10906. }
  10907. }
  10908. return assign(siz[0],siz[1],siz[2],siz[3]);
  10909. }
  10910. //! Construct image with dimensions borrowed from another image and initialize pixel values \inplace.
  10911. /**
  10912. In-place version of the constructor CImg(const CImg<t>&,const char*,T).
  10913. **/
  10914. template<typename t>
  10915. CImg<T>& assign(const CImg<t>& img, const char *const dimensions, const T& value) {
  10916. return assign(img,dimensions).fill(value);
  10917. }
  10918. //! Construct image from a display window \inplace.
  10919. /**
  10920. In-place version of the constructor CImg(const CImgDisplay&).
  10921. **/
  10922. CImg<T>& assign(const CImgDisplay &disp) {
  10923. disp.snapshot(*this);
  10924. return *this;
  10925. }
  10926. //! Construct empty image \inplace.
  10927. /**
  10928. Equivalent to assign().
  10929. \note
  10930. - It has been defined for compatibility with STL naming conventions.
  10931. **/
  10932. CImg<T>& clear() {
  10933. return assign();
  10934. }
  10935. //! Transfer content of an image instance into another one.
  10936. /**
  10937. Transfer the dimensions and the pixel buffer content of an image instance into another one,
  10938. and replace instance by an empty image. It avoids the copy of the pixel buffer
  10939. when possible.
  10940. \param img Destination image.
  10941. \note
  10942. - Pixel types \c T and \c t of source and destination images can be different, though the process is
  10943. designed to be instantaneous when \c T and \c t are the same.
  10944. \par Example
  10945. \code
  10946. CImg<float> src(256,256,1,3,0), // Construct a 256x256x1x3 (color) image filled with value '0'.
  10947. dest(16,16); // Construct a 16x16x1x1 (scalar) image.
  10948. src.move_to(dest); // Now, 'src' is empty and 'dest' is the 256x256x1x3 image.
  10949. \endcode
  10950. **/
  10951. template<typename t>
  10952. CImg<t>& move_to(CImg<t>& img) {
  10953. img.assign(*this);
  10954. assign();
  10955. return img;
  10956. }
  10957. //! Transfer content of an image instance into another one \specialization.
  10958. CImg<T>& move_to(CImg<T>& img) {
  10959. if (_is_shared || img._is_shared) img.assign(*this);
  10960. else swap(img);
  10961. assign();
  10962. return img;
  10963. }
  10964. //! Transfer content of an image instance into a new image in an image list.
  10965. /**
  10966. Transfer the dimensions and the pixel buffer content of an image instance
  10967. into a newly inserted image at position \c pos in specified \c CImgList<t> instance.
  10968. \param list Destination list.
  10969. \param pos Position of the newly inserted image in the list.
  10970. \note
  10971. - When optional parameter \c pos is ommited, the image instance is transfered as a new
  10972. image at the end of the specified \c list.
  10973. - It is convenient to sequentially insert new images into image lists, with no
  10974. additional copies of memory buffer.
  10975. \par Example
  10976. \code
  10977. CImgList<float> list; // Construct an empty image list.
  10978. CImg<float> img("reference.jpg"); // Read image from filename.
  10979. img.move_to(list); // Transfer image content as a new item in the list (no buffer copy).
  10980. \endcode
  10981. **/
  10982. template<typename t>
  10983. CImgList<t>& move_to(CImgList<t>& list, const unsigned int pos=~0U) {
  10984. const unsigned int npos = pos>list._width?list._width:pos;
  10985. move_to(list.insert(1,npos)[npos]);
  10986. return list;
  10987. }
  10988. //! Swap fields of two image instances.
  10989. /**
  10990. \param img Image to swap fields with.
  10991. \note
  10992. - It can be used to interchange the content of two images in a very fast way. Can be convenient when dealing
  10993. with algorithms requiring two swapping buffers.
  10994. \par Example
  10995. \code
  10996. CImg<float> img1("lena.jpg"),
  10997. img2("milla.jpg");
  10998. img1.swap(img2); // Now, 'img1' is 'milla' and 'img2' is 'lena'.
  10999. \endcode
  11000. **/
  11001. CImg<T>& swap(CImg<T>& img) {
  11002. cimg::swap(_width,img._width,_height,img._height,_depth,img._depth,_spectrum,img._spectrum);
  11003. cimg::swap(_data,img._data);
  11004. cimg::swap(_is_shared,img._is_shared);
  11005. return img;
  11006. }
  11007. //! Return a reference to an empty image.
  11008. /**
  11009. \note
  11010. This function is useful mainly to declare optional parameters having type \c CImg<T> in functions prototypes,
  11011. e.g.
  11012. \code
  11013. void f(const int x=0, const int y=0, const CImg<float>& img=CImg<float>::empty());
  11014. \endcode
  11015. **/
  11016. static CImg<T>& empty() {
  11017. static CImg<T> _empty;
  11018. return _empty.assign();
  11019. }
  11020. //! Return a reference to an empty image \const.
  11021. static const CImg<T>& const_empty() {
  11022. static const CImg<T> _empty;
  11023. return _empty;
  11024. }
  11025. //@}
  11026. //------------------------------------------
  11027. //
  11028. //! \name Overloaded Operators
  11029. //@{
  11030. //------------------------------------------
  11031. //! Access to a pixel value.
  11032. /**
  11033. Return a reference to a located pixel value of the image instance,
  11034. being possibly \e const, whether the image instance is \e const or not.
  11035. This is the standard method to get/set pixel values in \c CImg<T> images.
  11036. \param x X-coordinate of the pixel value.
  11037. \param y Y-coordinate of the pixel value.
  11038. \param z Z-coordinate of the pixel value.
  11039. \param c C-coordinate of the pixel value.
  11040. \note
  11041. - Range of pixel coordinates start from <tt>(0,0,0,0)</tt> to
  11042. <tt>(width() - 1,height() - 1,depth() - 1,spectrum() - 1)</tt>.
  11043. - Due to the particular arrangement of the pixel buffers defined in %CImg, you can omit one coordinate if the
  11044. corresponding dimension is equal to \c 1.
  11045. For instance, pixels of a 2d image (depth() equal to \c 1) can be accessed by <tt>img(x,y,c)</tt> instead of
  11046. <tt>img(x,y,0,c)</tt>.
  11047. \warning
  11048. - There is \e no boundary checking done in this operator, to make it as fast as possible.
  11049. You \e must take care of out-of-bounds access by yourself, if necessary.
  11050. For debuging purposes, you may want to define macro \c 'cimg_verbosity'>=3 to enable additional boundary
  11051. checking operations in this operator. In that case, warning messages will be printed on the error output
  11052. when accessing out-of-bounds pixels.
  11053. \par Example
  11054. \code
  11055. CImg<float> img(100,100,1,3,0); // Construct a 100x100x1x3 (color) image with pixels set to '0'.
  11056. const float
  11057. valR = img(10,10,0,0), // Read red value at coordinates (10,10).
  11058. valG = img(10,10,0,1), // Read green value at coordinates (10,10)
  11059. valB = img(10,10,2), // Read blue value at coordinates (10,10) (Z-coordinate can be omitted).
  11060. avg = (valR + valG + valB)/3; // Compute average pixel value.
  11061. img(10,10,0) = img(10,10,1) = img(10,10,2) = avg; // Replace the color pixel (10,10) by the average grey value.
  11062. \endcode
  11063. **/
  11064. #if cimg_verbosity>=3
  11065. T& operator()(const unsigned int x, const unsigned int y=0,
  11066. const unsigned int z=0, const unsigned int c=0) {
  11067. const ulongT off = (ulongT)offset(x,y,z,c);
  11068. if (!_data || off>=size()) {
  11069. cimg::warn(_cimg_instance
  11070. "operator(): Invalid pixel request, at coordinates (%d,%d,%d,%d) [offset=%u].",
  11071. cimg_instance,
  11072. (int)x,(int)y,(int)z,(int)c,off);
  11073. return *_data;
  11074. }
  11075. else return _data[off];
  11076. }
  11077. //! Access to a pixel value \const.
  11078. const T& operator()(const unsigned int x, const unsigned int y=0,
  11079. const unsigned int z=0, const unsigned int c=0) const {
  11080. return const_cast<CImg<T>*>(this)->operator()(x,y,z,c);
  11081. }
  11082. //! Access to a pixel value.
  11083. /**
  11084. \param x X-coordinate of the pixel value.
  11085. \param y Y-coordinate of the pixel value.
  11086. \param z Z-coordinate of the pixel value.
  11087. \param c C-coordinate of the pixel value.
  11088. \param wh Precomputed offset, must be equal to <tt>width()*\ref height()</tt>.
  11089. \param whd Precomputed offset, must be equal to <tt>width()*\ref height()*\ref depth()</tt>.
  11090. \note
  11091. - Similar to (but faster than) operator()().
  11092. It uses precomputed offsets to optimize memory access. You may use it to optimize
  11093. the reading/writing of several pixel values in the same image (e.g. in a loop).
  11094. **/
  11095. T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
  11096. const ulongT wh, const ulongT whd=0) {
  11097. cimg::unused(wh,whd);
  11098. return (*this)(x,y,z,c);
  11099. }
  11100. //! Access to a pixel value \const.
  11101. const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
  11102. const ulongT wh, const ulongT whd=0) const {
  11103. cimg::unused(wh,whd);
  11104. return (*this)(x,y,z,c);
  11105. }
  11106. #else
  11107. T& operator()(const unsigned int x) {
  11108. return _data[x];
  11109. }
  11110. const T& operator()(const unsigned int x) const {
  11111. return _data[x];
  11112. }
  11113. T& operator()(const unsigned int x, const unsigned int y) {
  11114. return _data[x + y*_width];
  11115. }
  11116. const T& operator()(const unsigned int x, const unsigned int y) const {
  11117. return _data[x + y*_width];
  11118. }
  11119. T& operator()(const unsigned int x, const unsigned int y, const unsigned int z) {
  11120. return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height];
  11121. }
  11122. const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z) const {
  11123. return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height];
  11124. }
  11125. T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c) {
  11126. return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height + c*(ulongT)_width*_height*_depth];
  11127. }
  11128. const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c) const {
  11129. return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height + c*(ulongT)_width*_height*_depth];
  11130. }
  11131. T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int,
  11132. const ulongT wh) {
  11133. return _data[x + y*_width + z*wh];
  11134. }
  11135. const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int,
  11136. const ulongT wh) const {
  11137. return _data[x + y*_width + z*wh];
  11138. }
  11139. T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
  11140. const ulongT wh, const ulongT whd) {
  11141. return _data[x + y*_width + z*wh + c*whd];
  11142. }
  11143. const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
  11144. const ulongT wh, const ulongT whd) const {
  11145. return _data[x + y*_width + z*wh + c*whd];
  11146. }
  11147. #endif
  11148. //! Implicitely cast an image into a \c T*.
  11149. /**
  11150. Implicitely cast a \c CImg<T> instance into a \c T* or \c const \c T* pointer, whether the image instance
  11151. is \e const or not. The returned pointer points on the first value of the image pixel buffer.
  11152. \note
  11153. - It simply returns the pointer data() to the pixel buffer.
  11154. - This implicit conversion is convenient to test the empty state of images (data() being \c 0 in this case), e.g.
  11155. \code
  11156. CImg<float> img1(100,100), img2; // 'img1' is a 100x100 image, 'img2' is an empty image.
  11157. if (img1) { // Test succeeds, 'img1' is not an empty image.
  11158. if (!img2) { // Test succeeds, 'img2' is an empty image.
  11159. std::printf("'img1' is not empty, 'img2' is empty.");
  11160. }
  11161. }
  11162. \endcode
  11163. - It also allows to use brackets to access pixel values, without need for a \c CImg<T>::operator[](), e.g.
  11164. \code
  11165. CImg<float> img(100,100);
  11166. const float value = img[99]; // Access to value of the last pixel on the first row.
  11167. img[510] = 255; // Set pixel value at (10,5).
  11168. \endcode
  11169. **/
  11170. operator T*() {
  11171. return _data;
  11172. }
  11173. //! Implicitely cast an image into a \c T* \const.
  11174. operator const T*() const {
  11175. return _data;
  11176. }
  11177. //! Assign a value to all image pixels.
  11178. /**
  11179. Assign specified \c value to each pixel value of the image instance.
  11180. \param value Value that will be assigned to image pixels.
  11181. \note
  11182. - The image size is never modified.
  11183. - The \c value may be casted to pixel type \c T if necessary.
  11184. \par Example
  11185. \code
  11186. CImg<char> img(100,100); // Declare image (with garbage values).
  11187. img = 0; // Set all pixel values to '0'.
  11188. img = 1.2; // Set all pixel values to '1' (cast of '1.2' as a 'char').
  11189. \endcode
  11190. **/
  11191. CImg<T>& operator=(const T& value) {
  11192. return fill(value);
  11193. }
  11194. //! Assign pixels values from a specified expression.
  11195. /**
  11196. Initialize all pixel values from the specified string \c expression.
  11197. \param expression Value string describing the way pixel values are set.
  11198. \note
  11199. - String parameter \c expression may describe different things:
  11200. - 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"),
  11201. the pixel values are set from specified \c expression and the image size is not modified.
  11202. - If \c expression is a filename (as in \c "reference.jpg"), the corresponding image file is loaded and
  11203. replace the image instance. The image size is modified if necessary.
  11204. \par Example
  11205. \code
  11206. CImg<float> img1(100,100), img2(img1), img3(img1); // Declare 3 scalar images 100x100 with unitialized values.
  11207. img1 = "0,50,100,150,200,250,200,150,100,50"; // Set pixel values of 'img1' from a value sequence.
  11208. img2 = "10*((x*y)%25)"; // Set pixel values of 'img2' from a formula.
  11209. img3 = "reference.jpg"; // Set pixel values of 'img3' from a file (image size is modified).
  11210. (img1,img2,img3).display();
  11211. \endcode
  11212. \image html ref_operator_eq.jpg
  11213. **/
  11214. CImg<T>& operator=(const char *const expression) {
  11215. const unsigned int omode = cimg::exception_mode();
  11216. cimg::exception_mode(0);
  11217. try {
  11218. _fill(expression,true,true,0,0,"operator=",0);
  11219. } catch (CImgException&) {
  11220. cimg::exception_mode(omode);
  11221. load(expression);
  11222. }
  11223. cimg::exception_mode(omode);
  11224. return *this;
  11225. }
  11226. //! Copy an image into the current image instance.
  11227. /**
  11228. Similar to the in-place copy constructor assign(const CImg<t>&).
  11229. **/
  11230. template<typename t>
  11231. CImg<T>& operator=(const CImg<t>& img) {
  11232. return assign(img);
  11233. }
  11234. //! Copy an image into the current image instance \specialization.
  11235. CImg<T>& operator=(const CImg<T>& img) {
  11236. return assign(img);
  11237. }
  11238. //! Copy the content of a display window to the current image instance.
  11239. /**
  11240. Similar to assign(const CImgDisplay&).
  11241. **/
  11242. CImg<T>& operator=(const CImgDisplay& disp) {
  11243. disp.snapshot(*this);
  11244. return *this;
  11245. }
  11246. //! In-place addition operator.
  11247. /**
  11248. Add specified \c value to all pixels of an image instance.
  11249. \param value Value to add.
  11250. \note
  11251. - Resulting pixel values are casted to fit the pixel type \c T.
  11252. For instance, adding \c 0.2 to a \c CImg<char> is possible but does nothing indeed.
  11253. - Overflow values are treated as with standard C++ numeric types. For instance,
  11254. \code
  11255. CImg<unsigned char> img(100,100,1,1,255); // Construct a 100x100 image with pixel values '255'.
  11256. img+=1; // Add '1' to each pixels -> Overflow.
  11257. // here all pixels of image 'img' are equal to '0'.
  11258. \endcode
  11259. - To prevent value overflow, you may want to consider pixel type \c T as \c float or \c double,
  11260. and use cut() after addition.
  11261. \par Example
  11262. \code
  11263. CImg<unsigned char> img1("reference.jpg"); // Load a 8-bits RGB image (values in [0,255]).
  11264. CImg<float> img2(img1); // Construct a float-valued copy of 'img1'.
  11265. img2+=100; // Add '100' to pixel values -> goes out of [0,255] but no problems with floats.
  11266. img2.cut(0,255); // Cut values in [0,255] to fit the 'unsigned char' constraint.
  11267. img1 = img2; // Rewrite safe result in 'unsigned char' version 'img1'.
  11268. const CImg<unsigned char> img3 = (img1 + 100).cut(0,255); // Do the same in a more simple and elegant way.
  11269. (img1,img2,img3).display();
  11270. \endcode
  11271. \image html ref_operator_plus.jpg
  11272. **/
  11273. template<typename t>
  11274. CImg<T>& operator+=(const t value) {
  11275. if (is_empty()) return *this;
  11276. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=524288))
  11277. cimg_rof(*this,ptrd,T) *ptrd = (T)(*ptrd + value);
  11278. return *this;
  11279. }
  11280. //! In-place addition operator.
  11281. /**
  11282. Add values to image pixels, according to the specified string \c expression.
  11283. \param expression Value string describing the way pixel values are added.
  11284. \note
  11285. - Similar to operator=(const char*), except that it adds values to the pixels of the current image instance,
  11286. instead of assigning them.
  11287. **/
  11288. CImg<T>& operator+=(const char *const expression) {
  11289. return *this+=(+*this)._fill(expression,true,true,0,0,"operator+=",this);
  11290. }
  11291. //! In-place addition operator.
  11292. /**
  11293. Add values to image pixels, according to the values of the input image \c img.
  11294. \param img Input image to add.
  11295. \note
  11296. - The size of the image instance is never modified.
  11297. - It is not mandatory that input image \c img has the same size as the image instance.
  11298. If less values are available in \c img, then the values are added periodically. For instance, adding one
  11299. WxH scalar image (spectrum() equal to \c 1) to one WxH color image (spectrum() equal to \c 3)
  11300. means each color channel will be incremented with the same values at the same locations.
  11301. \par Example
  11302. \code
  11303. CImg<float> img1("reference.jpg"); // Load a RGB color image (img1.spectrum()==3)
  11304. // Construct a scalar shading (img2.spectrum()==1).
  11305. const CImg<float> img2(img1.width(),img.height(),1,1,"255*(x/w)^2");
  11306. img1+=img2; // Add shading to each channel of 'img1'.
  11307. img1.cut(0,255); // Prevent [0,255] overflow.
  11308. (img2,img1).display();
  11309. \endcode
  11310. \image html ref_operator_plus1.jpg
  11311. **/
  11312. template<typename t>
  11313. CImg<T>& operator+=(const CImg<t>& img) {
  11314. const ulongT siz = size(), isiz = img.size();
  11315. if (siz && isiz) {
  11316. if (is_overlapped(img)) return *this+=+img;
  11317. T *ptrd = _data, *const ptre = _data + siz;
  11318. if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
  11319. for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
  11320. *ptrd = (T)(*ptrd + *(ptrs++));
  11321. for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd + *(ptrs++));
  11322. }
  11323. return *this;
  11324. }
  11325. //! In-place increment operator (prefix).
  11326. /**
  11327. Add \c 1 to all image pixels, and return a reference to the current incremented image instance.
  11328. \note
  11329. - Writing \c ++img is equivalent to \c img+=1.
  11330. **/
  11331. CImg<T>& operator++() {
  11332. if (is_empty()) return *this;
  11333. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=524288))
  11334. cimg_rof(*this,ptrd,T) ++*ptrd;
  11335. return *this;
  11336. }
  11337. //! In-place increment operator (postfix).
  11338. /**
  11339. Add \c 1 to all image pixels, and return a new copy of the initial (pre-incremented) image instance.
  11340. \note
  11341. - Use the prefixed version operator++() if you don't need a copy of the initial
  11342. (pre-incremented) image instance, since a useless image copy may be expensive in terms of memory usage.
  11343. **/
  11344. CImg<T> operator++(int) {
  11345. const CImg<T> copy(*this,false);
  11346. ++*this;
  11347. return copy;
  11348. }
  11349. //! Return a non-shared copy of the image instance.
  11350. /**
  11351. \note
  11352. - Use this operator to ensure you get a non-shared copy of an image instance with same pixel type \c T.
  11353. Indeed, the usual copy constructor CImg<T>(const CImg<T>&) returns a shared copy of a shared input image,
  11354. and it may be not desirable to work on a regular copy (e.g. for a resize operation) if you have no
  11355. information about the shared state of the input image.
  11356. - Writing \c (+img) is equivalent to \c CImg<T>(img,false).
  11357. **/
  11358. CImg<T> operator+() const {
  11359. return CImg<T>(*this,false);
  11360. }
  11361. //! Addition operator.
  11362. /**
  11363. Similar to operator+=(const t), except that it returns a new image instance instead of operating in-place.
  11364. The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
  11365. **/
  11366. template<typename t>
  11367. CImg<_cimg_Tt> operator+(const t value) const {
  11368. return CImg<_cimg_Tt>(*this,false)+=value;
  11369. }
  11370. //! Addition operator.
  11371. /**
  11372. Similar to operator+=(const char*), except that it returns a new image instance instead of operating in-place.
  11373. The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
  11374. **/
  11375. CImg<Tfloat> operator+(const char *const expression) const {
  11376. return CImg<Tfloat>(*this,false)+=expression;
  11377. }
  11378. //! Addition operator.
  11379. /**
  11380. Similar to operator+=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
  11381. The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
  11382. **/
  11383. template<typename t>
  11384. CImg<_cimg_Tt> operator+(const CImg<t>& img) const {
  11385. return CImg<_cimg_Tt>(*this,false)+=img;
  11386. }
  11387. //! In-place substraction operator.
  11388. /**
  11389. Similar to operator+=(const t), except that it performs a substraction instead of an addition.
  11390. **/
  11391. template<typename t>
  11392. CImg<T>& operator-=(const t value) {
  11393. if (is_empty()) return *this;
  11394. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=524288))
  11395. cimg_rof(*this,ptrd,T) *ptrd = (T)(*ptrd - value);
  11396. return *this;
  11397. }
  11398. //! In-place substraction operator.
  11399. /**
  11400. Similar to operator+=(const char*), except that it performs a substraction instead of an addition.
  11401. **/
  11402. CImg<T>& operator-=(const char *const expression) {
  11403. return *this-=(+*this)._fill(expression,true,true,0,0,"operator-=",this);
  11404. }
  11405. //! In-place substraction operator.
  11406. /**
  11407. Similar to operator+=(const CImg<t>&), except that it performs a substraction instead of an addition.
  11408. **/
  11409. template<typename t>
  11410. CImg<T>& operator-=(const CImg<t>& img) {
  11411. const ulongT siz = size(), isiz = img.size();
  11412. if (siz && isiz) {
  11413. if (is_overlapped(img)) return *this-=+img;
  11414. T *ptrd = _data, *const ptre = _data + siz;
  11415. if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
  11416. for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
  11417. *ptrd = (T)(*ptrd - *(ptrs++));
  11418. for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd - *(ptrs++));
  11419. }
  11420. return *this;
  11421. }
  11422. //! In-place decrement operator (prefix).
  11423. /**
  11424. Similar to operator++(), except that it performs a decrement instead of an increment.
  11425. **/
  11426. CImg<T>& operator--() {
  11427. if (is_empty()) return *this;
  11428. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=524288))
  11429. cimg_rof(*this,ptrd,T) *ptrd = *ptrd - (T)1;
  11430. return *this;
  11431. }
  11432. //! In-place decrement operator (postfix).
  11433. /**
  11434. Similar to operator++(int), except that it performs a decrement instead of an increment.
  11435. **/
  11436. CImg<T> operator--(int) {
  11437. const CImg<T> copy(*this,false);
  11438. --*this;
  11439. return copy;
  11440. }
  11441. //! Replace each pixel by its opposite value.
  11442. /**
  11443. \note
  11444. - If the computed opposite values are out-of-range, they are treated as with standard C++ numeric types.
  11445. For instance, the \c unsigned \c char opposite of \c 1 is \c 255.
  11446. \par Example
  11447. \code
  11448. const CImg<unsigned char>
  11449. img1("reference.jpg"), // Load a RGB color image.
  11450. img2 = -img1; // Compute its opposite (in 'unsigned char').
  11451. (img1,img2).display();
  11452. \endcode
  11453. \image html ref_operator_minus.jpg
  11454. **/
  11455. CImg<T> operator-() const {
  11456. return CImg<T>(_width,_height,_depth,_spectrum,(T)0)-=*this;
  11457. }
  11458. //! Substraction operator.
  11459. /**
  11460. Similar to operator-=(const t), except that it returns a new image instance instead of operating in-place.
  11461. The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
  11462. **/
  11463. template<typename t>
  11464. CImg<_cimg_Tt> operator-(const t value) const {
  11465. return CImg<_cimg_Tt>(*this,false)-=value;
  11466. }
  11467. //! Substraction operator.
  11468. /**
  11469. Similar to operator-=(const char*), except that it returns a new image instance instead of operating in-place.
  11470. The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
  11471. **/
  11472. CImg<Tfloat> operator-(const char *const expression) const {
  11473. return CImg<Tfloat>(*this,false)-=expression;
  11474. }
  11475. //! Substraction operator.
  11476. /**
  11477. Similar to operator-=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
  11478. The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
  11479. **/
  11480. template<typename t>
  11481. CImg<_cimg_Tt> operator-(const CImg<t>& img) const {
  11482. return CImg<_cimg_Tt>(*this,false)-=img;
  11483. }
  11484. //! In-place multiplication operator.
  11485. /**
  11486. Similar to operator+=(const t), except that it performs a multiplication instead of an addition.
  11487. **/
  11488. template<typename t>
  11489. CImg<T>& operator*=(const t value) {
  11490. if (is_empty()) return *this;
  11491. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=262144))
  11492. cimg_rof(*this,ptrd,T) *ptrd = (T)(*ptrd * value);
  11493. return *this;
  11494. }
  11495. //! In-place multiplication operator.
  11496. /**
  11497. Similar to operator+=(const char*), except that it performs a multiplication instead of an addition.
  11498. **/
  11499. CImg<T>& operator*=(const char *const expression) {
  11500. return mul((+*this)._fill(expression,true,true,0,0,"operator*=",this));
  11501. }
  11502. //! In-place multiplication operator.
  11503. /**
  11504. Replace the image instance by the matrix multiplication between the image instance and the specified matrix
  11505. \c img.
  11506. \param img Second operand of the matrix multiplication.
  11507. \note
  11508. - It does \e not compute a pointwise multiplication between two images. For this purpose, use
  11509. mul(const CImg<t>&) instead.
  11510. - The size of the image instance can be modified by this operator.
  11511. \par Example
  11512. \code
  11513. CImg<float> A(2,2,1,1, 1,2,3,4); // Construct 2x2 matrix A = [1,2;3,4].
  11514. const CImg<float> X(1,2,1,1, 1,2); // Construct 1x2 vector X = [1;2].
  11515. A*=X; // Assign matrix multiplication A*X to 'A'.
  11516. // 'A' is now a 1x2 vector whose values are [5;11].
  11517. \endcode
  11518. **/
  11519. template<typename t>
  11520. CImg<T>& operator*=(const CImg<t>& img) {
  11521. return ((*this)*img).move_to(*this);
  11522. }
  11523. //! Multiplication operator.
  11524. /**
  11525. Similar to operator*=(const t), except that it returns a new image instance instead of operating in-place.
  11526. The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
  11527. **/
  11528. template<typename t>
  11529. CImg<_cimg_Tt> operator*(const t value) const {
  11530. return CImg<_cimg_Tt>(*this,false)*=value;
  11531. }
  11532. //! Multiplication operator.
  11533. /**
  11534. Similar to operator*=(const char*), except that it returns a new image instance instead of operating in-place.
  11535. The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
  11536. **/
  11537. CImg<Tfloat> operator*(const char *const expression) const {
  11538. return CImg<Tfloat>(*this,false)*=expression;
  11539. }
  11540. //! Multiplication operator.
  11541. /**
  11542. Similar to operator*=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
  11543. The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
  11544. **/
  11545. template<typename t>
  11546. CImg<_cimg_Tt> operator*(const CImg<t>& img) const {
  11547. if (_width!=img._height || _depth!=1 || _spectrum!=1)
  11548. throw CImgArgumentException(_cimg_instance
  11549. "operator*(): Invalid multiplication of instance by specified "
  11550. "matrix (%u,%u,%u,%u,%p)",
  11551. cimg_instance,
  11552. img._width,img._height,img._depth,img._spectrum,img._data);
  11553. CImg<_cimg_Tt> res(img._width,_height);
  11554. #ifdef cimg_use_openmp
  11555. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(size()>1024 && img.size()>1024))
  11556. cimg_forXY(res,i,j) {
  11557. _cimg_Ttdouble value = 0; cimg_forX(*this,k) value+=(*this)(k,j)*img(i,k); res(i,j) = (_cimg_Tt)value;
  11558. }
  11559. #else
  11560. _cimg_Tt *ptrd = res._data;
  11561. cimg_forXY(res,i,j) {
  11562. _cimg_Ttdouble value = 0; cimg_forX(*this,k) value+=(*this)(k,j)*img(i,k); *(ptrd++) = (_cimg_Tt)value;
  11563. }
  11564. #endif
  11565. return res;
  11566. }
  11567. //! In-place division operator.
  11568. /**
  11569. Similar to operator+=(const t), except that it performs a division instead of an addition.
  11570. **/
  11571. template<typename t>
  11572. CImg<T>& operator/=(const t value) {
  11573. if (is_empty()) return *this;
  11574. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
  11575. cimg_rof(*this,ptrd,T) *ptrd = (T)(*ptrd / value);
  11576. return *this;
  11577. }
  11578. //! In-place division operator.
  11579. /**
  11580. Similar to operator+=(const char*), except that it performs a division instead of an addition.
  11581. **/
  11582. CImg<T>& operator/=(const char *const expression) {
  11583. return div((+*this)._fill(expression,true,true,0,0,"operator/=",this));
  11584. }
  11585. //! In-place division operator.
  11586. /**
  11587. Replace the image instance by the (right) matrix division between the image instance and the specified
  11588. matrix \c img.
  11589. \param img Second operand of the matrix division.
  11590. \note
  11591. - It does \e not compute a pointwise division between two images. For this purpose, use
  11592. div(const CImg<t>&) instead.
  11593. - It returns the matrix operation \c A*inverse(img).
  11594. - The size of the image instance can be modified by this operator.
  11595. **/
  11596. template<typename t>
  11597. CImg<T>& operator/=(const CImg<t>& img) {
  11598. return (*this*img.get_invert()).move_to(*this);
  11599. }
  11600. //! Division operator.
  11601. /**
  11602. Similar to operator/=(const t), except that it returns a new image instance instead of operating in-place.
  11603. The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
  11604. **/
  11605. template<typename t>
  11606. CImg<_cimg_Tt> operator/(const t value) const {
  11607. return CImg<_cimg_Tt>(*this,false)/=value;
  11608. }
  11609. //! Division operator.
  11610. /**
  11611. Similar to operator/=(const char*), except that it returns a new image instance instead of operating in-place.
  11612. The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
  11613. **/
  11614. CImg<Tfloat> operator/(const char *const expression) const {
  11615. return CImg<Tfloat>(*this,false)/=expression;
  11616. }
  11617. //! Division operator.
  11618. /**
  11619. Similar to operator/=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
  11620. The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
  11621. **/
  11622. template<typename t>
  11623. CImg<_cimg_Tt> operator/(const CImg<t>& img) const {
  11624. return (*this)*img.get_invert();
  11625. }
  11626. //! In-place modulo operator.
  11627. /**
  11628. Similar to operator+=(const t), except that it performs a modulo operation instead of an addition.
  11629. **/
  11630. template<typename t>
  11631. CImg<T>& operator%=(const t value) {
  11632. if (is_empty()) return *this;
  11633. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=16384))
  11634. cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::mod(*ptrd,(T)value);
  11635. return *this;
  11636. }
  11637. //! In-place modulo operator.
  11638. /**
  11639. Similar to operator+=(const char*), except that it performs a modulo operation instead of an addition.
  11640. **/
  11641. CImg<T>& operator%=(const char *const expression) {
  11642. return *this%=(+*this)._fill(expression,true,true,0,0,"operator%=",this);
  11643. }
  11644. //! In-place modulo operator.
  11645. /**
  11646. Similar to operator+=(const CImg<t>&), except that it performs a modulo operation instead of an addition.
  11647. **/
  11648. template<typename t>
  11649. CImg<T>& operator%=(const CImg<t>& img) {
  11650. const ulongT siz = size(), isiz = img.size();
  11651. if (siz && isiz) {
  11652. if (is_overlapped(img)) return *this%=+img;
  11653. T *ptrd = _data, *const ptre = _data + siz;
  11654. if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
  11655. for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
  11656. *ptrd = cimg::mod(*ptrd,(T)*(ptrs++));
  11657. for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = cimg::mod(*ptrd,(T)*(ptrs++));
  11658. }
  11659. return *this;
  11660. }
  11661. //! Modulo operator.
  11662. /**
  11663. Similar to operator%=(const t), except that it returns a new image instance instead of operating in-place.
  11664. The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
  11665. **/
  11666. template<typename t>
  11667. CImg<_cimg_Tt> operator%(const t value) const {
  11668. return CImg<_cimg_Tt>(*this,false)%=value;
  11669. }
  11670. //! Modulo operator.
  11671. /**
  11672. Similar to operator%=(const char*), except that it returns a new image instance instead of operating in-place.
  11673. The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
  11674. **/
  11675. CImg<Tfloat> operator%(const char *const expression) const {
  11676. return CImg<Tfloat>(*this,false)%=expression;
  11677. }
  11678. //! Modulo operator.
  11679. /**
  11680. Similar to operator%=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
  11681. The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
  11682. **/
  11683. template<typename t>
  11684. CImg<_cimg_Tt> operator%(const CImg<t>& img) const {
  11685. return CImg<_cimg_Tt>(*this,false)%=img;
  11686. }
  11687. //! In-place bitwise AND operator.
  11688. /**
  11689. Similar to operator+=(const t), except that it performs a bitwise AND operation instead of an addition.
  11690. **/
  11691. template<typename t>
  11692. CImg<T>& operator&=(const t value) {
  11693. if (is_empty()) return *this;
  11694. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
  11695. cimg_rof(*this,ptrd,T) *ptrd = (T)((ulongT)*ptrd & (ulongT)value);
  11696. return *this;
  11697. }
  11698. //! In-place bitwise AND operator.
  11699. /**
  11700. Similar to operator+=(const char*), except that it performs a bitwise AND operation instead of an addition.
  11701. **/
  11702. CImg<T>& operator&=(const char *const expression) {
  11703. return *this&=(+*this)._fill(expression,true,true,0,0,"operator&=",this);
  11704. }
  11705. //! In-place bitwise AND operator.
  11706. /**
  11707. Similar to operator+=(const CImg<t>&), except that it performs a bitwise AND operation instead of an addition.
  11708. **/
  11709. template<typename t>
  11710. CImg<T>& operator&=(const CImg<t>& img) {
  11711. const ulongT siz = size(), isiz = img.size();
  11712. if (siz && isiz) {
  11713. if (is_overlapped(img)) return *this&=+img;
  11714. T *ptrd = _data, *const ptre = _data + siz;
  11715. if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
  11716. for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
  11717. *ptrd = (T)((ulongT)*ptrd & (ulongT)*(ptrs++));
  11718. for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((ulongT)*ptrd & (ulongT)*(ptrs++));
  11719. }
  11720. return *this;
  11721. }
  11722. //! Bitwise AND operator.
  11723. /**
  11724. Similar to operator&=(const t), except that it returns a new image instance instead of operating in-place.
  11725. The pixel type of the returned image is \c T.
  11726. **/
  11727. template<typename t>
  11728. CImg<T> operator&(const t value) const {
  11729. return (+*this)&=value;
  11730. }
  11731. //! Bitwise AND operator.
  11732. /**
  11733. Similar to operator&=(const char*), except that it returns a new image instance instead of operating in-place.
  11734. The pixel type of the returned image is \c T.
  11735. **/
  11736. CImg<T> operator&(const char *const expression) const {
  11737. return (+*this)&=expression;
  11738. }
  11739. //! Bitwise AND operator.
  11740. /**
  11741. Similar to operator&=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
  11742. The pixel type of the returned image is \c T.
  11743. **/
  11744. template<typename t>
  11745. CImg<T> operator&(const CImg<t>& img) const {
  11746. return (+*this)&=img;
  11747. }
  11748. //! In-place bitwise OR operator.
  11749. /**
  11750. Similar to operator+=(const t), except that it performs a bitwise OR operation instead of an addition.
  11751. **/
  11752. template<typename t>
  11753. CImg<T>& operator|=(const t value) {
  11754. if (is_empty()) return *this;
  11755. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
  11756. cimg_rof(*this,ptrd,T) *ptrd = (T)((ulongT)*ptrd | (ulongT)value);
  11757. return *this;
  11758. }
  11759. //! In-place bitwise OR operator.
  11760. /**
  11761. Similar to operator+=(const char*), except that it performs a bitwise OR operation instead of an addition.
  11762. **/
  11763. CImg<T>& operator|=(const char *const expression) {
  11764. return *this|=(+*this)._fill(expression,true,true,0,0,"operator|=",this);
  11765. }
  11766. //! In-place bitwise OR operator.
  11767. /**
  11768. Similar to operator+=(const CImg<t>&), except that it performs a bitwise OR operation instead of an addition.
  11769. **/
  11770. template<typename t>
  11771. CImg<T>& operator|=(const CImg<t>& img) {
  11772. const ulongT siz = size(), isiz = img.size();
  11773. if (siz && isiz) {
  11774. if (is_overlapped(img)) return *this|=+img;
  11775. T *ptrd = _data, *const ptre = _data + siz;
  11776. if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
  11777. for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
  11778. *ptrd = (T)((ulongT)*ptrd | (ulongT)*(ptrs++));
  11779. for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((ulongT)*ptrd | (ulongT)*(ptrs++));
  11780. }
  11781. return *this;
  11782. }
  11783. //! Bitwise OR operator.
  11784. /**
  11785. Similar to operator|=(const t), except that it returns a new image instance instead of operating in-place.
  11786. The pixel type of the returned image is \c T.
  11787. **/
  11788. template<typename t>
  11789. CImg<T> operator|(const t value) const {
  11790. return (+*this)|=value;
  11791. }
  11792. //! Bitwise OR operator.
  11793. /**
  11794. Similar to operator|=(const char*), except that it returns a new image instance instead of operating in-place.
  11795. The pixel type of the returned image is \c T.
  11796. **/
  11797. CImg<T> operator|(const char *const expression) const {
  11798. return (+*this)|=expression;
  11799. }
  11800. //! Bitwise OR operator.
  11801. /**
  11802. Similar to operator|=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
  11803. The pixel type of the returned image is \c T.
  11804. **/
  11805. template<typename t>
  11806. CImg<T> operator|(const CImg<t>& img) const {
  11807. return (+*this)|=img;
  11808. }
  11809. //! In-place bitwise XOR operator.
  11810. /**
  11811. Similar to operator+=(const t), except that it performs a bitwise XOR operation instead of an addition.
  11812. \warning
  11813. - It does \e not compute the \e power of pixel values. For this purpose, use pow(const t) instead.
  11814. **/
  11815. template<typename t>
  11816. CImg<T>& operator^=(const t value) {
  11817. if (is_empty()) return *this;
  11818. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
  11819. cimg_rof(*this,ptrd,T) *ptrd = (T)((ulongT)*ptrd ^ (ulongT)value);
  11820. return *this;
  11821. }
  11822. //! In-place bitwise XOR operator.
  11823. /**
  11824. Similar to operator+=(const char*), except that it performs a bitwise XOR operation instead of an addition.
  11825. \warning
  11826. - It does \e not compute the \e power of pixel values. For this purpose, use pow(const char*) instead.
  11827. **/
  11828. CImg<T>& operator^=(const char *const expression) {
  11829. return *this^=(+*this)._fill(expression,true,true,0,0,"operator^=",this);
  11830. }
  11831. //! In-place bitwise XOR operator.
  11832. /**
  11833. Similar to operator+=(const CImg<t>&), except that it performs a bitwise XOR operation instead of an addition.
  11834. \warning
  11835. - It does \e not compute the \e power of pixel values. For this purpose, use pow(const CImg<t>&) instead.
  11836. **/
  11837. template<typename t>
  11838. CImg<T>& operator^=(const CImg<t>& img) {
  11839. const ulongT siz = size(), isiz = img.size();
  11840. if (siz && isiz) {
  11841. if (is_overlapped(img)) return *this^=+img;
  11842. T *ptrd = _data, *const ptre = _data + siz;
  11843. if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
  11844. for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
  11845. *ptrd = (T)((ulongT)*ptrd ^ (ulongT)*(ptrs++));
  11846. for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((ulongT)*ptrd ^ (ulongT)*(ptrs++));
  11847. }
  11848. return *this;
  11849. }
  11850. //! Bitwise XOR operator.
  11851. /**
  11852. Similar to operator^=(const t), except that it returns a new image instance instead of operating in-place.
  11853. The pixel type of the returned image is \c T.
  11854. **/
  11855. template<typename t>
  11856. CImg<T> operator^(const t value) const {
  11857. return (+*this)^=value;
  11858. }
  11859. //! Bitwise XOR operator.
  11860. /**
  11861. Similar to operator^=(const char*), except that it returns a new image instance instead of operating in-place.
  11862. The pixel type of the returned image is \c T.
  11863. **/
  11864. CImg<T> operator^(const char *const expression) const {
  11865. return (+*this)^=expression;
  11866. }
  11867. //! Bitwise XOR operator.
  11868. /**
  11869. Similar to operator^=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
  11870. The pixel type of the returned image is \c T.
  11871. **/
  11872. template<typename t>
  11873. CImg<T> operator^(const CImg<t>& img) const {
  11874. return (+*this)^=img;
  11875. }
  11876. //! In-place bitwise left shift operator.
  11877. /**
  11878. Similar to operator+=(const t), except that it performs a bitwise left shift instead of an addition.
  11879. **/
  11880. template<typename t>
  11881. CImg<T>& operator<<=(const t value) {
  11882. if (is_empty()) return *this;
  11883. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=65536))
  11884. cimg_rof(*this,ptrd,T) *ptrd = (T)(((longT)*ptrd) << (int)value);
  11885. return *this;
  11886. }
  11887. //! In-place bitwise left shift operator.
  11888. /**
  11889. Similar to operator+=(const char*), except that it performs a bitwise left shift instead of an addition.
  11890. **/
  11891. CImg<T>& operator<<=(const char *const expression) {
  11892. return *this<<=(+*this)._fill(expression,true,true,0,0,"operator<<=",this);
  11893. }
  11894. //! In-place bitwise left shift operator.
  11895. /**
  11896. Similar to operator+=(const CImg<t>&), except that it performs a bitwise left shift instead of an addition.
  11897. **/
  11898. template<typename t>
  11899. CImg<T>& operator<<=(const CImg<t>& img) {
  11900. const ulongT siz = size(), isiz = img.size();
  11901. if (siz && isiz) {
  11902. if (is_overlapped(img)) return *this^=+img;
  11903. T *ptrd = _data, *const ptre = _data + siz;
  11904. if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
  11905. for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
  11906. *ptrd = (T)((longT)*ptrd << (int)*(ptrs++));
  11907. for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((longT)*ptrd << (int)*(ptrs++));
  11908. }
  11909. return *this;
  11910. }
  11911. //! Bitwise left shift operator.
  11912. /**
  11913. Similar to operator<<=(const t), except that it returns a new image instance instead of operating in-place.
  11914. The pixel type of the returned image is \c T.
  11915. **/
  11916. template<typename t>
  11917. CImg<T> operator<<(const t value) const {
  11918. return (+*this)<<=value;
  11919. }
  11920. //! Bitwise left shift operator.
  11921. /**
  11922. Similar to operator<<=(const char*), except that it returns a new image instance instead of operating in-place.
  11923. The pixel type of the returned image is \c T.
  11924. **/
  11925. CImg<T> operator<<(const char *const expression) const {
  11926. return (+*this)<<=expression;
  11927. }
  11928. //! Bitwise left shift operator.
  11929. /**
  11930. Similar to operator<<=(const CImg<t>&), except that it returns a new image instance instead of
  11931. operating in-place.
  11932. The pixel type of the returned image is \c T.
  11933. **/
  11934. template<typename t>
  11935. CImg<T> operator<<(const CImg<t>& img) const {
  11936. return (+*this)<<=img;
  11937. }
  11938. //! In-place bitwise right shift operator.
  11939. /**
  11940. Similar to operator+=(const t), except that it performs a bitwise right shift instead of an addition.
  11941. **/
  11942. template<typename t>
  11943. CImg<T>& operator>>=(const t value) {
  11944. if (is_empty()) return *this;
  11945. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=65536))
  11946. cimg_rof(*this,ptrd,T) *ptrd = (T)(((longT)*ptrd) >> (int)value);
  11947. return *this;
  11948. }
  11949. //! In-place bitwise right shift operator.
  11950. /**
  11951. Similar to operator+=(const char*), except that it performs a bitwise right shift instead of an addition.
  11952. **/
  11953. CImg<T>& operator>>=(const char *const expression) {
  11954. return *this>>=(+*this)._fill(expression,true,true,0,0,"operator>>=",this);
  11955. }
  11956. //! In-place bitwise right shift operator.
  11957. /**
  11958. Similar to operator+=(const CImg<t>&), except that it performs a bitwise right shift instead of an addition.
  11959. **/
  11960. template<typename t>
  11961. CImg<T>& operator>>=(const CImg<t>& img) {
  11962. const ulongT siz = size(), isiz = img.size();
  11963. if (siz && isiz) {
  11964. if (is_overlapped(img)) return *this^=+img;
  11965. T *ptrd = _data, *const ptre = _data + siz;
  11966. if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
  11967. for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
  11968. *ptrd = (T)((longT)*ptrd >> (int)*(ptrs++));
  11969. for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((longT)*ptrd >> (int)*(ptrs++));
  11970. }
  11971. return *this;
  11972. }
  11973. //! Bitwise right shift operator.
  11974. /**
  11975. Similar to operator>>=(const t), except that it returns a new image instance instead of operating in-place.
  11976. The pixel type of the returned image is \c T.
  11977. **/
  11978. template<typename t>
  11979. CImg<T> operator>>(const t value) const {
  11980. return (+*this)>>=value;
  11981. }
  11982. //! Bitwise right shift operator.
  11983. /**
  11984. Similar to operator>>=(const char*), except that it returns a new image instance instead of operating in-place.
  11985. The pixel type of the returned image is \c T.
  11986. **/
  11987. CImg<T> operator>>(const char *const expression) const {
  11988. return (+*this)>>=expression;
  11989. }
  11990. //! Bitwise right shift operator.
  11991. /**
  11992. Similar to operator>>=(const CImg<t>&), except that it returns a new image instance instead of
  11993. operating in-place.
  11994. The pixel type of the returned image is \c T.
  11995. **/
  11996. template<typename t>
  11997. CImg<T> operator>>(const CImg<t>& img) const {
  11998. return (+*this)>>=img;
  11999. }
  12000. //! Bitwise inversion operator.
  12001. /**
  12002. Similar to operator-(), except that it compute the bitwise inverse instead of the opposite value.
  12003. **/
  12004. CImg<T> operator~() const {
  12005. CImg<T> res(_width,_height,_depth,_spectrum);
  12006. const T *ptrs = _data;
  12007. cimg_for(res,ptrd,T) { const ulongT value = (ulongT)*(ptrs++); *ptrd = (T)~value; }
  12008. return res;
  12009. }
  12010. //! Test if all pixels of an image have the same value.
  12011. /**
  12012. Return \c true is all pixels of the image instance are equal to the specified \c value.
  12013. \param value Reference value to compare with.
  12014. **/
  12015. template<typename t>
  12016. bool operator==(const t value) const {
  12017. if (is_empty()) return false;
  12018. typedef _cimg_Tt Tt;
  12019. bool is_equal = true;
  12020. for (T *ptrd = _data + size(); is_equal && ptrd>_data; is_equal = ((Tt)*(--ptrd)==(Tt)value)) {}
  12021. return is_equal;
  12022. }
  12023. //! Test if all pixel values of an image follow a specified expression.
  12024. /**
  12025. Return \c true is all pixels of the image instance are equal to the specified \c expression.
  12026. \param expression Value string describing the way pixel values are compared.
  12027. **/
  12028. bool operator==(const char *const expression) const {
  12029. return *this==(+*this)._fill(expression,true,true,0,0,"operator==",this);
  12030. }
  12031. //! Test if two images have the same size and values.
  12032. /**
  12033. Return \c true if the image instance and the input image \c img have the same dimensions and pixel values,
  12034. and \c false otherwise.
  12035. \param img Input image to compare with.
  12036. \note
  12037. - The pixel buffer pointers data() of the two compared images do not have to be the same for operator==()
  12038. to return \c true.
  12039. Only the dimensions and the pixel values matter. Thus, the comparison can be \c true even for different
  12040. pixel types \c T and \c t.
  12041. \par Example
  12042. \code
  12043. const CImg<float> img1(1,3,1,1, 0,1,2); // Construct a 1x3 vector [0;1;2] (with 'float' pixel values).
  12044. const CImg<char> img2(1,3,1,1, 0,1,2); // Construct a 1x3 vector [0;1;2] (with 'char' pixel values).
  12045. if (img1==img2) { // Test succeeds, image dimensions and values are the same.
  12046. std::printf("'img1' and 'img2' have same dimensions and values.");
  12047. }
  12048. \endcode
  12049. **/
  12050. template<typename t>
  12051. bool operator==(const CImg<t>& img) const {
  12052. typedef _cimg_Tt Tt;
  12053. const ulongT siz = size();
  12054. bool is_equal = true;
  12055. if (siz!=img.size()) return false;
  12056. t *ptrs = img._data + siz;
  12057. for (T *ptrd = _data + siz; is_equal && ptrd>_data; is_equal = ((Tt)*(--ptrd)==(Tt)*(--ptrs))) {}
  12058. return is_equal;
  12059. }
  12060. //! Test if pixels of an image are all different from a value.
  12061. /**
  12062. Return \c true is all pixels of the image instance are different than the specified \c value.
  12063. \param value Reference value to compare with.
  12064. **/
  12065. template<typename t>
  12066. bool operator!=(const t value) const {
  12067. return !((*this)==value);
  12068. }
  12069. //! Test if all pixel values of an image are different from a specified expression.
  12070. /**
  12071. Return \c true is all pixels of the image instance are different to the specified \c expression.
  12072. \param expression Value string describing the way pixel values are compared.
  12073. **/
  12074. bool operator!=(const char *const expression) const {
  12075. return !((*this)==expression);
  12076. }
  12077. //! Test if two images have different sizes or values.
  12078. /**
  12079. Return \c true if the image instance and the input image \c img have different dimensions or pixel values,
  12080. and \c false otherwise.
  12081. \param img Input image to compare with.
  12082. \note
  12083. - Writing \c img1!=img2 is equivalent to \c !(img1==img2).
  12084. **/
  12085. template<typename t>
  12086. bool operator!=(const CImg<t>& img) const {
  12087. return !((*this)==img);
  12088. }
  12089. //! Construct an image list from two images.
  12090. /**
  12091. Return a new list of image (\c CImgList instance) containing exactly two elements:
  12092. - A copy of the image instance, at position [\c 0].
  12093. - A copy of the specified image \c img, at position [\c 1].
  12094. \param img Input image that will be the second image of the resulting list.
  12095. \note
  12096. - The family of operator,() is convenient to easily create list of images, but it is also \e quite \e slow
  12097. in practice (see warning below).
  12098. - Constructed lists contain no shared images. If image instance or input image \c img are shared, they are
  12099. inserted as new non-shared copies in the resulting list.
  12100. - The pixel type of the returned list may be a superset of the initial pixel type \c T, if necessary.
  12101. \warning
  12102. - Pipelining operator,() \c N times will perform \c N copies of the entire content of a (growing) image list.
  12103. This may become very expensive in terms of speed and used memory. You should avoid using this technique to
  12104. build a new CImgList instance from several images, if you are seeking for performance.
  12105. Fast insertions of images in an image list are possible with
  12106. CImgList<T>::insert(const CImg<t>&,unsigned int,bool) or move_to(CImgList<t>&,unsigned int).
  12107. \par Example
  12108. \code
  12109. const CImg<float>
  12110. img1("reference.jpg"),
  12111. img2 = img1.get_mirror('x'),
  12112. img3 = img2.get_blur(5);
  12113. const CImgList<float> list = (img1,img2); // Create list of two elements from 'img1' and 'img2'.
  12114. (list,img3).display(); // Display image list containing copies of 'img1','img2' and 'img3'.
  12115. \endcode
  12116. \image html ref_operator_comma.jpg
  12117. **/
  12118. template<typename t>
  12119. CImgList<_cimg_Tt> operator,(const CImg<t>& img) const {
  12120. return CImgList<_cimg_Tt>(*this,img);
  12121. }
  12122. //! Construct an image list from image instance and an input image list.
  12123. /**
  12124. Return a new list of images (\c CImgList instance) containing exactly \c list.size() \c + \c 1 elements:
  12125. - A copy of the image instance, at position [\c 0].
  12126. - A copy of the specified image list \c list, from positions [\c 1] to [\c list.size()].
  12127. \param list Input image list that will be appended to the image instance.
  12128. \note
  12129. - Similar to operator,(const CImg<t>&) const, except that it takes an image list as an argument.
  12130. **/
  12131. template<typename t>
  12132. CImgList<_cimg_Tt> operator,(const CImgList<t>& list) const {
  12133. return CImgList<_cimg_Tt>(list,false).insert(*this,0);
  12134. }
  12135. //! Split image along specified axis.
  12136. /**
  12137. Return a new list of images (\c CImgList instance) containing the splitted components
  12138. of the instance image along the specified axis.
  12139. \param axis Splitting axis (can be '\c x','\c y','\c z' or '\c c')
  12140. \note
  12141. - Similar to get_split(char,int) const, with default second argument.
  12142. \par Example
  12143. \code
  12144. const CImg<unsigned char> img("reference.jpg"); // Load a RGB color image.
  12145. const CImgList<unsigned char> list = (img<'c'); // Get a list of its three R,G,B channels.
  12146. (img,list).display();
  12147. \endcode
  12148. \image html ref_operator_less.jpg
  12149. **/
  12150. CImgList<T> operator<(const char axis) const {
  12151. return get_split(axis);
  12152. }
  12153. //@}
  12154. //-------------------------------------
  12155. //
  12156. //! \name Instance Characteristics
  12157. //@{
  12158. //-------------------------------------
  12159. //! Return the type of image pixel values as a C string.
  12160. /**
  12161. Return a \c char* string containing the usual type name of the image pixel values
  12162. (i.e. a stringified version of the template parameter \c T).
  12163. \note
  12164. - The returned string may contain spaces (as in \c "unsigned char").
  12165. - If the pixel type \c T does not correspond to a registered type, the string <tt>"unknown"</tt> is returned.
  12166. **/
  12167. static const char* pixel_type() {
  12168. return cimg::type<T>::string();
  12169. }
  12170. //! Return the number of image columns.
  12171. /**
  12172. Return the image width, i.e. the image dimension along the X-axis.
  12173. \note
  12174. - The width() of an empty image is equal to \c 0.
  12175. - width() is typically equal to \c 1 when considering images as \e vectors for matrix calculations.
  12176. - width() returns an \c int, although the image width is internally stored as an \c unsigned \c int.
  12177. Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
  12178. \c unsigned \c int variables.
  12179. Access to the initial \c unsigned \c int variable is possible (though not recommended) by
  12180. <tt>(*this)._width</tt>.
  12181. **/
  12182. int width() const {
  12183. return (int)_width;
  12184. }
  12185. //! Return the number of image rows.
  12186. /**
  12187. Return the image height, i.e. the image dimension along the Y-axis.
  12188. \note
  12189. - The height() of an empty image is equal to \c 0.
  12190. - height() returns an \c int, although the image height is internally stored as an \c unsigned \c int.
  12191. Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
  12192. \c unsigned \c int variables.
  12193. Access to the initial \c unsigned \c int variable is possible (though not recommended) by
  12194. <tt>(*this)._height</tt>.
  12195. **/
  12196. int height() const {
  12197. return (int)_height;
  12198. }
  12199. //! Return the number of image slices.
  12200. /**
  12201. Return the image depth, i.e. the image dimension along the Z-axis.
  12202. \note
  12203. - The depth() of an empty image is equal to \c 0.
  12204. - depth() is typically equal to \c 1 when considering usual 2d images. When depth()\c > \c 1, the image
  12205. is said to be \e volumetric.
  12206. - depth() returns an \c int, although the image depth is internally stored as an \c unsigned \c int.
  12207. Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
  12208. \c unsigned \c int variables.
  12209. Access to the initial \c unsigned \c int variable is possible (though not recommended) by
  12210. <tt>(*this)._depth</tt>.
  12211. **/
  12212. int depth() const {
  12213. return (int)_depth;
  12214. }
  12215. //! Return the number of image channels.
  12216. /**
  12217. Return the number of image channels, i.e. the image dimension along the C-axis.
  12218. \note
  12219. - The spectrum() of an empty image is equal to \c 0.
  12220. - spectrum() is typically equal to \c 1 when considering scalar-valued images, to \c 3
  12221. for RGB-coded color images, and to \c 4 for RGBA-coded color images (with alpha-channel).
  12222. The number of channels of an image instance is not limited. The meaning of the pixel values is not linked
  12223. up to the number of channels (e.g. a 4-channel image may indifferently stands for a RGBA or CMYK color image).
  12224. - spectrum() returns an \c int, although the image spectrum is internally stored as an \c unsigned \c int.
  12225. Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
  12226. \c unsigned \c int variables.
  12227. Access to the initial \c unsigned \c int variable is possible (though not recommended) by
  12228. <tt>(*this)._spectrum</tt>.
  12229. **/
  12230. int spectrum() const {
  12231. return (int)_spectrum;
  12232. }
  12233. //! Return the total number of pixel values.
  12234. /**
  12235. Return <tt>width()*\ref height()*\ref depth()*\ref spectrum()</tt>,
  12236. i.e. the total number of values of type \c T in the pixel buffer of the image instance.
  12237. \note
  12238. - The size() of an empty image is equal to \c 0.
  12239. - The allocated memory size for a pixel buffer of a non-shared \c CImg<T> instance is equal to
  12240. <tt>size()*sizeof(T)</tt>.
  12241. \par Example
  12242. \code
  12243. const CImg<float> img(100,100,1,3); // Construct new 100x100 color image.
  12244. if (img.size()==30000) // Test succeeds.
  12245. std::printf("Pixel buffer uses %lu bytes",
  12246. img.size()*sizeof(float));
  12247. \endcode
  12248. **/
  12249. ulongT size() const {
  12250. return (ulongT)_width*_height*_depth*_spectrum;
  12251. }
  12252. //! Return a pointer to the first pixel value.
  12253. /**
  12254. Return a \c T*, or a \c const \c T* pointer to the first value in the pixel buffer of the image instance,
  12255. whether the instance is \c const or not.
  12256. \note
  12257. - The data() of an empty image is equal to \c 0 (null pointer).
  12258. - The allocated pixel buffer for the image instance starts from \c data()
  12259. and goes to <tt>data()+\ref size() - 1</tt> (included).
  12260. - To get the pointer to one particular location of the pixel buffer, use
  12261. data(unsigned int,unsigned int,unsigned int,unsigned int) instead.
  12262. **/
  12263. T* data() {
  12264. return _data;
  12265. }
  12266. //! Return a pointer to the first pixel value \const.
  12267. const T* data() const {
  12268. return _data;
  12269. }
  12270. //! Return a pointer to a located pixel value.
  12271. /**
  12272. 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
  12273. of the image instance,
  12274. whether the instance is \c const or not.
  12275. \param x X-coordinate of the pixel value.
  12276. \param y Y-coordinate of the pixel value.
  12277. \param z Z-coordinate of the pixel value.
  12278. \param c C-coordinate of the pixel value.
  12279. \note
  12280. - Writing \c img.data(x,y,z,c) is equivalent to <tt>&(img(x,y,z,c))</tt>. Thus, this method has the same
  12281. properties as operator()(unsigned int,unsigned int,unsigned int,unsigned int).
  12282. **/
  12283. #if cimg_verbosity>=3
  12284. T *data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) {
  12285. const ulongT off = (ulongT)offset(x,y,z,c);
  12286. if (off>=size())
  12287. cimg::warn(_cimg_instance
  12288. "data(): Invalid pointer request, at coordinates (%u,%u,%u,%u) [offset=%u].",
  12289. cimg_instance,
  12290. x,y,z,c,off);
  12291. return _data + off;
  12292. }
  12293. //! Return a pointer to a located pixel value \const.
  12294. const T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const {
  12295. return const_cast<CImg<T>*>(this)->data(x,y,z,c);
  12296. }
  12297. #else
  12298. T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) {
  12299. return _data + x + (ulongT)y*_width + (ulongT)z*_width*_height + (ulongT)c*_width*_height*_depth;
  12300. }
  12301. const T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const {
  12302. return _data + x + (ulongT)y*_width + (ulongT)z*_width*_height + (ulongT)c*_width*_height*_depth;
  12303. }
  12304. #endif
  12305. //! Return the offset to a located pixel value, with respect to the beginning of the pixel buffer.
  12306. /**
  12307. \param x X-coordinate of the pixel value.
  12308. \param y Y-coordinate of the pixel value.
  12309. \param z Z-coordinate of the pixel value.
  12310. \param c C-coordinate of the pixel value.
  12311. \note
  12312. - Writing \c img.data(x,y,z,c) is equivalent to <tt>&(img(x,y,z,c)) - img.data()</tt>.
  12313. Thus, this method has the same properties as operator()(unsigned int,unsigned int,unsigned int,unsigned int).
  12314. \par Example
  12315. \code
  12316. const CImg<float> img(100,100,1,3); // Define a 100x100 RGB-color image.
  12317. const long off = img.offset(10,10,0,2); // Get the offset of the blue value of the pixel located at (10,10).
  12318. const float val = img[off]; // Get the blue value of this pixel.
  12319. \endcode
  12320. **/
  12321. longT offset(const int x, const int y=0, const int z=0, const int c=0) const {
  12322. return x + (longT)y*_width + (longT)z*_width*_height + (longT)c*_width*_height*_depth;
  12323. }
  12324. //! Return a CImg<T>::iterator pointing to the first pixel value.
  12325. /**
  12326. \note
  12327. - Equivalent to data().
  12328. - It has been mainly defined for compatibility with STL naming conventions.
  12329. **/
  12330. iterator begin() {
  12331. return _data;
  12332. }
  12333. //! Return a CImg<T>::iterator pointing to the first value of the pixel buffer \const.
  12334. const_iterator begin() const {
  12335. return _data;
  12336. }
  12337. //! Return a CImg<T>::iterator pointing next to the last pixel value.
  12338. /**
  12339. \note
  12340. - Writing \c img.end() is equivalent to <tt>img.data() + img.size()</tt>.
  12341. - It has been mainly defined for compatibility with STL naming conventions.
  12342. \warning
  12343. - The returned iterator actually points to a value located \e outside the acceptable bounds of the pixel buffer.
  12344. Trying to read or write the content of the returned iterator will probably result in a crash.
  12345. Use it mainly as a strict upper bound for a CImg<T>::iterator.
  12346. \par Example
  12347. \code
  12348. CImg<float> img(100,100,1,3); // Define a 100x100 RGB color image.
  12349. // 'img.end()' used below as an upper bound for the iterator.
  12350. for (CImg<float>::iterator it = img.begin(); it<img.end(); ++it)
  12351. *it = 0;
  12352. \endcode
  12353. **/
  12354. iterator end() {
  12355. return _data + size();
  12356. }
  12357. //! Return a CImg<T>::iterator pointing next to the last pixel value \const.
  12358. const_iterator end() const {
  12359. return _data + size();
  12360. }
  12361. //! Return a reference to the first pixel value.
  12362. /**
  12363. \note
  12364. - Writing \c img.front() is equivalent to <tt>img[0]</tt>, or <tt>img(0,0,0,0)</tt>.
  12365. - It has been mainly defined for compatibility with STL naming conventions.
  12366. **/
  12367. T& front() {
  12368. return *_data;
  12369. }
  12370. //! Return a reference to the first pixel value \const.
  12371. const T& front() const {
  12372. return *_data;
  12373. }
  12374. //! Return a reference to the last pixel value.
  12375. /**
  12376. \note
  12377. - Writing \c img.back() is equivalent to <tt>img[img.size() - 1]</tt>, or
  12378. <tt>img(img.width() - 1,img.height() - 1,img.depth() - 1,img.spectrum() - 1)</tt>.
  12379. - It has been mainly defined for compatibility with STL naming conventions.
  12380. **/
  12381. T& back() {
  12382. return *(_data + size() - 1);
  12383. }
  12384. //! Return a reference to the last pixel value \const.
  12385. const T& back() const {
  12386. return *(_data + size() - 1);
  12387. }
  12388. //! Access to a pixel value at a specified offset, using Dirichlet boundary conditions.
  12389. /**
  12390. Return a reference to the pixel value of the image instance located at a specified \c offset,
  12391. or to a specified default value in case of out-of-bounds access.
  12392. \param offset Offset to the desired pixel value.
  12393. \param out_value Default value returned if \c offset is outside image bounds.
  12394. \note
  12395. - Writing \c img.at(offset,out_value) is similar to <tt>img[offset]</tt>, except that if \c offset
  12396. is outside bounds (e.g. \c offset<0 or \c offset>=img.size()), a reference to a value \c out_value
  12397. is safely returned instead.
  12398. - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
  12399. you are \e not sure about the validity of the specified pixel offset.
  12400. **/
  12401. T& at(const int offset, const T& out_value) {
  12402. return (offset<0 || offset>=(int)size())?(cimg::temporary(out_value)=out_value):(*this)[offset];
  12403. }
  12404. //! Access to a pixel value at a specified offset, using Dirichlet boundary conditions \const.
  12405. T at(const int offset, const T& out_value) const {
  12406. return (offset<0 || offset>=(int)size())?out_value:(*this)[offset];
  12407. }
  12408. //! Access to a pixel value at a specified offset, using Neumann boundary conditions.
  12409. /**
  12410. Return a reference to the pixel value of the image instance located at a specified \c offset,
  12411. or to the nearest pixel location in the image instance in case of out-of-bounds access.
  12412. \param offset Offset to the desired pixel value.
  12413. \note
  12414. - Similar to at(int,const T), except that an out-of-bounds access returns the value of the
  12415. nearest pixel in the image instance, regarding the specified offset, i.e.
  12416. - If \c offset<0, then \c img[0] is returned.
  12417. - If \c offset>=img.size(), then \c img[img.size() - 1] is returned.
  12418. - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
  12419. you are \e not sure about the validity of the specified pixel offset.
  12420. - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _at(int).
  12421. **/
  12422. T& at(const int offset) {
  12423. if (is_empty())
  12424. throw CImgInstanceException(_cimg_instance
  12425. "at(): Empty instance.",
  12426. cimg_instance);
  12427. return _at(offset);
  12428. }
  12429. T& _at(const int offset) {
  12430. const unsigned int siz = (unsigned int)size();
  12431. return (*this)[offset<0?0:(unsigned int)offset>=siz?siz - 1:offset];
  12432. }
  12433. //! Access to a pixel value at a specified offset, using Neumann boundary conditions \const.
  12434. const T& at(const int offset) const {
  12435. if (is_empty())
  12436. throw CImgInstanceException(_cimg_instance
  12437. "at(): Empty instance.",
  12438. cimg_instance);
  12439. return _at(offset);
  12440. }
  12441. const T& _at(const int offset) const {
  12442. const unsigned int siz = (unsigned int)size();
  12443. return (*this)[offset<0?0:(unsigned int)offset>=siz?siz - 1:offset];
  12444. }
  12445. //! Access to a pixel value, using Dirichlet boundary conditions for the X-coordinate.
  12446. /**
  12447. Return a reference to the pixel value of the image instance located at (\c x,\c y,\c z,\c c),
  12448. or to a specified default value in case of out-of-bounds access along the X-axis.
  12449. \param x X-coordinate of the pixel value.
  12450. \param y Y-coordinate of the pixel value.
  12451. \param z Z-coordinate of the pixel value.
  12452. \param c C-coordinate of the pixel value.
  12453. \param out_value Default value returned if \c (\c x,\c y,\c z,\c c) is outside image bounds.
  12454. \note
  12455. - Similar to operator()(), except that an out-of-bounds access along the X-axis returns the specified value
  12456. \c out_value.
  12457. - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
  12458. you are \e not sure about the validity of the specified pixel coordinates.
  12459. \warning
  12460. - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
  12461. **/
  12462. T& atX(const int x, const int y, const int z, const int c, const T& out_value) {
  12463. return (x<0 || x>=width())?(cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
  12464. }
  12465. //! Access to a pixel value, using Dirichlet boundary conditions for the X-coordinate \const.
  12466. T atX(const int x, const int y, const int z, const int c, const T& out_value) const {
  12467. return (x<0 || x>=width())?out_value:(*this)(x,y,z,c);
  12468. }
  12469. //! Access to a pixel value, using Neumann boundary conditions for the X-coordinate.
  12470. /**
  12471. Return a reference to the pixel value of the image instance located at (\c x,\c y,\c z,\c c),
  12472. or to the nearest pixel location in the image instance in case of out-of-bounds access along the X-axis.
  12473. \param x X-coordinate of the pixel value.
  12474. \param y Y-coordinate of the pixel value.
  12475. \param z Z-coordinate of the pixel value.
  12476. \param c C-coordinate of the pixel value.
  12477. \note
  12478. - Similar to at(int,int,int,int,const T), except that an out-of-bounds access returns the value of the
  12479. nearest pixel in the image instance, regarding the specified X-coordinate.
  12480. - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
  12481. you are \e not sure about the validity of the specified pixel coordinates.
  12482. - If you know your image instance is \e not empty, you may rather use the slightly faster method
  12483. \c _at(int,int,int,int).
  12484. \warning
  12485. - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
  12486. **/
  12487. T& atX(const int x, const int y=0, const int z=0, const int c=0) {
  12488. if (is_empty())
  12489. throw CImgInstanceException(_cimg_instance
  12490. "atX(): Empty instance.",
  12491. cimg_instance);
  12492. return _atX(x,y,z,c);
  12493. }
  12494. T& _atX(const int x, const int y=0, const int z=0, const int c=0) {
  12495. return (*this)(x<0?0:(x>=width()?width() - 1:x),y,z,c);
  12496. }
  12497. //! Access to a pixel value, using Neumann boundary conditions for the X-coordinate \const.
  12498. const T& atX(const int x, const int y=0, const int z=0, const int c=0) const {
  12499. if (is_empty())
  12500. throw CImgInstanceException(_cimg_instance
  12501. "atX(): Empty instance.",
  12502. cimg_instance);
  12503. return _atX(x,y,z,c);
  12504. }
  12505. const T& _atX(const int x, const int y=0, const int z=0, const int c=0) const {
  12506. return (*this)(x<0?0:(x>=width()?width() - 1:x),y,z,c);
  12507. }
  12508. //! Access to a pixel value, using Dirichlet boundary conditions for the X and Y-coordinates.
  12509. /**
  12510. Similar to atX(int,int,int,int,const T), except that boundary checking is performed both on X and Y-coordinates.
  12511. **/
  12512. T& atXY(const int x, const int y, const int z, const int c, const T& out_value) {
  12513. return (x<0 || y<0 || x>=width() || y>=height())?(cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
  12514. }
  12515. //! Access to a pixel value, using Dirichlet boundary conditions for the X and Y coordinates \const.
  12516. T atXY(const int x, const int y, const int z, const int c, const T& out_value) const {
  12517. return (x<0 || y<0 || x>=width() || y>=height())?out_value:(*this)(x,y,z,c);
  12518. }
  12519. //! Access to a pixel value, using Neumann boundary conditions for the X and Y-coordinates.
  12520. /**
  12521. Similar to atX(int,int,int,int), except that boundary checking is performed both on X and Y-coordinates.
  12522. \note
  12523. - If you know your image instance is \e not empty, you may rather use the slightly faster method
  12524. \c _atXY(int,int,int,int).
  12525. **/
  12526. T& atXY(const int x, const int y, const int z=0, const int c=0) {
  12527. if (is_empty())
  12528. throw CImgInstanceException(_cimg_instance
  12529. "atXY(): Empty instance.",
  12530. cimg_instance);
  12531. return _atXY(x,y,z,c);
  12532. }
  12533. T& _atXY(const int x, const int y, const int z=0, const int c=0) {
  12534. return (*this)(cimg::cut(x,0,width() - 1),
  12535. cimg::cut(y,0,height() - 1),z,c);
  12536. }
  12537. //! Access to a pixel value, using Neumann boundary conditions for the X and Y-coordinates \const.
  12538. const T& atXY(const int x, const int y, const int z=0, const int c=0) const {
  12539. if (is_empty())
  12540. throw CImgInstanceException(_cimg_instance
  12541. "atXY(): Empty instance.",
  12542. cimg_instance);
  12543. return _atXY(x,y,z,c);
  12544. }
  12545. const T& _atXY(const int x, const int y, const int z=0, const int c=0) const {
  12546. return (*this)(cimg::cut(x,0,width() - 1),
  12547. cimg::cut(y,0,height() - 1),z,c);
  12548. }
  12549. //! Access to a pixel value, using Dirichlet boundary conditions for the X,Y and Z-coordinates.
  12550. /**
  12551. Similar to atX(int,int,int,int,const T), except that boundary checking is performed both on
  12552. X,Y and Z-coordinates.
  12553. **/
  12554. T& atXYZ(const int x, const int y, const int z, const int c, const T& out_value) {
  12555. return (x<0 || y<0 || z<0 || x>=width() || y>=height() || z>=depth())?
  12556. (cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
  12557. }
  12558. //! Access to a pixel value, using Dirichlet boundary conditions for the X,Y and Z-coordinates \const.
  12559. T atXYZ(const int x, const int y, const int z, const int c, const T& out_value) const {
  12560. return (x<0 || y<0 || z<0 || x>=width() || y>=height() || z>=depth())?out_value:(*this)(x,y,z,c);
  12561. }
  12562. //! Access to a pixel value, using Neumann boundary conditions for the X,Y and Z-coordinates.
  12563. /**
  12564. Similar to atX(int,int,int,int), except that boundary checking is performed both on X,Y and Z-coordinates.
  12565. \note
  12566. - If you know your image instance is \e not empty, you may rather use the slightly faster method
  12567. \c _atXYZ(int,int,int,int).
  12568. **/
  12569. T& atXYZ(const int x, const int y, const int z, const int c=0) {
  12570. if (is_empty())
  12571. throw CImgInstanceException(_cimg_instance
  12572. "atXYZ(): Empty instance.",
  12573. cimg_instance);
  12574. return _atXYZ(x,y,z,c);
  12575. }
  12576. T& _atXYZ(const int x, const int y, const int z, const int c=0) {
  12577. return (*this)(cimg::cut(x,0,width() - 1),
  12578. cimg::cut(y,0,height() - 1),
  12579. cimg::cut(z,0,depth() - 1),c);
  12580. }
  12581. //! Access to a pixel value, using Neumann boundary conditions for the X,Y and Z-coordinates \const.
  12582. const T& atXYZ(const int x, const int y, const int z, const int c=0) const {
  12583. if (is_empty())
  12584. throw CImgInstanceException(_cimg_instance
  12585. "atXYZ(): Empty instance.",
  12586. cimg_instance);
  12587. return _atXYZ(x,y,z,c);
  12588. }
  12589. const T& _atXYZ(const int x, const int y, const int z, const int c=0) const {
  12590. return (*this)(cimg::cut(x,0,width() - 1),
  12591. cimg::cut(y,0,height() - 1),
  12592. cimg::cut(z,0,depth() - 1),c);
  12593. }
  12594. //! Access to a pixel value, using Dirichlet boundary conditions.
  12595. /**
  12596. Similar to atX(int,int,int,int,const T), except that boundary checking is performed on all
  12597. X,Y,Z and C-coordinates.
  12598. **/
  12599. T& atXYZC(const int x, const int y, const int z, const int c, const T& out_value) {
  12600. return (x<0 || y<0 || z<0 || c<0 || x>=width() || y>=height() || z>=depth() || c>=spectrum())?
  12601. (cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
  12602. }
  12603. //! Access to a pixel value, using Dirichlet boundary conditions \const.
  12604. T atXYZC(const int x, const int y, const int z, const int c, const T& out_value) const {
  12605. return (x<0 || y<0 || z<0 || c<0 || x>=width() || y>=height() || z>=depth() || c>=spectrum())?out_value:
  12606. (*this)(x,y,z,c);
  12607. }
  12608. //! Access to a pixel value, using Neumann boundary conditions.
  12609. /**
  12610. Similar to atX(int,int,int,int), except that boundary checking is performed on all X,Y,Z and C-coordinates.
  12611. \note
  12612. - If you know your image instance is \e not empty, you may rather use the slightly faster method
  12613. \c _atXYZC(int,int,int,int).
  12614. **/
  12615. T& atXYZC(const int x, const int y, const int z, const int c) {
  12616. if (is_empty())
  12617. throw CImgInstanceException(_cimg_instance
  12618. "atXYZC(): Empty instance.",
  12619. cimg_instance);
  12620. return _atXYZC(x,y,z,c);
  12621. }
  12622. T& _atXYZC(const int x, const int y, const int z, const int c) {
  12623. return (*this)(cimg::cut(x,0,width() - 1),
  12624. cimg::cut(y,0,height() - 1),
  12625. cimg::cut(z,0,depth() - 1),
  12626. cimg::cut(c,0,spectrum() - 1));
  12627. }
  12628. //! Access to a pixel value, using Neumann boundary conditions \const.
  12629. const T& atXYZC(const int x, const int y, const int z, const int c) const {
  12630. if (is_empty())
  12631. throw CImgInstanceException(_cimg_instance
  12632. "atXYZC(): Empty instance.",
  12633. cimg_instance);
  12634. return _atXYZC(x,y,z,c);
  12635. }
  12636. const T& _atXYZC(const int x, const int y, const int z, const int c) const {
  12637. return (*this)(cimg::cut(x,0,width() - 1),
  12638. cimg::cut(y,0,height() - 1),
  12639. cimg::cut(z,0,depth() - 1),
  12640. cimg::cut(c,0,spectrum() - 1));
  12641. }
  12642. //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X-coordinate.
  12643. /**
  12644. Return a linearly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
  12645. or a specified default value in case of out-of-bounds access along the X-axis.
  12646. \param fx X-coordinate of the pixel value (float-valued).
  12647. \param y Y-coordinate of the pixel value.
  12648. \param z Z-coordinate of the pixel value.
  12649. \param c C-coordinate of the pixel value.
  12650. \param out_value Default value returned if \c (\c fx,\c y,\c z,\c c) is outside image bounds.
  12651. \note
  12652. - Similar to atX(int,int,int,int,const T), except that the returned pixel value is approximated by
  12653. a linear interpolation along the X-axis, if corresponding coordinates are not integers.
  12654. - The type of the returned pixel value is extended to \c float, if the pixel type \c T is not float-valued.
  12655. \warning
  12656. - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
  12657. **/
  12658. Tfloat linear_atX(const float fx, const int y, const int z, const int c, const T& out_value) const {
  12659. const int
  12660. x = (int)fx - (fx>=0?0:1), nx = x + 1;
  12661. const float
  12662. dx = fx - x;
  12663. const Tfloat
  12664. Ic = (Tfloat)atX(x,y,z,c,out_value), In = (Tfloat)atXY(nx,y,z,c,out_value);
  12665. return Ic + dx*(In - Ic);
  12666. }
  12667. //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X-coordinate.
  12668. /**
  12669. Return a linearly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
  12670. or the value of the nearest pixel location in the image instance in case of out-of-bounds access along
  12671. the X-axis.
  12672. \param fx X-coordinate of the pixel value (float-valued).
  12673. \param y Y-coordinate of the pixel value.
  12674. \param z Z-coordinate of the pixel value.
  12675. \param c C-coordinate of the pixel value.
  12676. \note
  12677. - Similar to linear_atX(float,int,int,int,const T) const, except that an out-of-bounds access returns
  12678. the value of the nearest pixel in the image instance, regarding the specified X-coordinate.
  12679. - If you know your image instance is \e not empty, you may rather use the slightly faster method
  12680. \c _linear_atX(float,int,int,int).
  12681. \warning
  12682. - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
  12683. **/
  12684. Tfloat linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
  12685. if (is_empty())
  12686. throw CImgInstanceException(_cimg_instance
  12687. "linear_atX(): Empty instance.",
  12688. cimg_instance);
  12689. return _linear_atX(fx,y,z,c);
  12690. }
  12691. Tfloat _linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
  12692. const float
  12693. nfx = cimg::cut(fx,0,width() - 1);
  12694. const unsigned int
  12695. x = (unsigned int)nfx;
  12696. const float
  12697. dx = nfx - x;
  12698. const unsigned int
  12699. nx = dx>0?x + 1:x;
  12700. const Tfloat
  12701. Ic = (Tfloat)(*this)(x,y,z,c), In = (Tfloat)(*this)(nx,y,z,c);
  12702. return Ic + dx*(In - Ic);
  12703. }
  12704. //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X and Y-coordinates.
  12705. /**
  12706. Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the
  12707. boundary checking are achieved both for X and Y-coordinates.
  12708. **/
  12709. Tfloat linear_atXY(const float fx, const float fy, const int z, const int c, const T& out_value) const {
  12710. const int
  12711. x = (int)fx - (fx>=0?0:1), nx = x + 1,
  12712. y = (int)fy - (fy>=0?0:1), ny = y + 1;
  12713. const float
  12714. dx = fx - x,
  12715. dy = fy - y;
  12716. const Tfloat
  12717. Icc = (Tfloat)atXY(x,y,z,c,out_value), Inc = (Tfloat)atXY(nx,y,z,c,out_value),
  12718. Icn = (Tfloat)atXY(x,ny,z,c,out_value), Inn = (Tfloat)atXY(nx,ny,z,c,out_value);
  12719. return Icc + dx*(Inc - Icc + dy*(Icc + Inn - Icn - Inc)) + dy*(Icn - Icc);
  12720. }
  12721. //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X and Y-coordinates.
  12722. /**
  12723. Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking
  12724. are achieved both for X and Y-coordinates.
  12725. \note
  12726. - If you know your image instance is \e not empty, you may rather use the slightly faster method
  12727. \c _linear_atXY(float,float,int,int).
  12728. **/
  12729. Tfloat linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
  12730. if (is_empty())
  12731. throw CImgInstanceException(_cimg_instance
  12732. "linear_atXY(): Empty instance.",
  12733. cimg_instance);
  12734. return _linear_atXY(fx,fy,z,c);
  12735. }
  12736. Tfloat _linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
  12737. const float
  12738. nfx = cimg::cut(fx,0,width() - 1),
  12739. nfy = cimg::cut(fy,0,height() - 1);
  12740. const unsigned int
  12741. x = (unsigned int)nfx,
  12742. y = (unsigned int)nfy;
  12743. const float
  12744. dx = nfx - x,
  12745. dy = nfy - y;
  12746. const unsigned int
  12747. nx = dx>0?x + 1:x,
  12748. ny = dy>0?y + 1:y;
  12749. const Tfloat
  12750. Icc = (Tfloat)(*this)(x,y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c),
  12751. Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c);
  12752. return Icc + dx*(Inc - Icc + dy*(Icc + Inn - Icn - Inc)) + dy*(Icn - Icc);
  12753. }
  12754. //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates.
  12755. /**
  12756. Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the
  12757. boundary checking are achieved both for X,Y and Z-coordinates.
  12758. **/
  12759. Tfloat linear_atXYZ(const float fx, const float fy, const float fz, const int c, const T& out_value) const {
  12760. const int
  12761. x = (int)fx - (fx>=0?0:1), nx = x + 1,
  12762. y = (int)fy - (fy>=0?0:1), ny = y + 1,
  12763. z = (int)fz - (fz>=0?0:1), nz = z + 1;
  12764. const float
  12765. dx = fx - x,
  12766. dy = fy - y,
  12767. dz = fz - z;
  12768. const Tfloat
  12769. Iccc = (Tfloat)atXYZ(x,y,z,c,out_value), Incc = (Tfloat)atXYZ(nx,y,z,c,out_value),
  12770. Icnc = (Tfloat)atXYZ(x,ny,z,c,out_value), Innc = (Tfloat)atXYZ(nx,ny,z,c,out_value),
  12771. Iccn = (Tfloat)atXYZ(x,y,nz,c,out_value), Incn = (Tfloat)atXYZ(nx,y,nz,c,out_value),
  12772. Icnn = (Tfloat)atXYZ(x,ny,nz,c,out_value), Innn = (Tfloat)atXYZ(nx,ny,nz,c,out_value);
  12773. return Iccc +
  12774. dx*(Incc - Iccc +
  12775. dy*(Iccc + Innc - Icnc - Incc +
  12776. dz*(Iccn + Innn + Icnc + Incc - Icnn - Incn - Iccc - Innc)) +
  12777. dz*(Iccc + Incn - Iccn - Incc)) +
  12778. dy*(Icnc - Iccc +
  12779. dz*(Iccc + Icnn - Iccn - Icnc)) +
  12780. dz*(Iccn - Iccc);
  12781. }
  12782. //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X,Y and Z-coordinates.
  12783. /**
  12784. Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking
  12785. are achieved both for X,Y and Z-coordinates.
  12786. \note
  12787. - If you know your image instance is \e not empty, you may rather use the slightly faster method
  12788. \c _linear_atXYZ(float,float,float,int).
  12789. **/
  12790. Tfloat linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const {
  12791. if (is_empty())
  12792. throw CImgInstanceException(_cimg_instance
  12793. "linear_atXYZ(): Empty instance.",
  12794. cimg_instance);
  12795. return _linear_atXYZ(fx,fy,fz,c);
  12796. }
  12797. Tfloat _linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const {
  12798. const float
  12799. nfx = cimg::cut(fx,0,width() - 1),
  12800. nfy = cimg::cut(fy,0,height() - 1),
  12801. nfz = cimg::cut(fz,0,depth() - 1);
  12802. const unsigned int
  12803. x = (unsigned int)nfx,
  12804. y = (unsigned int)nfy,
  12805. z = (unsigned int)nfz;
  12806. const float
  12807. dx = nfx - x,
  12808. dy = nfy - y,
  12809. dz = nfz - z;
  12810. const unsigned int
  12811. nx = dx>0?x + 1:x,
  12812. ny = dy>0?y + 1:y,
  12813. nz = dz>0?z + 1:z;
  12814. const Tfloat
  12815. Iccc = (Tfloat)(*this)(x,y,z,c), Incc = (Tfloat)(*this)(nx,y,z,c),
  12816. Icnc = (Tfloat)(*this)(x,ny,z,c), Innc = (Tfloat)(*this)(nx,ny,z,c),
  12817. Iccn = (Tfloat)(*this)(x,y,nz,c), Incn = (Tfloat)(*this)(nx,y,nz,c),
  12818. Icnn = (Tfloat)(*this)(x,ny,nz,c), Innn = (Tfloat)(*this)(nx,ny,nz,c);
  12819. return Iccc +
  12820. dx*(Incc - Iccc +
  12821. dy*(Iccc + Innc - Icnc - Incc +
  12822. dz*(Iccn + Innn + Icnc + Incc - Icnn - Incn - Iccc - Innc)) +
  12823. dz*(Iccc + Incn - Iccn - Incc)) +
  12824. dy*(Icnc - Iccc +
  12825. dz*(Iccc + Icnn - Iccn - Icnc)) +
  12826. dz*(Iccn - Iccc);
  12827. }
  12828. //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for all X,Y,Z,C-coordinates.
  12829. /**
  12830. Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the
  12831. boundary checking are achieved for all X,Y,Z and C-coordinates.
  12832. **/
  12833. Tfloat linear_atXYZC(const float fx, const float fy, const float fz, const float fc, const T& out_value) const {
  12834. const int
  12835. x = (int)fx - (fx>=0?0:1), nx = x + 1,
  12836. y = (int)fy - (fy>=0?0:1), ny = y + 1,
  12837. z = (int)fz - (fz>=0?0:1), nz = z + 1,
  12838. c = (int)fc - (fc>=0?0:1), nc = c + 1;
  12839. const float
  12840. dx = fx - x,
  12841. dy = fy - y,
  12842. dz = fz - z,
  12843. dc = fc - c;
  12844. const Tfloat
  12845. Icccc = (Tfloat)atXYZC(x,y,z,c,out_value), Inccc = (Tfloat)atXYZC(nx,y,z,c,out_value),
  12846. Icncc = (Tfloat)atXYZC(x,ny,z,c,out_value), Inncc = (Tfloat)atXYZC(nx,ny,z,c,out_value),
  12847. Iccnc = (Tfloat)atXYZC(x,y,nz,c,out_value), Incnc = (Tfloat)atXYZC(nx,y,nz,c,out_value),
  12848. Icnnc = (Tfloat)atXYZC(x,ny,nz,c,out_value), Innnc = (Tfloat)atXYZC(nx,ny,nz,c,out_value),
  12849. Icccn = (Tfloat)atXYZC(x,y,z,nc,out_value), Inccn = (Tfloat)atXYZC(nx,y,z,nc,out_value),
  12850. Icncn = (Tfloat)atXYZC(x,ny,z,nc,out_value), Inncn = (Tfloat)atXYZC(nx,ny,z,nc,out_value),
  12851. Iccnn = (Tfloat)atXYZC(x,y,nz,nc,out_value), Incnn = (Tfloat)atXYZC(nx,y,nz,nc,out_value),
  12852. Icnnn = (Tfloat)atXYZC(x,ny,nz,nc,out_value), Innnn = (Tfloat)atXYZC(nx,ny,nz,nc,out_value);
  12853. return Icccc +
  12854. dx*(Inccc - Icccc +
  12855. dy*(Icccc + Inncc - Icncc - Inccc +
  12856. dz*(Iccnc + Innnc + Icncc + Inccc - Icnnc - Incnc - Icccc - Inncc +
  12857. dc*(Iccnn + Innnn + Icncn + Inccn + Icnnc + Incnc + Icccc + Inncc -
  12858. Icnnn - Incnn - Icccn - Inncn - Iccnc - Innnc - Icncc - Inccc)) +
  12859. dc*(Icccn + Inncn + Icncc + Inccc - Icncn - Inccn - Icccc - Inncc)) +
  12860. dz*(Icccc + Incnc - Iccnc - Inccc +
  12861. dc*(Icccn + Incnn + Iccnc + Inccc - Iccnn - Inccn - Icccc - Incnc)) +
  12862. dc*(Icccc + Inccn - Inccc - Icccn)) +
  12863. dy*(Icncc - Icccc +
  12864. dz*(Icccc + Icnnc - Iccnc - Icncc +
  12865. dc*(Icccn + Icnnn + Iccnc + Icncc - Iccnn - Icncn - Icccc - Icnnc)) +
  12866. dc*(Icccc + Icncn - Icncc - Icccn)) +
  12867. dz*(Iccnc - Icccc +
  12868. dc*(Icccc + Iccnn - Iccnc - Icccn)) +
  12869. dc*(Icccn -Icccc);
  12870. }
  12871. //! Return pixel value, using linear interpolation and Neumann boundary conditions for all X,Y,Z and C-coordinates.
  12872. /**
  12873. Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking
  12874. are achieved for all X,Y,Z and C-coordinates.
  12875. \note
  12876. - If you know your image instance is \e not empty, you may rather use the slightly faster method
  12877. \c _linear_atXYZC(float,float,float,float).
  12878. **/
  12879. Tfloat linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const {
  12880. if (is_empty())
  12881. throw CImgInstanceException(_cimg_instance
  12882. "linear_atXYZC(): Empty instance.",
  12883. cimg_instance);
  12884. return _linear_atXYZC(fx,fy,fz,fc);
  12885. }
  12886. Tfloat _linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const {
  12887. const float
  12888. nfx = cimg::cut(fx,0,width() - 1),
  12889. nfy = cimg::cut(fy,0,height() - 1),
  12890. nfz = cimg::cut(fz,0,depth() - 1),
  12891. nfc = cimg::cut(fc,0,spectrum() - 1);
  12892. const unsigned int
  12893. x = (unsigned int)nfx,
  12894. y = (unsigned int)nfy,
  12895. z = (unsigned int)nfz,
  12896. c = (unsigned int)nfc;
  12897. const float
  12898. dx = nfx - x,
  12899. dy = nfy - y,
  12900. dz = nfz - z,
  12901. dc = nfc - c;
  12902. const unsigned int
  12903. nx = dx>0?x + 1:x,
  12904. ny = dy>0?y + 1:y,
  12905. nz = dz>0?z + 1:z,
  12906. nc = dc>0?c + 1:c;
  12907. const Tfloat
  12908. Icccc = (Tfloat)(*this)(x,y,z,c), Inccc = (Tfloat)(*this)(nx,y,z,c),
  12909. Icncc = (Tfloat)(*this)(x,ny,z,c), Inncc = (Tfloat)(*this)(nx,ny,z,c),
  12910. Iccnc = (Tfloat)(*this)(x,y,nz,c), Incnc = (Tfloat)(*this)(nx,y,nz,c),
  12911. Icnnc = (Tfloat)(*this)(x,ny,nz,c), Innnc = (Tfloat)(*this)(nx,ny,nz,c),
  12912. Icccn = (Tfloat)(*this)(x,y,z,nc), Inccn = (Tfloat)(*this)(nx,y,z,nc),
  12913. Icncn = (Tfloat)(*this)(x,ny,z,nc), Inncn = (Tfloat)(*this)(nx,ny,z,nc),
  12914. Iccnn = (Tfloat)(*this)(x,y,nz,nc), Incnn = (Tfloat)(*this)(nx,y,nz,nc),
  12915. Icnnn = (Tfloat)(*this)(x,ny,nz,nc), Innnn = (Tfloat)(*this)(nx,ny,nz,nc);
  12916. return Icccc +
  12917. dx*(Inccc - Icccc +
  12918. dy*(Icccc + Inncc - Icncc - Inccc +
  12919. dz*(Iccnc + Innnc + Icncc + Inccc - Icnnc - Incnc - Icccc - Inncc +
  12920. dc*(Iccnn + Innnn + Icncn + Inccn + Icnnc + Incnc + Icccc + Inncc -
  12921. Icnnn - Incnn - Icccn - Inncn - Iccnc - Innnc - Icncc - Inccc)) +
  12922. dc*(Icccn + Inncn + Icncc + Inccc - Icncn - Inccn - Icccc - Inncc)) +
  12923. dz*(Icccc + Incnc - Iccnc - Inccc +
  12924. dc*(Icccn + Incnn + Iccnc + Inccc - Iccnn - Inccn - Icccc - Incnc)) +
  12925. dc*(Icccc + Inccn - Inccc - Icccn)) +
  12926. dy*(Icncc - Icccc +
  12927. dz*(Icccc + Icnnc - Iccnc - Icncc +
  12928. dc*(Icccn + Icnnn + Iccnc + Icncc - Iccnn - Icncn - Icccc - Icnnc)) +
  12929. dc*(Icccc + Icncn - Icncc - Icccn)) +
  12930. dz*(Iccnc - Icccc +
  12931. dc*(Icccc + Iccnn - Iccnc - Icccn)) +
  12932. dc*(Icccn - Icccc);
  12933. }
  12934. //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X-coordinate.
  12935. /**
  12936. Return a cubicly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
  12937. or a specified default value in case of out-of-bounds access along the X-axis.
  12938. The cubic interpolation uses Hermite splines.
  12939. \param fx d X-coordinate of the pixel value (float-valued).
  12940. \param y Y-coordinate of the pixel value.
  12941. \param z Z-coordinate of the pixel value.
  12942. \param c C-coordinate of the pixel value.
  12943. \param out_value Default value returned if \c (\c fx,\c y,\c z,\c c) is outside image bounds.
  12944. \note
  12945. - Similar to linear_atX(float,int,int,int,const T) const, except that the returned pixel value is
  12946. approximated by a \e cubic interpolation along the X-axis.
  12947. - The type of the returned pixel value is extended to \c float, if the pixel type \c T is not float-valued.
  12948. \warning
  12949. - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
  12950. **/
  12951. Tfloat cubic_atX(const float fx, const int y, const int z, const int c, const T& out_value) const {
  12952. const int
  12953. x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2;
  12954. const float
  12955. dx = fx - x;
  12956. const Tfloat
  12957. Ip = (Tfloat)atX(px,y,z,c,out_value), Ic = (Tfloat)atX(x,y,z,c,out_value),
  12958. In = (Tfloat)atX(nx,y,z,c,out_value), Ia = (Tfloat)atX(ax,y,z,c,out_value);
  12959. 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));
  12960. }
  12961. //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X-coordinate.
  12962. /**
  12963. Similar to cubic_atX(float,int,int,int,const T) const, except that the return value is clamped to stay in the
  12964. min/max range of the datatype \c T.
  12965. **/
  12966. T cubic_cut_atX(const float fx, const int y, const int z, const int c, const T& out_value) const {
  12967. return cimg::type<T>::cut(cubic_atX(fx,y,z,c,out_value));
  12968. }
  12969. //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate.
  12970. /**
  12971. Return a cubicly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
  12972. or the value of the nearest pixel location in the image instance in case of out-of-bounds access
  12973. along the X-axis. The cubic interpolation uses Hermite splines.
  12974. \param fx X-coordinate of the pixel value (float-valued).
  12975. \param y Y-coordinate of the pixel value.
  12976. \param z Z-coordinate of the pixel value.
  12977. \param c C-coordinate of the pixel value.
  12978. \note
  12979. - Similar to cubic_atX(float,int,int,int,const T) const, except that the returned pixel value is
  12980. approximated by a cubic interpolation along the X-axis.
  12981. - If you know your image instance is \e not empty, you may rather use the slightly faster method
  12982. \c _cubic_atX(float,int,int,int).
  12983. \warning
  12984. - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
  12985. **/
  12986. Tfloat cubic_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
  12987. if (is_empty())
  12988. throw CImgInstanceException(_cimg_instance
  12989. "cubic_atX(): Empty instance.",
  12990. cimg_instance);
  12991. return _cubic_atX(fx,y,z,c);
  12992. }
  12993. Tfloat _cubic_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
  12994. const float
  12995. nfx = cimg::cut(fx,0,width() - 1);
  12996. const int
  12997. x = (int)nfx;
  12998. const float
  12999. dx = nfx - x;
  13000. const int
  13001. px = x - 1<0?0:x - 1, nx = dx>0?x + 1:x, ax = x + 2>=width()?width() - 1:x + 2;
  13002. const Tfloat
  13003. Ip = (Tfloat)(*this)(px,y,z,c), Ic = (Tfloat)(*this)(x,y,z,c),
  13004. In = (Tfloat)(*this)(nx,y,z,c), Ia = (Tfloat)(*this)(ax,y,z,c);
  13005. 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));
  13006. }
  13007. //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate.
  13008. /**
  13009. Similar to cubic_atX(float,int,int,int) const, except that the return value is clamped to stay in the
  13010. min/max range of the datatype \c T.
  13011. **/
  13012. T cubic_cut_atX(const float fx, const int y, const int z, const int c) const {
  13013. return cimg::type<T>::cut(cubic_atX(fx,y,z,c));
  13014. }
  13015. T _cubic_cut_atX(const float fx, const int y, const int z, const int c) const {
  13016. return cimg::type<T>::cut(_cubic_atX(fx,y,z,c));
  13017. }
  13018. //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X and Y-coordinates.
  13019. /**
  13020. Similar to cubic_atX(float,int,int,int,const T) const, except that the cubic interpolation and boundary checking
  13021. are achieved both for X and Y-coordinates.
  13022. **/
  13023. Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c, const T& out_value) const {
  13024. const int
  13025. x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2,
  13026. y = (int)fy - (fy>=0?0:1), py = y - 1, ny = y + 1, ay = y + 2;
  13027. const float dx = fx - x, dy = fy - y;
  13028. const Tfloat
  13029. Ipp = (Tfloat)atXY(px,py,z,c,out_value), Icp = (Tfloat)atXY(x,py,z,c,out_value),
  13030. Inp = (Tfloat)atXY(nx,py,z,c,out_value), Iap = (Tfloat)atXY(ax,py,z,c,out_value),
  13031. 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)),
  13032. Ipc = (Tfloat)atXY(px,y,z,c,out_value), Icc = (Tfloat)atXY(x, y,z,c,out_value),
  13033. Inc = (Tfloat)atXY(nx,y,z,c,out_value), Iac = (Tfloat)atXY(ax,y,z,c,out_value),
  13034. 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)),
  13035. Ipn = (Tfloat)atXY(px,ny,z,c,out_value), Icn = (Tfloat)atXY(x,ny,z,c,out_value),
  13036. Inn = (Tfloat)atXY(nx,ny,z,c,out_value), Ian = (Tfloat)atXY(ax,ny,z,c,out_value),
  13037. 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)),
  13038. Ipa = (Tfloat)atXY(px,ay,z,c,out_value), Ica = (Tfloat)atXY(x,ay,z,c,out_value),
  13039. Ina = (Tfloat)atXY(nx,ay,z,c,out_value), Iaa = (Tfloat)atXY(ax,ay,z,c,out_value),
  13040. 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));
  13041. 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));
  13042. }
  13043. //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X,Y-coordinates.
  13044. /**
  13045. Similar to cubic_atXY(float,float,int,int,const T) const, except that the return value is clamped to stay in the
  13046. min/max range of the datatype \c T.
  13047. **/
  13048. T cubic_cut_atXY(const float fx, const float fy, const int z, const int c, const T& out_value) const {
  13049. return cimg::type<T>::cut(cubic_atXY(fx,fy,z,c,out_value));
  13050. }
  13051. //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X and Y-coordinates.
  13052. /**
  13053. Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking
  13054. are achieved for both X and Y-coordinates.
  13055. \note
  13056. - If you know your image instance is \e not empty, you may rather use the slightly faster method
  13057. \c _cubic_atXY(float,float,int,int).
  13058. **/
  13059. Tfloat cubic_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
  13060. if (is_empty())
  13061. throw CImgInstanceException(_cimg_instance
  13062. "cubic_atXY(): Empty instance.",
  13063. cimg_instance);
  13064. return _cubic_atXY(fx,fy,z,c);
  13065. }
  13066. Tfloat _cubic_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
  13067. const float
  13068. nfx = cimg::cut(fx,0,width() - 1),
  13069. nfy = cimg::cut(fy,0,height() - 1);
  13070. const int x = (int)nfx, y = (int)nfy;
  13071. const float dx = nfx - x, dy = nfy - y;
  13072. const int
  13073. px = x - 1<0?0:x - 1, nx = dx>0?x + 1:x, ax = x + 2>=width()?width() - 1:x + 2,
  13074. py = y - 1<0?0:y - 1, ny = dy>0?y + 1:y, ay = y + 2>=height()?height() - 1:y + 2;
  13075. const Tfloat
  13076. Ipp = (Tfloat)(*this)(px,py,z,c), Icp = (Tfloat)(*this)(x,py,z,c), Inp = (Tfloat)(*this)(nx,py,z,c),
  13077. Iap = (Tfloat)(*this)(ax,py,z,c),
  13078. 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)),
  13079. Ipc = (Tfloat)(*this)(px,y,z,c), Icc = (Tfloat)(*this)(x, y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c),
  13080. Iac = (Tfloat)(*this)(ax,y,z,c),
  13081. 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)),
  13082. Ipn = (Tfloat)(*this)(px,ny,z,c), Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c),
  13083. Ian = (Tfloat)(*this)(ax,ny,z,c),
  13084. 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)),
  13085. Ipa = (Tfloat)(*this)(px,ay,z,c), Ica = (Tfloat)(*this)(x,ay,z,c), Ina = (Tfloat)(*this)(nx,ay,z,c),
  13086. Iaa = (Tfloat)(*this)(ax,ay,z,c),
  13087. 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));
  13088. 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));
  13089. }
  13090. //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y-coordinates.
  13091. /**
  13092. Similar to cubic_atXY(float,float,int,int) const, except that the return value is clamped to stay in the
  13093. min/max range of the datatype \c T.
  13094. **/
  13095. T cubic_cut_atXY(const float fx, const float fy, const int z, const int c) const {
  13096. return cimg::type<T>::cut(cubic_atXY(fx,fy,z,c));
  13097. }
  13098. T _cubic_cut_atXY(const float fx, const float fy, const int z, const int c) const {
  13099. return cimg::type<T>::cut(_cubic_atXY(fx,fy,z,c));
  13100. }
  13101. //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates.
  13102. /**
  13103. Similar to cubic_atX(float,int,int,int,const T) const, except that the cubic interpolation and boundary checking
  13104. are achieved both for X,Y and Z-coordinates.
  13105. **/
  13106. Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c, const T& out_value) const {
  13107. const int
  13108. x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2,
  13109. y = (int)fy - (fy>=0?0:1), py = y - 1, ny = y + 1, ay = y + 2,
  13110. z = (int)fz - (fz>=0?0:1), pz = z - 1, nz = z + 1, az = z + 2;
  13111. const float dx = fx - x, dy = fy - y, dz = fz - z;
  13112. const Tfloat
  13113. Ippp = (Tfloat)atXYZ(px,py,pz,c,out_value), Icpp = (Tfloat)atXYZ(x,py,pz,c,out_value),
  13114. Inpp = (Tfloat)atXYZ(nx,py,pz,c,out_value), Iapp = (Tfloat)atXYZ(ax,py,pz,c,out_value),
  13115. Ipp = Icpp + 0.5f*(dx*(-Ippp + Inpp) + dx*dx*(2*Ippp - 5*Icpp + 4*Inpp - Iapp) +
  13116. dx*dx*dx*(-Ippp + 3*Icpp - 3*Inpp + Iapp)),
  13117. Ipcp = (Tfloat)atXYZ(px,y,pz,c,out_value), Iccp = (Tfloat)atXYZ(x, y,pz,c,out_value),
  13118. Incp = (Tfloat)atXYZ(nx,y,pz,c,out_value), Iacp = (Tfloat)atXYZ(ax,y,pz,c,out_value),
  13119. Icp = Iccp + 0.5f*(dx*(-Ipcp + Incp) + dx*dx*(2*Ipcp - 5*Iccp + 4*Incp - Iacp) +
  13120. dx*dx*dx*(-Ipcp + 3*Iccp - 3*Incp + Iacp)),
  13121. Ipnp = (Tfloat)atXYZ(px,ny,pz,c,out_value), Icnp = (Tfloat)atXYZ(x,ny,pz,c,out_value),
  13122. Innp = (Tfloat)atXYZ(nx,ny,pz,c,out_value), Ianp = (Tfloat)atXYZ(ax,ny,pz,c,out_value),
  13123. Inp = Icnp + 0.5f*(dx*(-Ipnp + Innp) + dx*dx*(2*Ipnp - 5*Icnp + 4*Innp - Ianp) +
  13124. dx*dx*dx*(-Ipnp + 3*Icnp - 3*Innp + Ianp)),
  13125. Ipap = (Tfloat)atXYZ(px,ay,pz,c,out_value), Icap = (Tfloat)atXYZ(x,ay,pz,c,out_value),
  13126. Inap = (Tfloat)atXYZ(nx,ay,pz,c,out_value), Iaap = (Tfloat)atXYZ(ax,ay,pz,c,out_value),
  13127. Iap = Icap + 0.5f*(dx*(-Ipap + Inap) + dx*dx*(2*Ipap - 5*Icap + 4*Inap - Iaap) +
  13128. dx*dx*dx*(-Ipap + 3*Icap - 3*Inap + Iaap)),
  13129. Ip = Icp + 0.5f*(dy*(-Ipp + Inp) + dy*dy*(2*Ipp - 5*Icp + 4*Inp - Iap) +
  13130. dy*dy*dy*(-Ipp + 3*Icp - 3*Inp + Iap)),
  13131. Ippc = (Tfloat)atXYZ(px,py,z,c,out_value), Icpc = (Tfloat)atXYZ(x,py,z,c,out_value),
  13132. Inpc = (Tfloat)atXYZ(nx,py,z,c,out_value), Iapc = (Tfloat)atXYZ(ax,py,z,c,out_value),
  13133. Ipc = Icpc + 0.5f*(dx*(-Ippc + Inpc) + dx*dx*(2*Ippc - 5*Icpc + 4*Inpc - Iapc) +
  13134. dx*dx*dx*(-Ippc + 3*Icpc - 3*Inpc + Iapc)),
  13135. Ipcc = (Tfloat)atXYZ(px,y,z,c,out_value), Iccc = (Tfloat)atXYZ(x, y,z,c,out_value),
  13136. Incc = (Tfloat)atXYZ(nx,y,z,c,out_value), Iacc = (Tfloat)atXYZ(ax,y,z,c,out_value),
  13137. Icc = Iccc + 0.5f*(dx*(-Ipcc + Incc) + dx*dx*(2*Ipcc - 5*Iccc + 4*Incc - Iacc) +
  13138. dx*dx*dx*(-Ipcc + 3*Iccc - 3*Incc + Iacc)),
  13139. Ipnc = (Tfloat)atXYZ(px,ny,z,c,out_value), Icnc = (Tfloat)atXYZ(x,ny,z,c,out_value),
  13140. Innc = (Tfloat)atXYZ(nx,ny,z,c,out_value), Ianc = (Tfloat)atXYZ(ax,ny,z,c,out_value),
  13141. Inc = Icnc + 0.5f*(dx*(-Ipnc + Innc) + dx*dx*(2*Ipnc - 5*Icnc + 4*Innc - Ianc) +
  13142. dx*dx*dx*(-Ipnc + 3*Icnc - 3*Innc + Ianc)),
  13143. Ipac = (Tfloat)atXYZ(px,ay,z,c,out_value), Icac = (Tfloat)atXYZ(x,ay,z,c,out_value),
  13144. Inac = (Tfloat)atXYZ(nx,ay,z,c,out_value), Iaac = (Tfloat)atXYZ(ax,ay,z,c,out_value),
  13145. Iac = Icac + 0.5f*(dx*(-Ipac + Inac) + dx*dx*(2*Ipac - 5*Icac + 4*Inac - Iaac) +
  13146. dx*dx*dx*(-Ipac + 3*Icac - 3*Inac + Iaac)),
  13147. Ic = Icc + 0.5f*(dy*(-Ipc + Inc) + dy*dy*(2*Ipc - 5*Icc + 4*Inc - Iac) +
  13148. dy*dy*dy*(-Ipc + 3*Icc - 3*Inc + Iac)),
  13149. Ippn = (Tfloat)atXYZ(px,py,nz,c,out_value), Icpn = (Tfloat)atXYZ(x,py,nz,c,out_value),
  13150. Inpn = (Tfloat)atXYZ(nx,py,nz,c,out_value), Iapn = (Tfloat)atXYZ(ax,py,nz,c,out_value),
  13151. Ipn = Icpn + 0.5f*(dx*(-Ippn + Inpn) + dx*dx*(2*Ippn - 5*Icpn + 4*Inpn - Iapn) +
  13152. dx*dx*dx*(-Ippn + 3*Icpn - 3*Inpn + Iapn)),
  13153. Ipcn = (Tfloat)atXYZ(px,y,nz,c,out_value), Iccn = (Tfloat)atXYZ(x, y,nz,c,out_value),
  13154. Incn = (Tfloat)atXYZ(nx,y,nz,c,out_value), Iacn = (Tfloat)atXYZ(ax,y,nz,c,out_value),
  13155. Icn = Iccn + 0.5f*(dx*(-Ipcn + Incn) + dx*dx*(2*Ipcn - 5*Iccn + 4*Incn - Iacn) +
  13156. dx*dx*dx*(-Ipcn + 3*Iccn - 3*Incn + Iacn)),
  13157. Ipnn = (Tfloat)atXYZ(px,ny,nz,c,out_value), Icnn = (Tfloat)atXYZ(x,ny,nz,c,out_value),
  13158. Innn = (Tfloat)atXYZ(nx,ny,nz,c,out_value), Iann = (Tfloat)atXYZ(ax,ny,nz,c,out_value),
  13159. Inn = Icnn + 0.5f*(dx*(-Ipnn + Innn) + dx*dx*(2*Ipnn - 5*Icnn + 4*Innn - Iann) +
  13160. dx*dx*dx*(-Ipnn + 3*Icnn - 3*Innn + Iann)),
  13161. Ipan = (Tfloat)atXYZ(px,ay,nz,c,out_value), Ican = (Tfloat)atXYZ(x,ay,nz,c,out_value),
  13162. Inan = (Tfloat)atXYZ(nx,ay,nz,c,out_value), Iaan = (Tfloat)atXYZ(ax,ay,nz,c,out_value),
  13163. Ian = Ican + 0.5f*(dx*(-Ipan + Inan) + dx*dx*(2*Ipan - 5*Ican + 4*Inan - Iaan) +
  13164. dx*dx*dx*(-Ipan + 3*Ican - 3*Inan + Iaan)),
  13165. In = Icn + 0.5f*(dy*(-Ipn + Inn) + dy*dy*(2*Ipn - 5*Icn + 4*Inn - Ian) +
  13166. dy*dy*dy*(-Ipn + 3*Icn - 3*Inn + Ian)),
  13167. Ippa = (Tfloat)atXYZ(px,py,az,c,out_value), Icpa = (Tfloat)atXYZ(x,py,az,c,out_value),
  13168. Inpa = (Tfloat)atXYZ(nx,py,az,c,out_value), Iapa = (Tfloat)atXYZ(ax,py,az,c,out_value),
  13169. Ipa = Icpa + 0.5f*(dx*(-Ippa + Inpa) + dx*dx*(2*Ippa - 5*Icpa + 4*Inpa - Iapa) +
  13170. dx*dx*dx*(-Ippa + 3*Icpa - 3*Inpa + Iapa)),
  13171. Ipca = (Tfloat)atXYZ(px,y,az,c,out_value), Icca = (Tfloat)atXYZ(x, y,az,c,out_value),
  13172. Inca = (Tfloat)atXYZ(nx,y,az,c,out_value), Iaca = (Tfloat)atXYZ(ax,y,az,c,out_value),
  13173. Ica = Icca + 0.5f*(dx*(-Ipca + Inca) + dx*dx*(2*Ipca - 5*Icca + 4*Inca - Iaca) +
  13174. dx*dx*dx*(-Ipca + 3*Icca - 3*Inca + Iaca)),
  13175. Ipna = (Tfloat)atXYZ(px,ny,az,c,out_value), Icna = (Tfloat)atXYZ(x,ny,az,c,out_value),
  13176. Inna = (Tfloat)atXYZ(nx,ny,az,c,out_value), Iana = (Tfloat)atXYZ(ax,ny,az,c,out_value),
  13177. Ina = Icna + 0.5f*(dx*(-Ipna + Inna) + dx*dx*(2*Ipna - 5*Icna + 4*Inna - Iana) +
  13178. dx*dx*dx*(-Ipna + 3*Icna - 3*Inna + Iana)),
  13179. Ipaa = (Tfloat)atXYZ(px,ay,az,c,out_value), Icaa = (Tfloat)atXYZ(x,ay,az,c,out_value),
  13180. Inaa = (Tfloat)atXYZ(nx,ay,az,c,out_value), Iaaa = (Tfloat)atXYZ(ax,ay,az,c,out_value),
  13181. Iaa = Icaa + 0.5f*(dx*(-Ipaa + Inaa) + dx*dx*(2*Ipaa - 5*Icaa + 4*Inaa - Iaaa) +
  13182. dx*dx*dx*(-Ipaa + 3*Icaa - 3*Inaa + Iaaa)),
  13183. Ia = Ica + 0.5f*(dy*(-Ipa + Ina) + dy*dy*(2*Ipa - 5*Ica + 4*Ina - Iaa) +
  13184. dy*dy*dy*(-Ipa + 3*Ica - 3*Ina + Iaa));
  13185. 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));
  13186. }
  13187. //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the XYZ-coordinates.
  13188. /**
  13189. Similar to cubic_atXYZ(float,float,float,int,const T) const, except that the return value is clamped to stay
  13190. in the min/max range of the datatype \c T.
  13191. **/
  13192. T cubic_cut_atXYZ(const float fx, const float fy, const float fz, const int c, const T& out_value) const {
  13193. return cimg::type<T>::cut(cubic_atXYZ(fx,fy,fz,c,out_value));
  13194. }
  13195. //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y and Z-coordinates.
  13196. /**
  13197. Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking
  13198. are achieved both for X,Y and Z-coordinates.
  13199. \note
  13200. - If you know your image instance is \e not empty, you may rather use the slightly faster method
  13201. \c _cubic_atXYZ(float,float,float,int).
  13202. **/
  13203. Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c=0) const {
  13204. if (is_empty())
  13205. throw CImgInstanceException(_cimg_instance
  13206. "cubic_atXYZ(): Empty instance.",
  13207. cimg_instance);
  13208. return _cubic_atXYZ(fx,fy,fz,c);
  13209. }
  13210. Tfloat _cubic_atXYZ(const float fx, const float fy, const float fz, const int c=0) const {
  13211. const float
  13212. nfx = cimg::cut(fx,0,width() - 1),
  13213. nfy = cimg::cut(fy,0,height() - 1),
  13214. nfz = cimg::cut(fz,0,depth() - 1);
  13215. const int x = (int)nfx, y = (int)nfy, z = (int)nfz;
  13216. const float dx = nfx - x, dy = nfy - y, dz = nfz - z;
  13217. const int
  13218. px = x - 1<0?0:x - 1, nx = dx>0?x + 1:x, ax = x + 2>=width()?width() - 1:x + 2,
  13219. py = y - 1<0?0:y - 1, ny = dy>0?y + 1:y, ay = y + 2>=height()?height() - 1:y + 2,
  13220. pz = z - 1<0?0:z - 1, nz = dz>0?z + 1:z, az = z + 2>=depth()?depth() - 1:z + 2;
  13221. const Tfloat
  13222. Ippp = (Tfloat)(*this)(px,py,pz,c), Icpp = (Tfloat)(*this)(x,py,pz,c),
  13223. Inpp = (Tfloat)(*this)(nx,py,pz,c), Iapp = (Tfloat)(*this)(ax,py,pz,c),
  13224. Ipp = Icpp + 0.5f*(dx*(-Ippp + Inpp) + dx*dx*(2*Ippp - 5*Icpp + 4*Inpp - Iapp) +
  13225. dx*dx*dx*(-Ippp + 3*Icpp - 3*Inpp + Iapp)),
  13226. Ipcp = (Tfloat)(*this)(px,y,pz,c), Iccp = (Tfloat)(*this)(x, y,pz,c),
  13227. Incp = (Tfloat)(*this)(nx,y,pz,c), Iacp = (Tfloat)(*this)(ax,y,pz,c),
  13228. Icp = Iccp + 0.5f*(dx*(-Ipcp + Incp) + dx*dx*(2*Ipcp - 5*Iccp + 4*Incp - Iacp) +
  13229. dx*dx*dx*(-Ipcp + 3*Iccp - 3*Incp + Iacp)),
  13230. Ipnp = (Tfloat)(*this)(px,ny,pz,c), Icnp = (Tfloat)(*this)(x,ny,pz,c),
  13231. Innp = (Tfloat)(*this)(nx,ny,pz,c), Ianp = (Tfloat)(*this)(ax,ny,pz,c),
  13232. Inp = Icnp + 0.5f*(dx*(-Ipnp + Innp) + dx*dx*(2*Ipnp - 5*Icnp + 4*Innp - Ianp) +
  13233. dx*dx*dx*(-Ipnp + 3*Icnp - 3*Innp + Ianp)),
  13234. Ipap = (Tfloat)(*this)(px,ay,pz,c), Icap = (Tfloat)(*this)(x,ay,pz,c),
  13235. Inap = (Tfloat)(*this)(nx,ay,pz,c), Iaap = (Tfloat)(*this)(ax,ay,pz,c),
  13236. Iap = Icap + 0.5f*(dx*(-Ipap + Inap) + dx*dx*(2*Ipap - 5*Icap + 4*Inap - Iaap) +
  13237. dx*dx*dx*(-Ipap + 3*Icap - 3*Inap + Iaap)),
  13238. Ip = Icp + 0.5f*(dy*(-Ipp + Inp) + dy*dy*(2*Ipp - 5*Icp + 4*Inp - Iap) +
  13239. dy*dy*dy*(-Ipp + 3*Icp - 3*Inp + Iap)),
  13240. Ippc = (Tfloat)(*this)(px,py,z,c), Icpc = (Tfloat)(*this)(x,py,z,c),
  13241. Inpc = (Tfloat)(*this)(nx,py,z,c), Iapc = (Tfloat)(*this)(ax,py,z,c),
  13242. Ipc = Icpc + 0.5f*(dx*(-Ippc + Inpc) + dx*dx*(2*Ippc - 5*Icpc + 4*Inpc - Iapc) +
  13243. dx*dx*dx*(-Ippc + 3*Icpc - 3*Inpc + Iapc)),
  13244. Ipcc = (Tfloat)(*this)(px,y,z,c), Iccc = (Tfloat)(*this)(x, y,z,c),
  13245. Incc = (Tfloat)(*this)(nx,y,z,c), Iacc = (Tfloat)(*this)(ax,y,z,c),
  13246. Icc = Iccc + 0.5f*(dx*(-Ipcc + Incc) + dx*dx*(2*Ipcc - 5*Iccc + 4*Incc - Iacc) +
  13247. dx*dx*dx*(-Ipcc + 3*Iccc - 3*Incc + Iacc)),
  13248. Ipnc = (Tfloat)(*this)(px,ny,z,c), Icnc = (Tfloat)(*this)(x,ny,z,c),
  13249. Innc = (Tfloat)(*this)(nx,ny,z,c), Ianc = (Tfloat)(*this)(ax,ny,z,c),
  13250. Inc = Icnc + 0.5f*(dx*(-Ipnc + Innc) + dx*dx*(2*Ipnc - 5*Icnc + 4*Innc - Ianc) +
  13251. dx*dx*dx*(-Ipnc + 3*Icnc - 3*Innc + Ianc)),
  13252. Ipac = (Tfloat)(*this)(px,ay,z,c), Icac = (Tfloat)(*this)(x,ay,z,c),
  13253. Inac = (Tfloat)(*this)(nx,ay,z,c), Iaac = (Tfloat)(*this)(ax,ay,z,c),
  13254. Iac = Icac + 0.5f*(dx*(-Ipac + Inac) + dx*dx*(2*Ipac - 5*Icac + 4*Inac - Iaac) +
  13255. dx*dx*dx*(-Ipac + 3*Icac - 3*Inac + Iaac)),
  13256. Ic = Icc + 0.5f*(dy*(-Ipc + Inc) + dy*dy*(2*Ipc - 5*Icc + 4*Inc - Iac) +
  13257. dy*dy*dy*(-Ipc + 3*Icc - 3*Inc + Iac)),
  13258. Ippn = (Tfloat)(*this)(px,py,nz,c), Icpn = (Tfloat)(*this)(x,py,nz,c),
  13259. Inpn = (Tfloat)(*this)(nx,py,nz,c), Iapn = (Tfloat)(*this)(ax,py,nz,c),
  13260. Ipn = Icpn + 0.5f*(dx*(-Ippn + Inpn) + dx*dx*(2*Ippn - 5*Icpn + 4*Inpn - Iapn) +
  13261. dx*dx*dx*(-Ippn + 3*Icpn - 3*Inpn + Iapn)),
  13262. Ipcn = (Tfloat)(*this)(px,y,nz,c), Iccn = (Tfloat)(*this)(x, y,nz,c),
  13263. Incn = (Tfloat)(*this)(nx,y,nz,c), Iacn = (Tfloat)(*this)(ax,y,nz,c),
  13264. Icn = Iccn + 0.5f*(dx*(-Ipcn + Incn) + dx*dx*(2*Ipcn - 5*Iccn + 4*Incn - Iacn) +
  13265. dx*dx*dx*(-Ipcn + 3*Iccn - 3*Incn + Iacn)),
  13266. Ipnn = (Tfloat)(*this)(px,ny,nz,c), Icnn = (Tfloat)(*this)(x,ny,nz,c),
  13267. Innn = (Tfloat)(*this)(nx,ny,nz,c), Iann = (Tfloat)(*this)(ax,ny,nz,c),
  13268. Inn = Icnn + 0.5f*(dx*(-Ipnn + Innn) + dx*dx*(2*Ipnn - 5*Icnn + 4*Innn - Iann) +
  13269. dx*dx*dx*(-Ipnn + 3*Icnn - 3*Innn + Iann)),
  13270. Ipan = (Tfloat)(*this)(px,ay,nz,c), Ican = (Tfloat)(*this)(x,ay,nz,c),
  13271. Inan = (Tfloat)(*this)(nx,ay,nz,c), Iaan = (Tfloat)(*this)(ax,ay,nz,c),
  13272. Ian = Ican + 0.5f*(dx*(-Ipan + Inan) + dx*dx*(2*Ipan - 5*Ican + 4*Inan - Iaan) +
  13273. dx*dx*dx*(-Ipan + 3*Ican - 3*Inan + Iaan)),
  13274. In = Icn + 0.5f*(dy*(-Ipn + Inn) + dy*dy*(2*Ipn - 5*Icn + 4*Inn - Ian) +
  13275. dy*dy*dy*(-Ipn + 3*Icn - 3*Inn + Ian)),
  13276. Ippa = (Tfloat)(*this)(px,py,az,c), Icpa = (Tfloat)(*this)(x,py,az,c),
  13277. Inpa = (Tfloat)(*this)(nx,py,az,c), Iapa = (Tfloat)(*this)(ax,py,az,c),
  13278. Ipa = Icpa + 0.5f*(dx*(-Ippa + Inpa) + dx*dx*(2*Ippa - 5*Icpa + 4*Inpa - Iapa) +
  13279. dx*dx*dx*(-Ippa + 3*Icpa - 3*Inpa + Iapa)),
  13280. Ipca = (Tfloat)(*this)(px,y,az,c), Icca = (Tfloat)(*this)(x, y,az,c),
  13281. Inca = (Tfloat)(*this)(nx,y,az,c), Iaca = (Tfloat)(*this)(ax,y,az,c),
  13282. Ica = Icca + 0.5f*(dx*(-Ipca + Inca) + dx*dx*(2*Ipca - 5*Icca + 4*Inca - Iaca) +
  13283. dx*dx*dx*(-Ipca + 3*Icca - 3*Inca + Iaca)),
  13284. Ipna = (Tfloat)(*this)(px,ny,az,c), Icna = (Tfloat)(*this)(x,ny,az,c),
  13285. Inna = (Tfloat)(*this)(nx,ny,az,c), Iana = (Tfloat)(*this)(ax,ny,az,c),
  13286. Ina = Icna + 0.5f*(dx*(-Ipna + Inna) + dx*dx*(2*Ipna - 5*Icna + 4*Inna - Iana) +
  13287. dx*dx*dx*(-Ipna + 3*Icna - 3*Inna + Iana)),
  13288. Ipaa = (Tfloat)(*this)(px,ay,az,c), Icaa = (Tfloat)(*this)(x,ay,az,c),
  13289. Inaa = (Tfloat)(*this)(nx,ay,az,c), Iaaa = (Tfloat)(*this)(ax,ay,az,c),
  13290. Iaa = Icaa + 0.5f*(dx*(-Ipaa + Inaa) + dx*dx*(2*Ipaa - 5*Icaa + 4*Inaa - Iaaa) +
  13291. dx*dx*dx*(-Ipaa + 3*Icaa - 3*Inaa + Iaaa)),
  13292. Ia = Ica + 0.5f*(dy*(-Ipa + Ina) + dy*dy*(2*Ipa - 5*Ica + 4*Ina - Iaa) +
  13293. dy*dy*dy*(-Ipa + 3*Ica - 3*Ina + Iaa));
  13294. 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));
  13295. }
  13296. //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the XYZ-coordinates.
  13297. /**
  13298. Similar to cubic_atXYZ(float,float,float,int) const, except that the return value is clamped to stay in the
  13299. min/max range of the datatype \c T.
  13300. **/
  13301. T cubic_cut_atXYZ(const float fx, const float fy, const float fz, const int c) const {
  13302. return cimg::type<T>::cut(cubic_atXYZ(fx,fy,fz,c));
  13303. }
  13304. T _cubic_cut_atXYZ(const float fx, const float fy, const float fz, const int c) const {
  13305. return cimg::type<T>::cut(_cubic_atXYZ(fx,fy,fz,c));
  13306. }
  13307. //! Set pixel value, using linear interpolation for the X-coordinates.
  13308. /**
  13309. Set pixel value at specified coordinates (\c fx,\c y,\c z,\c c) in the image instance, in a way that
  13310. the value is spread amongst several neighbors if the pixel coordinates are float-valued.
  13311. \param value Pixel value to set.
  13312. \param fx X-coordinate of the pixel value (float-valued).
  13313. \param y Y-coordinate of the pixel value.
  13314. \param z Z-coordinate of the pixel value.
  13315. \param c C-coordinate of the pixel value.
  13316. \param is_added Tells if the pixel value is added to (\c true), or simply replace (\c false) the current image
  13317. pixel(s).
  13318. \return A reference to the current image instance.
  13319. \note
  13320. - Calling this method with out-of-bounds coordinates does nothing.
  13321. **/
  13322. CImg<T>& set_linear_atX(const T& value, const float fx, const int y=0, const int z=0, const int c=0,
  13323. const bool is_added=false) {
  13324. const int
  13325. x = (int)fx - (fx>=0?0:1), nx = x + 1;
  13326. const float
  13327. dx = fx - x;
  13328. if (y>=0 && y<height() && z>=0 && z<depth() && c>=0 && c<spectrum()) {
  13329. if (x>=0 && x<width()) {
  13330. const float w1 = 1 - dx, w2 = is_added?1:(1 - w1);
  13331. (*this)(x,y,z,c) = (T)(w1*value + w2*(*this)(x,y,z,c));
  13332. }
  13333. if (nx>=0 && nx<width()) {
  13334. const float w1 = dx, w2 = is_added?1:(1 - w1);
  13335. (*this)(nx,y,z,c) = (T)(w1*value + w2*(*this)(nx,y,z,c));
  13336. }
  13337. }
  13338. return *this;
  13339. }
  13340. //! Set pixel value, using linear interpolation for the X and Y-coordinates.
  13341. /**
  13342. Similar to set_linear_atX(const T&,float,int,int,int,bool), except that the linear interpolation
  13343. is achieved both for X and Y-coordinates.
  13344. **/
  13345. CImg<T>& set_linear_atXY(const T& value, const float fx, const float fy=0, const int z=0, const int c=0,
  13346. const bool is_added=false) {
  13347. const int
  13348. x = (int)fx - (fx>=0?0:1), nx = x + 1,
  13349. y = (int)fy - (fy>=0?0:1), ny = y + 1;
  13350. const float
  13351. dx = fx - x,
  13352. dy = fy - y;
  13353. if (z>=0 && z<depth() && c>=0 && c<spectrum()) {
  13354. if (y>=0 && y<height()) {
  13355. if (x>=0 && x<width()) {
  13356. const float w1 = (1 - dx)*(1 - dy), w2 = is_added?1:(1 - w1);
  13357. (*this)(x,y,z,c) = (T)(w1*value + w2*(*this)(x,y,z,c));
  13358. }
  13359. if (nx>=0 && nx<width()) {
  13360. const float w1 = dx*(1 - dy), w2 = is_added?1:(1 - w1);
  13361. (*this)(nx,y,z,c) = (T)(w1*value + w2*(*this)(nx,y,z,c));
  13362. }
  13363. }
  13364. if (ny>=0 && ny<height()) {
  13365. if (x>=0 && x<width()) {
  13366. const float w1 = (1 - dx)*dy, w2 = is_added?1:(1 - w1);
  13367. (*this)(x,ny,z,c) = (T)(w1*value + w2*(*this)(x,ny,z,c));
  13368. }
  13369. if (nx>=0 && nx<width()) {
  13370. const float w1 = dx*dy, w2 = is_added?1:(1 - w1);
  13371. (*this)(nx,ny,z,c) = (T)(w1*value + w2*(*this)(nx,ny,z,c));
  13372. }
  13373. }
  13374. }
  13375. return *this;
  13376. }
  13377. //! Set pixel value, using linear interpolation for the X,Y and Z-coordinates.
  13378. /**
  13379. Similar to set_linear_atXY(const T&,float,float,int,int,bool), except that the linear interpolation
  13380. is achieved both for X,Y and Z-coordinates.
  13381. **/
  13382. CImg<T>& set_linear_atXYZ(const T& value, const float fx, const float fy=0, const float fz=0, const int c=0,
  13383. const bool is_added=false) {
  13384. const int
  13385. x = (int)fx - (fx>=0?0:1), nx = x + 1,
  13386. y = (int)fy - (fy>=0?0:1), ny = y + 1,
  13387. z = (int)fz - (fz>=0?0:1), nz = z + 1;
  13388. const float
  13389. dx = fx - x,
  13390. dy = fy - y,
  13391. dz = fz - z;
  13392. if (c>=0 && c<spectrum()) {
  13393. if (z>=0 && z<depth()) {
  13394. if (y>=0 && y<height()) {
  13395. if (x>=0 && x<width()) {
  13396. const float w1 = (1 - dx)*(1 - dy)*(1 - dz), w2 = is_added?1:(1 - w1);
  13397. (*this)(x,y,z,c) = (T)(w1*value + w2*(*this)(x,y,z,c));
  13398. }
  13399. if (nx>=0 && nx<width()) {
  13400. const float w1 = dx*(1 - dy)*(1 - dz), w2 = is_added?1:(1 - w1);
  13401. (*this)(nx,y,z,c) = (T)(w1*value + w2*(*this)(nx,y,z,c));
  13402. }
  13403. }
  13404. if (ny>=0 && ny<height()) {
  13405. if (x>=0 && x<width()) {
  13406. const float w1 = (1 - dx)*dy*(1 - dz), w2 = is_added?1:(1 - w1);
  13407. (*this)(x,ny,z,c) = (T)(w1*value + w2*(*this)(x,ny,z,c));
  13408. }
  13409. if (nx>=0 && nx<width()) {
  13410. const float w1 = dx*dy*(1 - dz), w2 = is_added?1:(1 - w1);
  13411. (*this)(nx,ny,z,c) = (T)(w1*value + w2*(*this)(nx,ny,z,c));
  13412. }
  13413. }
  13414. }
  13415. if (nz>=0 && nz<depth()) {
  13416. if (y>=0 && y<height()) {
  13417. if (x>=0 && x<width()) {
  13418. const float w1 = (1 - dx)*(1 - dy)*dz, w2 = is_added?1:(1 - w1);
  13419. (*this)(x,y,nz,c) = (T)(w1*value + w2*(*this)(x,y,nz,c));
  13420. }
  13421. if (nx>=0 && nx<width()) {
  13422. const float w1 = dx*(1 - dy)*dz, w2 = is_added?1:(1 - w1);
  13423. (*this)(nx,y,nz,c) = (T)(w1*value + w2*(*this)(nx,y,nz,c));
  13424. }
  13425. }
  13426. if (ny>=0 && ny<height()) {
  13427. if (x>=0 && x<width()) {
  13428. const float w1 = (1 - dx)*dy*dz, w2 = is_added?1:(1 - w1);
  13429. (*this)(x,ny,nz,c) = (T)(w1*value + w2*(*this)(x,ny,nz,c));
  13430. }
  13431. if (nx>=0 && nx<width()) {
  13432. const float w1 = dx*dy*dz, w2 = is_added?1:(1 - w1);
  13433. (*this)(nx,ny,nz,c) = (T)(w1*value + w2*(*this)(nx,ny,nz,c));
  13434. }
  13435. }
  13436. }
  13437. }
  13438. return *this;
  13439. }
  13440. //! Return a C-string containing a list of all values of the image instance.
  13441. /**
  13442. Return a new \c CImg<char> image whose buffer data() is a \c char* string describing the list of all pixel values
  13443. of the image instance (written in base 10), separated by specified \c separator character.
  13444. \param separator A \c char character which specifies the separator between values in the returned C-string.
  13445. \param max_size Maximum size of the returned image (or \c 0 if no limits are set).
  13446. \param format For float/double-values, tell the printf format used to generate the ascii representation
  13447. of the numbers (or \c 0 for default representation).
  13448. \note
  13449. - The returned image is never empty.
  13450. - For an empty image instance, the returned string is <tt>""</tt>.
  13451. - If \c max_size is equal to \c 0, there are no limits on the size of the returned string.
  13452. - Otherwise, if the maximum number of string characters is exceeded, the value string is cut off
  13453. and terminated by character \c '\0'. In that case, the returned image size is <tt>max_size + 1</tt>.
  13454. **/
  13455. CImg<charT> value_string(const char separator=',', const unsigned int max_size=0,
  13456. const char *const format=0) const {
  13457. if (is_empty() || max_size==1) return CImg<charT>(1,1,1,1,0);
  13458. CImgList<charT> items;
  13459. CImg<charT> s_item(256); *s_item = 0;
  13460. const T *ptrs = _data;
  13461. unsigned int string_size = 0;
  13462. const char *const _format = format?format:cimg::type<T>::format();
  13463. for (ulongT off = 0, siz = size(); off<siz && (!max_size || string_size<max_size); ++off) {
  13464. const unsigned int printed_size = 1U + cimg_snprintf(s_item,s_item._width,_format,
  13465. cimg::type<T>::format(*(ptrs++)));
  13466. CImg<charT> item(s_item._data,printed_size);
  13467. item[printed_size - 1] = separator;
  13468. item.move_to(items);
  13469. if (max_size) string_size+=printed_size;
  13470. }
  13471. CImg<charT> res;
  13472. (items>'x').move_to(res);
  13473. if (max_size && res._width>=max_size) res.crop(0,max_size - 1);
  13474. res.back() = 0;
  13475. return res;
  13476. }
  13477. //@}
  13478. //-------------------------------------
  13479. //
  13480. //! \name Instance Checking
  13481. //@{
  13482. //-------------------------------------
  13483. //! Test shared state of the pixel buffer.
  13484. /**
  13485. Return \c true if image instance has a shared memory buffer, and \c false otherwise.
  13486. \note
  13487. - A shared image do not own his pixel buffer data() and will not deallocate it on destruction.
  13488. - Most of the time, a \c CImg<T> image instance will \e not be shared.
  13489. - A shared image can only be obtained by a limited set of constructors and methods (see list below).
  13490. **/
  13491. bool is_shared() const {
  13492. return _is_shared;
  13493. }
  13494. //! Test if image instance is empty.
  13495. /**
  13496. Return \c true, if image instance is empty, i.e. does \e not contain any pixel values, has dimensions
  13497. \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.
  13498. **/
  13499. bool is_empty() const {
  13500. return !(_data && _width && _height && _depth && _spectrum);
  13501. }
  13502. //! Test if image instance contains a 'inf' value.
  13503. /**
  13504. Return \c true, if image instance contains a 'inf' value, and \c false otherwise.
  13505. **/
  13506. bool is_inf() const {
  13507. if (cimg::type<T>::is_float()) cimg_for(*this,p,T) if (cimg::type<T>::is_inf((float)*p)) return true;
  13508. return false;
  13509. }
  13510. //! Test if image instance contains a NaN value.
  13511. /**
  13512. Return \c true, if image instance contains a NaN value, and \c false otherwise.
  13513. **/
  13514. bool is_nan() const {
  13515. if (cimg::type<T>::is_float()) cimg_for(*this,p,T) if (cimg::type<T>::is_nan((float)*p)) return true;
  13516. return false;
  13517. }
  13518. //! Test if image width is equal to specified value.
  13519. bool is_sameX(const unsigned int size_x) const {
  13520. return _width==size_x;
  13521. }
  13522. //! Test if image width is equal to specified value.
  13523. template<typename t>
  13524. bool is_sameX(const CImg<t>& img) const {
  13525. return is_sameX(img._width);
  13526. }
  13527. //! Test if image width is equal to specified value.
  13528. bool is_sameX(const CImgDisplay& disp) const {
  13529. return is_sameX(disp._width);
  13530. }
  13531. //! Test if image height is equal to specified value.
  13532. bool is_sameY(const unsigned int size_y) const {
  13533. return _height==size_y;
  13534. }
  13535. //! Test if image height is equal to specified value.
  13536. template<typename t>
  13537. bool is_sameY(const CImg<t>& img) const {
  13538. return is_sameY(img._height);
  13539. }
  13540. //! Test if image height is equal to specified value.
  13541. bool is_sameY(const CImgDisplay& disp) const {
  13542. return is_sameY(disp._height);
  13543. }
  13544. //! Test if image depth is equal to specified value.
  13545. bool is_sameZ(const unsigned int size_z) const {
  13546. return _depth==size_z;
  13547. }
  13548. //! Test if image depth is equal to specified value.
  13549. template<typename t>
  13550. bool is_sameZ(const CImg<t>& img) const {
  13551. return is_sameZ(img._depth);
  13552. }
  13553. //! Test if image spectrum is equal to specified value.
  13554. bool is_sameC(const unsigned int size_c) const {
  13555. return _spectrum==size_c;
  13556. }
  13557. //! Test if image spectrum is equal to specified value.
  13558. template<typename t>
  13559. bool is_sameC(const CImg<t>& img) const {
  13560. return is_sameC(img._spectrum);
  13561. }
  13562. //! Test if image width and height are equal to specified values.
  13563. /**
  13564. Test if is_sameX(unsigned int) const and is_sameY(unsigned int) const are both verified.
  13565. **/
  13566. bool is_sameXY(const unsigned int size_x, const unsigned int size_y) const {
  13567. return _width==size_x && _height==size_y;
  13568. }
  13569. //! Test if image width and height are the same as that of another image.
  13570. /**
  13571. Test if is_sameX(const CImg<t>&) const and is_sameY(const CImg<t>&) const are both verified.
  13572. **/
  13573. template<typename t>
  13574. bool is_sameXY(const CImg<t>& img) const {
  13575. return is_sameXY(img._width,img._height);
  13576. }
  13577. //! Test if image width and height are the same as that of an existing display window.
  13578. /**
  13579. Test if is_sameX(const CImgDisplay&) const and is_sameY(const CImgDisplay&) const are both verified.
  13580. **/
  13581. bool is_sameXY(const CImgDisplay& disp) const {
  13582. return is_sameXY(disp._width,disp._height);
  13583. }
  13584. //! Test if image width and depth are equal to specified values.
  13585. /**
  13586. Test if is_sameX(unsigned int) const and is_sameZ(unsigned int) const are both verified.
  13587. **/
  13588. bool is_sameXZ(const unsigned int size_x, const unsigned int size_z) const {
  13589. return _width==size_x && _depth==size_z;
  13590. }
  13591. //! Test if image width and depth are the same as that of another image.
  13592. /**
  13593. Test if is_sameX(const CImg<t>&) const and is_sameZ(const CImg<t>&) const are both verified.
  13594. **/
  13595. template<typename t>
  13596. bool is_sameXZ(const CImg<t>& img) const {
  13597. return is_sameXZ(img._width,img._depth);
  13598. }
  13599. //! Test if image width and spectrum are equal to specified values.
  13600. /**
  13601. Test if is_sameX(unsigned int) const and is_sameC(unsigned int) const are both verified.
  13602. **/
  13603. bool is_sameXC(const unsigned int size_x, const unsigned int size_c) const {
  13604. return _width==size_x && _spectrum==size_c;
  13605. }
  13606. //! Test if image width and spectrum are the same as that of another image.
  13607. /**
  13608. Test if is_sameX(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
  13609. **/
  13610. template<typename t>
  13611. bool is_sameXC(const CImg<t>& img) const {
  13612. return is_sameXC(img._width,img._spectrum);
  13613. }
  13614. //! Test if image height and depth are equal to specified values.
  13615. /**
  13616. Test if is_sameY(unsigned int) const and is_sameZ(unsigned int) const are both verified.
  13617. **/
  13618. bool is_sameYZ(const unsigned int size_y, const unsigned int size_z) const {
  13619. return _height==size_y && _depth==size_z;
  13620. }
  13621. //! Test if image height and depth are the same as that of another image.
  13622. /**
  13623. Test if is_sameY(const CImg<t>&) const and is_sameZ(const CImg<t>&) const are both verified.
  13624. **/
  13625. template<typename t>
  13626. bool is_sameYZ(const CImg<t>& img) const {
  13627. return is_sameYZ(img._height,img._depth);
  13628. }
  13629. //! Test if image height and spectrum are equal to specified values.
  13630. /**
  13631. Test if is_sameY(unsigned int) const and is_sameC(unsigned int) const are both verified.
  13632. **/
  13633. bool is_sameYC(const unsigned int size_y, const unsigned int size_c) const {
  13634. return _height==size_y && _spectrum==size_c;
  13635. }
  13636. //! Test if image height and spectrum are the same as that of another image.
  13637. /**
  13638. Test if is_sameY(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
  13639. **/
  13640. template<typename t>
  13641. bool is_sameYC(const CImg<t>& img) const {
  13642. return is_sameYC(img._height,img._spectrum);
  13643. }
  13644. //! Test if image depth and spectrum are equal to specified values.
  13645. /**
  13646. Test if is_sameZ(unsigned int) const and is_sameC(unsigned int) const are both verified.
  13647. **/
  13648. bool is_sameZC(const unsigned int size_z, const unsigned int size_c) const {
  13649. return _depth==size_z && _spectrum==size_c;
  13650. }
  13651. //! Test if image depth and spectrum are the same as that of another image.
  13652. /**
  13653. Test if is_sameZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
  13654. **/
  13655. template<typename t>
  13656. bool is_sameZC(const CImg<t>& img) const {
  13657. return is_sameZC(img._depth,img._spectrum);
  13658. }
  13659. //! Test if image width, height and depth are equal to specified values.
  13660. /**
  13661. Test if is_sameXY(unsigned int,unsigned int) const and is_sameZ(unsigned int) const are both verified.
  13662. **/
  13663. bool is_sameXYZ(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z) const {
  13664. return is_sameXY(size_x,size_y) && _depth==size_z;
  13665. }
  13666. //! Test if image width, height and depth are the same as that of another image.
  13667. /**
  13668. Test if is_sameXY(const CImg<t>&) const and is_sameZ(const CImg<t>&) const are both verified.
  13669. **/
  13670. template<typename t>
  13671. bool is_sameXYZ(const CImg<t>& img) const {
  13672. return is_sameXYZ(img._width,img._height,img._depth);
  13673. }
  13674. //! Test if image width, height and spectrum are equal to specified values.
  13675. /**
  13676. Test if is_sameXY(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified.
  13677. **/
  13678. bool is_sameXYC(const unsigned int size_x, const unsigned int size_y, const unsigned int size_c) const {
  13679. return is_sameXY(size_x,size_y) && _spectrum==size_c;
  13680. }
  13681. //! Test if image width, height and spectrum are the same as that of another image.
  13682. /**
  13683. Test if is_sameXY(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
  13684. **/
  13685. template<typename t>
  13686. bool is_sameXYC(const CImg<t>& img) const {
  13687. return is_sameXYC(img._width,img._height,img._spectrum);
  13688. }
  13689. //! Test if image width, depth and spectrum are equal to specified values.
  13690. /**
  13691. Test if is_sameXZ(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified.
  13692. **/
  13693. bool is_sameXZC(const unsigned int size_x, const unsigned int size_z, const unsigned int size_c) const {
  13694. return is_sameXZ(size_x,size_z) && _spectrum==size_c;
  13695. }
  13696. //! Test if image width, depth and spectrum are the same as that of another image.
  13697. /**
  13698. Test if is_sameXZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
  13699. **/
  13700. template<typename t>
  13701. bool is_sameXZC(const CImg<t>& img) const {
  13702. return is_sameXZC(img._width,img._depth,img._spectrum);
  13703. }
  13704. //! Test if image height, depth and spectrum are equal to specified values.
  13705. /**
  13706. Test if is_sameYZ(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified.
  13707. **/
  13708. bool is_sameYZC(const unsigned int size_y, const unsigned int size_z, const unsigned int size_c) const {
  13709. return is_sameYZ(size_y,size_z) && _spectrum==size_c;
  13710. }
  13711. //! Test if image height, depth and spectrum are the same as that of another image.
  13712. /**
  13713. Test if is_sameYZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
  13714. **/
  13715. template<typename t>
  13716. bool is_sameYZC(const CImg<t>& img) const {
  13717. return is_sameYZC(img._height,img._depth,img._spectrum);
  13718. }
  13719. //! Test if image width, height, depth and spectrum are equal to specified values.
  13720. /**
  13721. Test if is_sameXYZ(unsigned int,unsigned int,unsigned int) const and is_sameC(unsigned int) const are both
  13722. verified.
  13723. **/
  13724. bool is_sameXYZC(const unsigned int size_x, const unsigned int size_y,
  13725. const unsigned int size_z, const unsigned int size_c) const {
  13726. return is_sameXYZ(size_x,size_y,size_z) && _spectrum==size_c;
  13727. }
  13728. //! Test if image width, height, depth and spectrum are the same as that of another image.
  13729. /**
  13730. Test if is_sameXYZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
  13731. **/
  13732. template<typename t>
  13733. bool is_sameXYZC(const CImg<t>& img) const {
  13734. return is_sameXYZC(img._width,img._height,img._depth,img._spectrum);
  13735. }
  13736. //! Test if specified coordinates are inside image bounds.
  13737. /**
  13738. Return \c true if pixel located at (\c x,\c y,\c z,\c c) is inside bounds of the image instance,
  13739. and \c false otherwise.
  13740. \param x X-coordinate of the pixel value.
  13741. \param y Y-coordinate of the pixel value.
  13742. \param z Z-coordinate of the pixel value.
  13743. \param c C-coordinate of the pixel value.
  13744. \note
  13745. - Return \c true only if all these conditions are verified:
  13746. - The image instance is \e not empty.
  13747. - <tt>0<=x<=\ref width() - 1</tt>.
  13748. - <tt>0<=y<=\ref height() - 1</tt>.
  13749. - <tt>0<=z<=\ref depth() - 1</tt>.
  13750. - <tt>0<=c<=\ref spectrum() - 1</tt>.
  13751. **/
  13752. bool containsXYZC(const int x, const int y=0, const int z=0, const int c=0) const {
  13753. return !is_empty() && x>=0 && x<width() && y>=0 && y<height() && z>=0 && z<depth() && c>=0 && c<spectrum();
  13754. }
  13755. //! Test if pixel value is inside image bounds and get its X,Y,Z and C-coordinates.
  13756. /**
  13757. Return \c true, if specified reference refers to a pixel value inside bounds of the image instance,
  13758. and \c false otherwise.
  13759. \param pixel Reference to pixel value to test.
  13760. \param[out] x X-coordinate of the pixel value, if test succeeds.
  13761. \param[out] y Y-coordinate of the pixel value, if test succeeds.
  13762. \param[out] z Z-coordinate of the pixel value, if test succeeds.
  13763. \param[out] c C-coordinate of the pixel value, if test succeeds.
  13764. \note
  13765. - Useful to convert an offset to a buffer value into pixel value coordinates:
  13766. \code
  13767. const CImg<float> img(100,100,1,3); // Construct a 100x100 RGB color image.
  13768. const unsigned long offset = 1249; // Offset to the pixel (49,12,0,0).
  13769. unsigned int x,y,z,c;
  13770. if (img.contains(img[offset],x,y,z,c)) { // Convert offset to (x,y,z,c) coordinates.
  13771. std::printf("Offset %u refers to pixel located at (%u,%u,%u,%u).\n",
  13772. offset,x,y,z,c);
  13773. }
  13774. \endcode
  13775. **/
  13776. template<typename t>
  13777. bool contains(const T& pixel, t& x, t& y, t& z, t& c) const {
  13778. const ulongT wh = (ulongT)_width*_height, whd = wh*_depth, siz = whd*_spectrum;
  13779. const T *const ppixel = &pixel;
  13780. if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false;
  13781. ulongT off = (ulongT)(ppixel - _data);
  13782. const ulongT nc = off/whd;
  13783. off%=whd;
  13784. const ulongT nz = off/wh;
  13785. off%=wh;
  13786. const ulongT ny = off/_width, nx = off%_width;
  13787. x = (t)nx; y = (t)ny; z = (t)nz; c = (t)nc;
  13788. return true;
  13789. }
  13790. //! Test if pixel value is inside image bounds and get its X,Y and Z-coordinates.
  13791. /**
  13792. Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X,Y and Z-coordinates are set.
  13793. **/
  13794. template<typename t>
  13795. bool contains(const T& pixel, t& x, t& y, t& z) const {
  13796. const ulongT wh = (ulongT)_width*_height, whd = wh*_depth, siz = whd*_spectrum;
  13797. const T *const ppixel = &pixel;
  13798. if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false;
  13799. ulongT off = ((ulongT)(ppixel - _data))%whd;
  13800. const ulongT nz = off/wh;
  13801. off%=wh;
  13802. const ulongT ny = off/_width, nx = off%_width;
  13803. x = (t)nx; y = (t)ny; z = (t)nz;
  13804. return true;
  13805. }
  13806. //! Test if pixel value is inside image bounds and get its X and Y-coordinates.
  13807. /**
  13808. Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X and Y-coordinates are set.
  13809. **/
  13810. template<typename t>
  13811. bool contains(const T& pixel, t& x, t& y) const {
  13812. const ulongT wh = (ulongT)_width*_height, siz = wh*_depth*_spectrum;
  13813. const T *const ppixel = &pixel;
  13814. if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false;
  13815. ulongT off = ((unsigned int)(ppixel - _data))%wh;
  13816. const ulongT ny = off/_width, nx = off%_width;
  13817. x = (t)nx; y = (t)ny;
  13818. return true;
  13819. }
  13820. //! Test if pixel value is inside image bounds and get its X-coordinate.
  13821. /**
  13822. Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X-coordinate is set.
  13823. **/
  13824. template<typename t>
  13825. bool contains(const T& pixel, t& x) const {
  13826. const T *const ppixel = &pixel;
  13827. if (is_empty() || ppixel<_data || ppixel>=_data + size()) return false;
  13828. x = (t)(((ulongT)(ppixel - _data))%_width);
  13829. return true;
  13830. }
  13831. //! Test if pixel value is inside image bounds.
  13832. /**
  13833. Similar to contains(const T&,t&,t&,t&,t&) const, except that no pixel coordinates are set.
  13834. **/
  13835. bool contains(const T& pixel) const {
  13836. const T *const ppixel = &pixel;
  13837. return !is_empty() && ppixel>=_data && ppixel<_data + size();
  13838. }
  13839. //! Test if pixel buffers of instance and input images overlap.
  13840. /**
  13841. Return \c true, if pixel buffers attached to image instance and input image \c img overlap,
  13842. and \c false otherwise.
  13843. \param img Input image to compare with.
  13844. \note
  13845. - Buffer overlapping may happen when manipulating \e shared images.
  13846. - If two image buffers overlap, operating on one of the image will probably modify the other one.
  13847. - Most of the time, \c CImg<T> instances are \e non-shared and do not overlap between each others.
  13848. \par Example
  13849. \code
  13850. const CImg<float>
  13851. img1("reference.jpg"), // Load RGB-color image.
  13852. img2 = img1.get_shared_channel(1); // Get shared version of the green channel.
  13853. if (img1.is_overlapped(img2)) { // Test succeeds, 'img1' and 'img2' overlaps.
  13854. std::printf("Buffers overlap!\n");
  13855. }
  13856. \endcode
  13857. **/
  13858. template<typename t>
  13859. bool is_overlapped(const CImg<t>& img) const {
  13860. const ulongT csiz = size(), isiz = img.size();
  13861. return !((void*)(_data + csiz)<=(void*)img._data || (void*)_data>=(void*)(img._data + isiz));
  13862. }
  13863. //! Test if the set {\c *this,\c primitives,\c colors,\c opacities} defines a valid 3d object.
  13864. /**
  13865. Return \c true is the 3d object represented by the set {\c *this,\c primitives,\c colors,\c opacities} defines a
  13866. valid 3d object, and \c false otherwise. The vertex coordinates are defined by the instance image.
  13867. \param primitives List of primitives of the 3d object.
  13868. \param colors List of colors of the 3d object.
  13869. \param opacities List (or image) of opacities of the 3d object.
  13870. \param full_check Tells if full checking of the 3d object must be performed.
  13871. \param[out] error_message C-string to contain the error message, if the test does not succeed.
  13872. \note
  13873. - Set \c full_checking to \c false to speed-up the 3d object checking. In this case, only the size of
  13874. each 3d object component is checked.
  13875. - Size of the string \c error_message should be at least 128-bytes long, to be able to contain the error message.
  13876. **/
  13877. template<typename tp, typename tc, typename to>
  13878. bool is_object3d(const CImgList<tp>& primitives,
  13879. const CImgList<tc>& colors,
  13880. const to& opacities,
  13881. const bool full_check=true,
  13882. char *const error_message=0) const {
  13883. if (error_message) *error_message = 0;
  13884. // Check consistency for the particular case of an empty 3d object.
  13885. if (is_empty()) {
  13886. if (primitives || colors || opacities) {
  13887. if (error_message) cimg_sprintf(error_message,
  13888. "3d object (%u,%u) defines no vertices but %u primitives, "
  13889. "%u colors and %lu opacities",
  13890. _width,primitives._width,primitives._width,
  13891. colors._width,(unsigned long)opacities.size());
  13892. return false;
  13893. }
  13894. return true;
  13895. }
  13896. // Check consistency of vertices.
  13897. if (_height!=3 || _depth>1 || _spectrum>1) { // Check vertices dimensions.
  13898. if (error_message) cimg_sprintf(error_message,
  13899. "3d object (%u,%u) has invalid vertex dimensions (%u,%u,%u,%u)",
  13900. _width,primitives._width,_width,_height,_depth,_spectrum);
  13901. return false;
  13902. }
  13903. if (colors._width>primitives._width + 1) {
  13904. if (error_message) cimg_sprintf(error_message,
  13905. "3d object (%u,%u) defines %u colors",
  13906. _width,primitives._width,colors._width);
  13907. return false;
  13908. }
  13909. if (opacities.size()>primitives._width) {
  13910. if (error_message) cimg_sprintf(error_message,
  13911. "3d object (%u,%u) defines %lu opacities",
  13912. _width,primitives._width,(unsigned long)opacities.size());
  13913. return false;
  13914. }
  13915. if (!full_check) return true;
  13916. // Check consistency of primitives.
  13917. cimglist_for(primitives,l) {
  13918. const CImg<tp>& primitive = primitives[l];
  13919. const unsigned int psiz = (unsigned int)primitive.size();
  13920. switch (psiz) {
  13921. case 1 : { // Point.
  13922. const unsigned int i0 = (unsigned int)primitive(0);
  13923. if (i0>=_width) {
  13924. if (error_message) cimg_sprintf(error_message,
  13925. "3d object (%u,%u) refers to invalid vertex indice %u in "
  13926. "point primitive [%u]",
  13927. _width,primitives._width,i0,l);
  13928. return false;
  13929. }
  13930. } break;
  13931. case 5 : { // Sphere.
  13932. const unsigned int
  13933. i0 = (unsigned int)primitive(0),
  13934. i1 = (unsigned int)primitive(1);
  13935. if (i0>=_width || i1>=_width) {
  13936. if (error_message) cimg_sprintf(error_message,
  13937. "3d object (%u,%u) refers to invalid vertex indices (%u,%u) in "
  13938. "sphere primitive [%u]",
  13939. _width,primitives._width,i0,i1,l);
  13940. return false;
  13941. }
  13942. } break;
  13943. case 2 : // Segment.
  13944. case 6 : {
  13945. const unsigned int
  13946. i0 = (unsigned int)primitive(0),
  13947. i1 = (unsigned int)primitive(1);
  13948. if (i0>=_width || i1>=_width) {
  13949. if (error_message) cimg_sprintf(error_message,
  13950. "3d object (%u,%u) refers to invalid vertex indices (%u,%u) in "
  13951. "segment primitive [%u]",
  13952. _width,primitives._width,i0,i1,l);
  13953. return false;
  13954. }
  13955. } break;
  13956. case 3 : // Triangle.
  13957. case 9 : {
  13958. const unsigned int
  13959. i0 = (unsigned int)primitive(0),
  13960. i1 = (unsigned int)primitive(1),
  13961. i2 = (unsigned int)primitive(2);
  13962. if (i0>=_width || i1>=_width || i2>=_width) {
  13963. if (error_message) cimg_sprintf(error_message,
  13964. "3d object (%u,%u) refers to invalid vertex indices (%u,%u,%u) in "
  13965. "triangle primitive [%u]",
  13966. _width,primitives._width,i0,i1,i2,l);
  13967. return false;
  13968. }
  13969. } break;
  13970. case 4 : // Quadrangle.
  13971. case 12 : {
  13972. const unsigned int
  13973. i0 = (unsigned int)primitive(0),
  13974. i1 = (unsigned int)primitive(1),
  13975. i2 = (unsigned int)primitive(2),
  13976. i3 = (unsigned int)primitive(3);
  13977. if (i0>=_width || i1>=_width || i2>=_width || i3>=_width) {
  13978. if (error_message) cimg_sprintf(error_message,
  13979. "3d object (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in "
  13980. "quadrangle primitive [%u]",
  13981. _width,primitives._width,i0,i1,i2,i3,l);
  13982. return false;
  13983. }
  13984. } break;
  13985. default :
  13986. if (error_message) cimg_sprintf(error_message,
  13987. "3d object (%u,%u) defines an invalid primitive [%u] of size %u",
  13988. _width,primitives._width,l,(unsigned int)psiz);
  13989. return false;
  13990. }
  13991. }
  13992. // Check consistency of colors.
  13993. cimglist_for(colors,c) {
  13994. const CImg<tc>& color = colors[c];
  13995. if (!color) {
  13996. if (error_message) cimg_sprintf(error_message,
  13997. "3d object (%u,%u) defines no color for primitive [%u]",
  13998. _width,primitives._width,c);
  13999. return false;
  14000. }
  14001. }
  14002. // Check consistency of light texture.
  14003. if (colors._width>primitives._width) {
  14004. const CImg<tc> &light = colors.back();
  14005. if (!light || light._depth>1) {
  14006. if (error_message) cimg_sprintf(error_message,
  14007. "3d object (%u,%u) defines an invalid light texture (%u,%u,%u,%u)",
  14008. _width,primitives._width,light._width,
  14009. light._height,light._depth,light._spectrum);
  14010. return false;
  14011. }
  14012. }
  14013. return true;
  14014. }
  14015. //! Test if image instance represents a valid serialization of a 3d object.
  14016. /**
  14017. Return \c true if the image instance represents a valid serialization of a 3d object, and \c false otherwise.
  14018. \param full_check Tells if full checking of the instance must be performed.
  14019. \param[out] error_message C-string to contain the error message, if the test does not succeed.
  14020. \note
  14021. - Set \c full_check to \c false to speed-up the 3d object checking. In this case, only the size of
  14022. each 3d object component is checked.
  14023. - Size of the string \c error_message should be at least 128-bytes long, to be able to contain the error message.
  14024. **/
  14025. bool is_CImg3d(const bool full_check=true, char *const error_message=0) const {
  14026. if (error_message) *error_message = 0;
  14027. // Check instance dimension and header.
  14028. if (_width!=1 || _height<8 || _depth!=1 || _spectrum!=1) {
  14029. if (error_message) cimg_sprintf(error_message,
  14030. "CImg3d has invalid dimensions (%u,%u,%u,%u)",
  14031. _width,_height,_depth,_spectrum);
  14032. return false;
  14033. }
  14034. const T *ptrs = _data, *const ptre = end();
  14035. if (!_is_CImg3d(*(ptrs++),'C') || !_is_CImg3d(*(ptrs++),'I') || !_is_CImg3d(*(ptrs++),'m') ||
  14036. !_is_CImg3d(*(ptrs++),'g') || !_is_CImg3d(*(ptrs++),'3') || !_is_CImg3d(*(ptrs++),'d')) {
  14037. if (error_message) cimg_sprintf(error_message,
  14038. "CImg3d header not found");
  14039. return false;
  14040. }
  14041. const unsigned int
  14042. nb_points = cimg::float2uint((float)*(ptrs++)),
  14043. nb_primitives = cimg::float2uint((float)*(ptrs++));
  14044. // Check consistency of number of vertices / primitives.
  14045. if (!full_check) {
  14046. const ulongT minimal_size = 8UL + 3*nb_points + 6*nb_primitives;
  14047. if (_data + minimal_size>ptre) {
  14048. if (error_message) cimg_sprintf(error_message,
  14049. "CImg3d (%u,%u) has only %lu values, while at least %lu values were expected",
  14050. nb_points,nb_primitives,(unsigned long)size(),(unsigned long)minimal_size);
  14051. return false;
  14052. }
  14053. }
  14054. // Check consistency of vertex data.
  14055. if (!nb_points) {
  14056. if (nb_primitives) {
  14057. if (error_message) cimg_sprintf(error_message,
  14058. "CImg3d (%u,%u) defines no vertices but %u primitives",
  14059. nb_points,nb_primitives,nb_primitives);
  14060. return false;
  14061. }
  14062. if (ptrs!=ptre) {
  14063. if (error_message) cimg_sprintf(error_message,
  14064. "CImg3d (%u,%u) is an empty object but contains %u value%s "
  14065. "more than expected",
  14066. nb_points,nb_primitives,(unsigned int)(ptre - ptrs),(ptre - ptrs)>1?"s":"");
  14067. return false;
  14068. }
  14069. return true;
  14070. }
  14071. if (ptrs + 3*nb_points>ptre) {
  14072. if (error_message) cimg_sprintf(error_message,
  14073. "CImg3d (%u,%u) defines only %u vertices data",
  14074. nb_points,nb_primitives,(unsigned int)(ptre - ptrs)/3);
  14075. return false;
  14076. }
  14077. ptrs+=3*nb_points;
  14078. // Check consistency of primitive data.
  14079. if (ptrs==ptre) {
  14080. if (error_message) cimg_sprintf(error_message,
  14081. "CImg3d (%u,%u) defines %u vertices but no primitive",
  14082. nb_points,nb_primitives,nb_points);
  14083. return false;
  14084. }
  14085. if (!full_check) return true;
  14086. for (unsigned int p = 0; p<nb_primitives; ++p) {
  14087. const unsigned int nb_inds = (unsigned int)*(ptrs++);
  14088. switch (nb_inds) {
  14089. case 1 : { // Point.
  14090. const unsigned int i0 = cimg::float2uint((float)*(ptrs++));
  14091. if (i0>=nb_points) {
  14092. if (error_message) cimg_sprintf(error_message,
  14093. "CImg3d (%u,%u) refers to invalid vertex indice %u in point primitive [%u]",
  14094. nb_points,nb_primitives,i0,p);
  14095. return false;
  14096. }
  14097. } break;
  14098. case 5 : { // Sphere.
  14099. const unsigned int
  14100. i0 = cimg::float2uint((float)*(ptrs++)),
  14101. i1 = cimg::float2uint((float)*(ptrs++));
  14102. ptrs+=3;
  14103. if (i0>=nb_points || i1>=nb_points) {
  14104. if (error_message) cimg_sprintf(error_message,
  14105. "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in "
  14106. "sphere primitive [%u]",
  14107. nb_points,nb_primitives,i0,i1,p);
  14108. return false;
  14109. }
  14110. } break;
  14111. case 2 : case 6 : { // Segment.
  14112. const unsigned int
  14113. i0 = cimg::float2uint((float)*(ptrs++)),
  14114. i1 = cimg::float2uint((float)*(ptrs++));
  14115. if (nb_inds==6) ptrs+=4;
  14116. if (i0>=nb_points || i1>=nb_points) {
  14117. if (error_message) cimg_sprintf(error_message,
  14118. "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in "
  14119. "segment primitive [%u]",
  14120. nb_points,nb_primitives,i0,i1,p);
  14121. return false;
  14122. }
  14123. } break;
  14124. case 3 : case 9 : { // Triangle.
  14125. const unsigned int
  14126. i0 = cimg::float2uint((float)*(ptrs++)),
  14127. i1 = cimg::float2uint((float)*(ptrs++)),
  14128. i2 = cimg::float2uint((float)*(ptrs++));
  14129. if (nb_inds==9) ptrs+=6;
  14130. if (i0>=nb_points || i1>=nb_points || i2>=nb_points) {
  14131. if (error_message) cimg_sprintf(error_message,
  14132. "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u) in "
  14133. "triangle primitive [%u]",
  14134. nb_points,nb_primitives,i0,i1,i2,p);
  14135. return false;
  14136. }
  14137. } break;
  14138. case 4 : case 12 : { // Quadrangle.
  14139. const unsigned int
  14140. i0 = cimg::float2uint((float)*(ptrs++)),
  14141. i1 = cimg::float2uint((float)*(ptrs++)),
  14142. i2 = cimg::float2uint((float)*(ptrs++)),
  14143. i3 = cimg::float2uint((float)*(ptrs++));
  14144. if (nb_inds==12) ptrs+=8;
  14145. if (i0>=nb_points || i1>=nb_points || i2>=nb_points || i3>=nb_points) {
  14146. if (error_message) cimg_sprintf(error_message,
  14147. "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in "
  14148. "quadrangle primitive [%u]",
  14149. nb_points,nb_primitives,i0,i1,i2,i3,p);
  14150. return false;
  14151. }
  14152. } break;
  14153. default :
  14154. if (error_message) cimg_sprintf(error_message,
  14155. "CImg3d (%u,%u) defines an invalid primitive [%u] of size %u",
  14156. nb_points,nb_primitives,p,nb_inds);
  14157. return false;
  14158. }
  14159. if (ptrs>ptre) {
  14160. if (error_message) cimg_sprintf(error_message,
  14161. "CImg3d (%u,%u) has incomplete primitive data for primitive [%u], "
  14162. "%u values missing",
  14163. nb_points,nb_primitives,p,(unsigned int)(ptrs - ptre));
  14164. return false;
  14165. }
  14166. }
  14167. // Check consistency of color data.
  14168. if (ptrs==ptre) {
  14169. if (error_message) cimg_sprintf(error_message,
  14170. "CImg3d (%u,%u) defines no color/texture data",
  14171. nb_points,nb_primitives);
  14172. return false;
  14173. }
  14174. for (unsigned int c = 0; c<nb_primitives; ++c) {
  14175. if (*(ptrs++)!=(T)-128) ptrs+=2;
  14176. else if ((ptrs+=3)<ptre) {
  14177. const unsigned int
  14178. w = (unsigned int)*(ptrs - 3),
  14179. h = (unsigned int)*(ptrs - 2),
  14180. s = (unsigned int)*(ptrs - 1);
  14181. if (!h && !s) {
  14182. if (w>=c) {
  14183. if (error_message) cimg_sprintf(error_message,
  14184. "CImg3d (%u,%u) refers to invalid shared sprite/texture indice %u "
  14185. "for primitive [%u]",
  14186. nb_points,nb_primitives,w,c);
  14187. return false;
  14188. }
  14189. } else ptrs+=w*h*s;
  14190. }
  14191. if (ptrs>ptre) {
  14192. if (error_message) cimg_sprintf(error_message,
  14193. "CImg3d (%u,%u) has incomplete color/texture data for primitive [%u], "
  14194. "%u values missing",
  14195. nb_points,nb_primitives,c,(unsigned int)(ptrs - ptre));
  14196. return false;
  14197. }
  14198. }
  14199. // Check consistency of opacity data.
  14200. if (ptrs==ptre) {
  14201. if (error_message) cimg_sprintf(error_message,
  14202. "CImg3d (%u,%u) defines no opacity data",
  14203. nb_points,nb_primitives);
  14204. return false;
  14205. }
  14206. for (unsigned int o = 0; o<nb_primitives; ++o) {
  14207. if (*(ptrs++)==(T)-128 && (ptrs+=3)<ptre) {
  14208. const unsigned int
  14209. w = (unsigned int)*(ptrs - 3),
  14210. h = (unsigned int)*(ptrs - 2),
  14211. s = (unsigned int)*(ptrs - 1);
  14212. if (!h && !s) {
  14213. if (w>=o) {
  14214. if (error_message) cimg_sprintf(error_message,
  14215. "CImg3d (%u,%u) refers to invalid shared opacity indice %u "
  14216. "for primitive [%u]",
  14217. nb_points,nb_primitives,w,o);
  14218. return false;
  14219. }
  14220. } else ptrs+=w*h*s;
  14221. }
  14222. if (ptrs>ptre) {
  14223. if (error_message) cimg_sprintf(error_message,
  14224. "CImg3d (%u,%u) has incomplete opacity data for primitive [%u]",
  14225. nb_points,nb_primitives,o);
  14226. return false;
  14227. }
  14228. }
  14229. // Check end of data.
  14230. if (ptrs<ptre) {
  14231. if (error_message) cimg_sprintf(error_message,
  14232. "CImg3d (%u,%u) contains %u value%s more than expected",
  14233. nb_points,nb_primitives,(unsigned int)(ptre - ptrs),(ptre - ptrs)>1?"s":"");
  14234. return false;
  14235. }
  14236. return true;
  14237. }
  14238. static bool _is_CImg3d(const T val, const char c) {
  14239. return val>=(T)c && val<(T)(c + 1);
  14240. }
  14241. //@}
  14242. //-------------------------------------
  14243. //
  14244. //! \name Mathematical Functions
  14245. //@{
  14246. //-------------------------------------
  14247. // Define the math formula parser/compiler and expression evaluator.
  14248. struct _cimg_math_parser {
  14249. CImg<doubleT> mem;
  14250. CImg<intT> memtype;
  14251. CImgList<ulongT> _code, &code, code_init, code_end;
  14252. CImg<ulongT> opcode;
  14253. const CImg<ulongT> *p_code_end, *p_code;
  14254. const CImg<ulongT> *const p_break;
  14255. CImg<charT> expr, pexpr;
  14256. const CImg<T>& imgin;
  14257. const CImgList<T>& listin;
  14258. CImg<T> &imgout;
  14259. CImgList<T>& listout;
  14260. CImg<doubleT> _img_stats, &img_stats, constcache_vals;
  14261. CImgList<doubleT> _list_stats, &list_stats, _list_median, &list_median;
  14262. CImg<uintT> mem_img_stats, constcache_inds;
  14263. CImg<uintT> level, variable_pos, reserved_label;
  14264. CImgList<charT> variable_def, macro_def, macro_body;
  14265. CImgList<boolT> macro_body_is_string;
  14266. char *user_macro;
  14267. unsigned int mempos, mem_img_median, debug_indent, result_dim, break_type, constcache_size;
  14268. bool is_parallelizable, is_fill, need_input_copy;
  14269. double *result;
  14270. const char *const calling_function, *s_op, *ss_op;
  14271. typedef double (*mp_func)(_cimg_math_parser&);
  14272. #define _cimg_mp_is_constant(arg) (memtype[arg]==1) // Is constant value?
  14273. #define _cimg_mp_is_scalar(arg) (memtype[arg]<2) // Is scalar value?
  14274. #define _cimg_mp_is_comp(arg) (!memtype[arg]) // Is computation value?
  14275. #define _cimg_mp_is_variable(arg) (memtype[arg]==-1) // Is scalar variable?
  14276. #define _cimg_mp_is_vector(arg) (memtype[arg]>1) // Is vector?
  14277. #define _cimg_mp_size(arg) (_cimg_mp_is_scalar(arg)?0U:(unsigned int)memtype[arg] - 1) // Size (0=scalar, N>0=vectorN)
  14278. #define _cimg_mp_calling_function calling_function_s()._data
  14279. #define _cimg_mp_op(s) s_op = s; ss_op = ss
  14280. #define _cimg_mp_check_type(arg,n_arg,mode,N) check_type(arg,n_arg,mode,N,ss,se,saved_char)
  14281. #define _cimg_mp_check_constant(arg,n_arg,mode) check_constant(arg,n_arg,mode,ss,se,saved_char)
  14282. #define _cimg_mp_check_matrix_square(arg,n_arg) check_matrix_square(arg,n_arg,ss,se,saved_char)
  14283. #define _cimg_mp_check_vector0(dim) check_vector0(dim,ss,se,saved_char)
  14284. #define _cimg_mp_check_list(is_out) check_list(is_out,ss,se,saved_char)
  14285. #define _cimg_mp_defunc(mp) (*(mp_func)(*(mp).opcode))(mp)
  14286. #define _cimg_mp_return(x) { *se = saved_char; s_op = previous_s_op; ss_op = previous_ss_op; return x; }
  14287. #define _cimg_mp_return_nan() _cimg_mp_return(_cimg_mp_slot_nan)
  14288. #define _cimg_mp_constant(val) _cimg_mp_return(constant((double)(val)))
  14289. #define _cimg_mp_scalar0(op) _cimg_mp_return(scalar0(op))
  14290. #define _cimg_mp_scalar1(op,i1) _cimg_mp_return(scalar1(op,i1))
  14291. #define _cimg_mp_scalar2(op,i1,i2) _cimg_mp_return(scalar2(op,i1,i2))
  14292. #define _cimg_mp_scalar3(op,i1,i2,i3) _cimg_mp_return(scalar3(op,i1,i2,i3))
  14293. #define _cimg_mp_scalar4(op,i1,i2,i3,i4) _cimg_mp_return(scalar4(op,i1,i2,i3,i4))
  14294. #define _cimg_mp_scalar5(op,i1,i2,i3,i4,i5) _cimg_mp_return(scalar5(op,i1,i2,i3,i4,i5))
  14295. #define _cimg_mp_scalar6(op,i1,i2,i3,i4,i5,i6) _cimg_mp_return(scalar6(op,i1,i2,i3,i4,i5,i6))
  14296. #define _cimg_mp_scalar7(op,i1,i2,i3,i4,i5,i6,i7) _cimg_mp_return(scalar7(op,i1,i2,i3,i4,i5,i6,i7))
  14297. #define _cimg_mp_vector1_v(op,i1) _cimg_mp_return(vector1_v(op,i1))
  14298. #define _cimg_mp_vector2_sv(op,i1,i2) _cimg_mp_return(vector2_sv(op,i1,i2))
  14299. #define _cimg_mp_vector2_vs(op,i1,i2) _cimg_mp_return(vector2_vs(op,i1,i2))
  14300. #define _cimg_mp_vector2_vv(op,i1,i2) _cimg_mp_return(vector2_vv(op,i1,i2))
  14301. #define _cimg_mp_vector3_vss(op,i1,i2,i3) _cimg_mp_return(vector3_vss(op,i1,i2,i3))
  14302. // Constructors.
  14303. _cimg_math_parser(const char *const expression, const char *const funcname=0,
  14304. const CImg<T>& img_input=CImg<T>::const_empty(), CImg<T> *const img_output=0,
  14305. const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0,
  14306. const bool _is_fill=false):
  14307. code(_code),p_break((CImg<ulongT>*)0 - 2),
  14308. imgin(img_input),listin(list_inputs?*list_inputs:CImgList<T>::const_empty()),
  14309. imgout(img_output?*img_output:CImg<T>::empty()),listout(list_outputs?*list_outputs:CImgList<T>::empty()),
  14310. img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),user_macro(0),
  14311. mem_img_median(~0U),debug_indent(0),result_dim(0),break_type(0),constcache_size(0),
  14312. is_parallelizable(true),is_fill(_is_fill),need_input_copy(false),
  14313. calling_function(funcname?funcname:"cimg_math_parser") {
  14314. if (!expression || !*expression)
  14315. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  14316. "CImg<%s>::%s: Empty expression.",
  14317. pixel_type(),_cimg_mp_calling_function);
  14318. const char *_expression = expression;
  14319. while (*_expression && ((signed char)*_expression<=' ' || *_expression==';')) ++_expression;
  14320. CImg<charT>::string(_expression).move_to(expr);
  14321. char *ps = &expr.back() - 1;
  14322. while (ps>expr._data && ((signed char)*ps<=' ' || *ps==';')) --ps;
  14323. *(++ps) = 0; expr._width = (unsigned int)(ps - expr._data + 1);
  14324. // Ease the retrieval of previous non-space characters afterwards.
  14325. pexpr.assign(expr._width);
  14326. char c, *pe = pexpr._data;
  14327. for (ps = expr._data, c = ' '; *ps; ++ps) {
  14328. if ((signed char)*ps>' ') c = *ps; else *ps = ' ';
  14329. *(pe++) = c;
  14330. }
  14331. *pe = 0;
  14332. level = get_level(expr);
  14333. // Init constant values.
  14334. #define _cimg_mp_interpolation (reserved_label[29]!=~0U?reserved_label[29]:0)
  14335. #define _cimg_mp_boundary (reserved_label[30]!=~0U?reserved_label[30]:0)
  14336. #define _cimg_mp_slot_nan 29
  14337. #define _cimg_mp_slot_x 30
  14338. #define _cimg_mp_slot_y 31
  14339. #define _cimg_mp_slot_z 32
  14340. #define _cimg_mp_slot_c 33
  14341. mem.assign(96);
  14342. for (unsigned int i = 0; i<=10; ++i) mem[i] = (double)i; // mem[0-10] = 0...10
  14343. for (unsigned int i = 1; i<=5; ++i) mem[i + 10] = -(double)i; // mem[11-15] = -1...-5
  14344. mem[16] = 0.5;
  14345. mem[17] = 0; // thread_id
  14346. mem[18] = (double)imgin._width; // w
  14347. mem[19] = (double)imgin._height; // h
  14348. mem[20] = (double)imgin._depth; // d
  14349. mem[21] = (double)imgin._spectrum; // s
  14350. mem[22] = (double)imgin._is_shared; // r
  14351. mem[23] = (double)imgin._width*imgin._height; // wh
  14352. mem[24] = (double)imgin._width*imgin._height*imgin._depth; // whd
  14353. mem[25] = (double)imgin._width*imgin._height*imgin._depth*imgin._spectrum; // whds
  14354. mem[26] = (double)listin._width; // l
  14355. mem[27] = std::exp(1.0); // e
  14356. mem[28] = cimg::PI; // pi
  14357. mem[_cimg_mp_slot_nan] = cimg::type<double>::nan(); // nan
  14358. // Set value property :
  14359. // { -2 = other | -1 = variable | 0 = computation value |
  14360. // 1 = compile-time constant | N>1 = constant ptr to vector[N-1] }.
  14361. memtype.assign(mem._width,1,1,1,0);
  14362. for (unsigned int i = 0; i<_cimg_mp_slot_x; ++i) memtype[i] = 1;
  14363. memtype[17] = 0;
  14364. memtype[_cimg_mp_slot_x] = memtype[_cimg_mp_slot_y] = memtype[_cimg_mp_slot_z] = memtype[_cimg_mp_slot_c] = -2;
  14365. mempos = _cimg_mp_slot_c + 1;
  14366. variable_pos.assign(8);
  14367. reserved_label.assign(128,1,1,1,~0U);
  14368. // reserved_label[4-28] are used to store these two-char variables:
  14369. // [0] = wh, [1] = whd, [2] = whds, [3] = pi, [4] = im, [5] = iM, [6] = ia, [7] = iv,
  14370. // [8] = is, [9] = ip, [10] = ic, [11] = xm, [12] = ym, [13] = zm, [14] = cm, [15] = xM,
  14371. // [16] = yM, [17] = zM, [18]=cM, [19]=i0...[28]=i9, [29] = interpolation, [30] = boundary
  14372. // Compile expression into a serie of opcodes.
  14373. s_op = ""; ss_op = expr._data;
  14374. const unsigned int ind_result = compile(expr._data,expr._data + expr._width - 1,0,0,false);
  14375. if (!_cimg_mp_is_constant(ind_result)) {
  14376. if (_cimg_mp_is_vector(ind_result))
  14377. CImg<doubleT>(&mem[ind_result] + 1,_cimg_mp_size(ind_result),1,1,1,true).
  14378. fill(cimg::type<double>::nan());
  14379. else mem[ind_result] = cimg::type<double>::nan();
  14380. }
  14381. // Free resources used for compiling expression and prepare evaluation.
  14382. result_dim = _cimg_mp_size(ind_result);
  14383. if (mem._width>=256 && mem._width - mempos>=mem._width/2) mem.resize(mempos,1,1,1,-1);
  14384. result = mem._data + ind_result;
  14385. memtype.assign();
  14386. constcache_vals.assign();
  14387. constcache_inds.assign();
  14388. level.assign();
  14389. variable_pos.assign();
  14390. reserved_label.assign();
  14391. expr.assign();
  14392. pexpr.assign();
  14393. opcode.assign();
  14394. opcode._is_shared = true;
  14395. // Execute init() bloc if any specified.
  14396. if (code_init) {
  14397. mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0;
  14398. p_code_end = code_init.end();
  14399. for (p_code = code_init; p_code<p_code_end; ++p_code) {
  14400. opcode._data = p_code->_data;
  14401. const ulongT target = opcode[1];
  14402. mem[target] = _cimg_mp_defunc(*this);
  14403. }
  14404. }
  14405. p_code_end = code.end();
  14406. }
  14407. _cimg_math_parser():
  14408. code(_code),p_code_end(0),p_break((CImg<ulongT>*)0 - 2),
  14409. imgin(CImg<T>::const_empty()),listin(CImgList<T>::const_empty()),
  14410. imgout(CImg<T>::empty()),listout(CImgList<T>::empty()),
  14411. img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),debug_indent(0),
  14412. result_dim(0),break_type(0),constcache_size(0),is_parallelizable(true),is_fill(false),need_input_copy(false),
  14413. calling_function(0) {
  14414. mem.assign(1 + _cimg_mp_slot_c,1,1,1,0); // Allow to skip 'is_empty?' test in operator()()
  14415. result = mem._data;
  14416. }
  14417. _cimg_math_parser(const _cimg_math_parser& mp):
  14418. mem(mp.mem),code(mp.code),p_code_end(mp.p_code_end),p_break(mp.p_break),
  14419. imgin(mp.imgin),listin(mp.listin),imgout(mp.imgout),listout(mp.listout),img_stats(mp.img_stats),
  14420. list_stats(mp.list_stats),list_median(mp.list_median),debug_indent(0),result_dim(mp.result_dim),
  14421. break_type(0),constcache_size(0),is_parallelizable(mp.is_parallelizable),is_fill(mp.is_fill),
  14422. need_input_copy(mp.need_input_copy), result(mem._data + (mp.result - mp.mem._data)),calling_function(0) {
  14423. #ifdef cimg_use_openmp
  14424. mem[17] = omp_get_thread_num();
  14425. #endif
  14426. opcode.assign();
  14427. opcode._is_shared = true;
  14428. }
  14429. // Count parentheses/brackets level of each character of the expression.
  14430. CImg<uintT> get_level(CImg<charT>& expr) const {
  14431. bool is_escaped = false, next_is_escaped = false;
  14432. unsigned int mode = 0, next_mode = 0; // { 0=normal | 1=char-string | 2=vector-string
  14433. CImg<uintT> res(expr._width - 1);
  14434. unsigned int *pd = res._data;
  14435. int level = 0;
  14436. for (const char *ps = expr._data; *ps && level>=0; ++ps) {
  14437. if (!is_escaped && !next_is_escaped && *ps=='\\') next_is_escaped = true;
  14438. if (!is_escaped && *ps=='\'') { // Non-escaped character
  14439. if (!mode && ps>expr._data && *(ps - 1)=='[') next_mode = mode = 2; // Start vector-string
  14440. else if (mode==2 && *(ps + 1)==']') next_mode = !mode; // End vector-string
  14441. else if (mode<2) next_mode = mode?(mode = 0):1; // Start/end char-string
  14442. }
  14443. *(pd++) = (unsigned int)(mode>=1 || is_escaped?level + (mode==1):
  14444. *ps=='(' || *ps=='['?level++:
  14445. *ps==')' || *ps==']'?--level:
  14446. level);
  14447. mode = next_mode;
  14448. is_escaped = next_is_escaped;
  14449. next_is_escaped = false;
  14450. }
  14451. if (mode) {
  14452. cimg::strellipsize(expr,64);
  14453. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  14454. "CImg<%s>::%s: Unterminated string literal, in expression '%s'.",
  14455. pixel_type(),_cimg_mp_calling_function,
  14456. expr._data);
  14457. }
  14458. if (level) {
  14459. cimg::strellipsize(expr,64);
  14460. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  14461. "CImg<%s>::%s: Unbalanced parentheses/brackets, in expression '%s'.",
  14462. pixel_type(),_cimg_mp_calling_function,
  14463. expr._data);
  14464. }
  14465. return res;
  14466. }
  14467. // Tell for each character of an expression if it is inside a string or not.
  14468. CImg<boolT> is_inside_string(CImg<charT>& expr) const {
  14469. bool is_escaped = false, next_is_escaped = false;
  14470. unsigned int mode = 0, next_mode = 0; // { 0=normal | 1=char-string | 2=vector-string
  14471. CImg<boolT> res = CImg<charT>::string(expr);
  14472. bool *pd = res._data;
  14473. for (const char *ps = expr._data; *ps; ++ps) {
  14474. if (!next_is_escaped && *ps=='\\') next_is_escaped = true;
  14475. if (!is_escaped && *ps=='\'') { // Non-escaped character
  14476. if (!mode && ps>expr._data && *(ps - 1)=='[') next_mode = mode = 2; // Start vector-string
  14477. else if (mode==2 && *(ps + 1)==']') next_mode = !mode; // End vector-string
  14478. else if (mode<2) next_mode = mode?(mode = 0):1; // Start/end char-string
  14479. }
  14480. *(pd++) = mode>=1 || is_escaped;
  14481. mode = next_mode;
  14482. is_escaped = next_is_escaped;
  14483. next_is_escaped = false;
  14484. }
  14485. return res;
  14486. }
  14487. // Compilation procedure.
  14488. unsigned int compile(char *ss, char *se, const unsigned int depth, unsigned int *const p_ref,
  14489. const bool is_single) {
  14490. if (depth>256) {
  14491. cimg::strellipsize(expr,64);
  14492. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  14493. "CImg<%s>::%s: Call stack overflow (infinite recursion?), "
  14494. "in expression '%s%s%s'.",
  14495. pixel_type(),_cimg_mp_calling_function,
  14496. (ss - 4)>expr._data?"...":"",
  14497. (ss - 4)>expr._data?ss - 4:expr._data,
  14498. se<&expr.back()?"...":"");
  14499. }
  14500. char c1, c2, c3, c4;
  14501. // Simplify expression when possible.
  14502. do {
  14503. c2 = 0;
  14504. if (ss<se) {
  14505. while (*ss && ((signed char)*ss<=' ' || *ss==';')) ++ss;
  14506. while (se>ss && ((signed char)(c1 = *(se - 1))<=' ' || c1==';')) --se;
  14507. }
  14508. while (*ss=='(' && *(se - 1)==')' && std::strchr(ss,')')==se - 1) {
  14509. ++ss; --se; c2 = 1;
  14510. }
  14511. } while (c2 && ss<se);
  14512. if (se<=ss || !*ss) {
  14513. cimg::strellipsize(expr,64);
  14514. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  14515. "CImg<%s>::%s: %s%s Missing %s, in expression '%s%s%s'.",
  14516. pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
  14517. *s_op=='F'?"argument":"item",
  14518. (ss_op - 4)>expr._data?"...":"",
  14519. (ss_op - 4)>expr._data?ss_op - 4:expr._data,
  14520. ss_op + std::strlen(ss_op)<&expr.back()?"...":"");
  14521. }
  14522. const char *const previous_s_op = s_op, *const previous_ss_op = ss_op;
  14523. const unsigned int depth1 = depth + 1;
  14524. unsigned int pos, p1, p2, p3, arg1, arg2, arg3, arg4, arg5, arg6;
  14525. char
  14526. *const se1 = se - 1, *const se2 = se - 2, *const se3 = se - 3,
  14527. *const ss1 = ss + 1, *const ss2 = ss + 2, *const ss3 = ss + 3, *const ss4 = ss + 4,
  14528. *const ss5 = ss + 5, *const ss6 = ss + 6, *const ss7 = ss + 7, *const ss8 = ss + 8,
  14529. *s, *ps, *ns, *s0, *s1, *s2, *s3, sep = 0, end = 0;
  14530. double val, val1, val2;
  14531. mp_func op;
  14532. // 'p_ref' is a 'unsigned int[7]' used to return a reference to an image or vector value
  14533. // linked to the returned memory slot (reference that cannot be determined at compile time).
  14534. // p_ref[0] can be { 0 = scalar (unlinked) | 1 = vector value | 2 = image value (offset) |
  14535. // 3 = image value (coordinates) | 4 = image value as a vector (offsets) |
  14536. // 5 = image value as a vector (coordinates) }.
  14537. // Depending on p_ref[0], the remaining p_ref[k] have the following meaning:
  14538. // When p_ref[0]==0, p_ref is actually unlinked.
  14539. // When p_ref[0]==1, p_ref = [ 1, vector_ind, offset ].
  14540. // When p_ref[0]==2, p_ref = [ 2, image_ind (or ~0U), is_relative, offset ].
  14541. // When p_ref[0]==3, p_ref = [ 3, image_ind (or ~0U), is_relative, x, y, z, c ].
  14542. // When p_ref[0]==4, p_ref = [ 4, image_ind (or ~0U), is_relative, offset ].
  14543. // When p_ref[0]==5, p_ref = [ 5, image_ind (or ~0U), is_relative, x, y, z ].
  14544. 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; }
  14545. const char saved_char = *se; *se = 0;
  14546. const unsigned int clevel = level[ss - expr._data], clevel1 = clevel + 1;
  14547. bool is_sth, is_relative;
  14548. CImg<uintT> ref;
  14549. CImg<charT> variable_name;
  14550. CImgList<ulongT> l_opcode;
  14551. // Look for a single value or a pre-defined variable.
  14552. int nb = cimg_sscanf(ss,"%lf%c%c",&val,&(sep=0),&(end=0));
  14553. #if cimg_OS==2
  14554. // Check for +/-NaN and +/-inf as Microsoft's sscanf() version is not able
  14555. // to read those particular values.
  14556. if (!nb && (*ss=='+' || *ss=='-' || *ss=='i' || *ss=='I' || *ss=='n' || *ss=='N')) {
  14557. is_sth = true;
  14558. s = ss;
  14559. if (*s=='+') ++s; else if (*s=='-') { ++s; is_sth = false; }
  14560. if (!cimg::strcasecmp(s,"inf")) { val = cimg::type<double>::inf(); nb = 1; }
  14561. else if (!cimg::strcasecmp(s,"nan")) { val = cimg::type<double>::nan(); nb = 1; }
  14562. if (nb==1 && !is_sth) val = -val;
  14563. }
  14564. #endif
  14565. if (nb==1) _cimg_mp_constant(val);
  14566. if (nb==2 && sep=='%') _cimg_mp_constant(val/100);
  14567. if (ss1==se) switch (*ss) { // One-char reserved variable
  14568. case 'c' : _cimg_mp_return(reserved_label['c']!=~0U?reserved_label['c']:_cimg_mp_slot_c);
  14569. case 'd' : _cimg_mp_return(reserved_label['d']!=~0U?reserved_label['d']:20);
  14570. case 'e' : _cimg_mp_return(reserved_label['e']!=~0U?reserved_label['e']:27);
  14571. case 'h' : _cimg_mp_return(reserved_label['h']!=~0U?reserved_label['h']:19);
  14572. case 'l' : _cimg_mp_return(reserved_label['l']!=~0U?reserved_label['l']:26);
  14573. case 'r' : _cimg_mp_return(reserved_label['r']!=~0U?reserved_label['r']:22);
  14574. case 's' : _cimg_mp_return(reserved_label['s']!=~0U?reserved_label['s']:21);
  14575. case 't' : _cimg_mp_return(reserved_label['t']!=~0U?reserved_label['t']:17);
  14576. case 'w' : _cimg_mp_return(reserved_label['w']!=~0U?reserved_label['w']:18);
  14577. case 'x' : _cimg_mp_return(reserved_label['x']!=~0U?reserved_label['x']:_cimg_mp_slot_x);
  14578. case 'y' : _cimg_mp_return(reserved_label['y']!=~0U?reserved_label['y']:_cimg_mp_slot_y);
  14579. case 'z' : _cimg_mp_return(reserved_label['z']!=~0U?reserved_label['z']:_cimg_mp_slot_z);
  14580. case 'u' :
  14581. if (reserved_label['u']!=~0U) _cimg_mp_return(reserved_label['u']);
  14582. _cimg_mp_scalar2(mp_u,0,1);
  14583. case 'g' :
  14584. if (reserved_label['g']!=~0U) _cimg_mp_return(reserved_label['g']);
  14585. _cimg_mp_scalar0(mp_g);
  14586. case 'i' :
  14587. if (reserved_label['i']!=~0U) _cimg_mp_return(reserved_label['i']);
  14588. _cimg_mp_scalar0(mp_i);
  14589. case 'I' :
  14590. _cimg_mp_op("Variable 'I'");
  14591. if (reserved_label['I']!=~0U) _cimg_mp_return(reserved_label['I']);
  14592. _cimg_mp_check_vector0(imgin._spectrum);
  14593. need_input_copy = true;
  14594. pos = vector(imgin._spectrum);
  14595. CImg<ulongT>::vector((ulongT)mp_Joff,pos,0,0,imgin._spectrum).move_to(code);
  14596. _cimg_mp_return(pos);
  14597. case 'R' :
  14598. if (reserved_label['R']!=~0U) _cimg_mp_return(reserved_label['R']);
  14599. need_input_copy = true;
  14600. _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,0,0,0);
  14601. case 'G' :
  14602. if (reserved_label['G']!=~0U) _cimg_mp_return(reserved_label['G']);
  14603. need_input_copy = true;
  14604. _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,1,0,0);
  14605. case 'B' :
  14606. if (reserved_label['B']!=~0U) _cimg_mp_return(reserved_label['B']);
  14607. need_input_copy = true;
  14608. _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,2,0,0);
  14609. case 'A' :
  14610. if (reserved_label['A']!=~0U) _cimg_mp_return(reserved_label['A']);
  14611. need_input_copy = true;
  14612. _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,3,0,0);
  14613. }
  14614. else if (ss2==se) { // Two-chars reserved variable
  14615. arg1 = arg2 = ~0U;
  14616. if (*ss=='w' && *ss1=='h') // wh
  14617. _cimg_mp_return(reserved_label[0]!=~0U?reserved_label[0]:23);
  14618. if (*ss=='p' && *ss1=='i') // pi
  14619. _cimg_mp_return(reserved_label[3]!=~0U?reserved_label[3]:28);
  14620. if (*ss=='i') {
  14621. if (*ss1>='0' && *ss1<='9') { // i0...i9
  14622. pos = 19 + *ss1 - '0';
  14623. if (reserved_label[pos]!=~0U) _cimg_mp_return(reserved_label[pos]);
  14624. need_input_copy = true;
  14625. _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,pos - 19,0,0);
  14626. }
  14627. switch (*ss1) {
  14628. case 'm' : arg1 = 4; arg2 = 0; break; // im
  14629. case 'M' : arg1 = 5; arg2 = 1; break; // iM
  14630. case 'a' : arg1 = 6; arg2 = 2; break; // ia
  14631. case 'v' : arg1 = 7; arg2 = 3; break; // iv
  14632. case 's' : arg1 = 8; arg2 = 12; break; // is
  14633. case 'p' : arg1 = 9; arg2 = 13; break; // ip
  14634. case 'c' : // ic
  14635. if (reserved_label[10]!=~0U) _cimg_mp_return(reserved_label[10]);
  14636. if (mem_img_median==~0U) mem_img_median = imgin?constant(imgin.median()):0;
  14637. _cimg_mp_return(mem_img_median);
  14638. break;
  14639. }
  14640. }
  14641. else if (*ss1=='m') switch (*ss) {
  14642. case 'x' : arg1 = 11; arg2 = 4; break; // xm
  14643. case 'y' : arg1 = 12; arg2 = 5; break; // ym
  14644. case 'z' : arg1 = 13; arg2 = 6; break; // zm
  14645. case 'c' : arg1 = 14; arg2 = 7; break; // cm
  14646. }
  14647. else if (*ss1=='M') switch (*ss) {
  14648. case 'x' : arg1 = 15; arg2 = 8; break; // xM
  14649. case 'y' : arg1 = 16; arg2 = 9; break; // yM
  14650. case 'z' : arg1 = 17; arg2 = 10; break; // zM
  14651. case 'c' : arg1 = 18; arg2 = 11; break; // cM
  14652. }
  14653. if (arg1!=~0U) {
  14654. if (reserved_label[arg1]!=~0U) _cimg_mp_return(reserved_label[arg1]);
  14655. if (!img_stats) {
  14656. img_stats.assign(1,14,1,1,0).fill(imgin.get_stats(),false);
  14657. mem_img_stats.assign(1,14,1,1,~0U);
  14658. }
  14659. if (mem_img_stats[arg2]==~0U) mem_img_stats[arg2] = constant(img_stats[arg2]);
  14660. _cimg_mp_return(mem_img_stats[arg2]);
  14661. }
  14662. } else if (ss3==se) { // Three-chars reserved variable
  14663. if (*ss=='w' && *ss1=='h' && *ss2=='d') // whd
  14664. _cimg_mp_return(reserved_label[1]!=~0U?reserved_label[1]:24);
  14665. } else if (ss4==se) { // Four-chars reserved variable
  14666. if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='s') // whds
  14667. _cimg_mp_return(reserved_label[2]!=~0U?reserved_label[2]:25);
  14668. }
  14669. pos = ~0U;
  14670. is_sth = false;
  14671. for (s0 = ss, s = ss1; s<se1; ++s)
  14672. if (*s==';' && level[s - expr._data]==clevel) { // Separator ';'
  14673. arg1 = code_end._width;
  14674. arg2 = compile(s0,s++,depth,0,is_single);
  14675. if (code_end._width==arg1) pos = arg2; // makes 'end()' return void
  14676. is_sth = true;
  14677. while (*s && ((signed char)*s<=' ' || *s==';')) ++s;
  14678. s0 = s;
  14679. }
  14680. if (is_sth) {
  14681. arg1 = code_end._width;
  14682. arg2 = compile(s0,se,depth,p_ref,is_single);
  14683. if (code_end._width==arg1) pos = arg2; // makes 'end()' return void
  14684. _cimg_mp_return(pos);
  14685. }
  14686. // Declare / assign variable, vector value or image value.
  14687. for (s = ss1, ps = ss, ns = ss2; s<se1; ++s, ++ps, ++ns)
  14688. if (*s=='=' && *ns!='=' && *ps!='=' && *ps!='>' && *ps!='<' && *ps!='!' &&
  14689. *ps!='+' && *ps!='-' && *ps!='*' && *ps!='/' && *ps!='%' &&
  14690. *ps!='>' && *ps!='<' && *ps!='&' && *ps!='|' && *ps!='^' &&
  14691. level[s - expr._data]==clevel) {
  14692. variable_name.assign(ss,(unsigned int)(s + 1 - ss)).back() = 0;
  14693. cimg::strpare(variable_name,false,true);
  14694. const unsigned int l_variable_name = (unsigned int)std::strlen(variable_name);
  14695. char *const ve1 = ss + l_variable_name - 1;
  14696. _cimg_mp_op("Operator '='");
  14697. // Assign image value (direct).
  14698. if (l_variable_name>2 && (*ss=='i' || *ss=='j' || *ss=='I' || *ss=='J') && (*ss1=='(' || *ss1=='[') &&
  14699. (reserved_label[*ss]==~0U || *ss1=='(' || !_cimg_mp_is_vector(reserved_label[*ss]))) {
  14700. is_relative = *ss=='j' || *ss=='J';
  14701. if (*ss1=='[' && *ve1==']') { // i/j/I/J[_#ind,offset] = value
  14702. if (!is_single) is_parallelizable = false;
  14703. if (*ss2=='#') { // Index specified
  14704. s0 = ss3; while (s0<ve1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  14705. p1 = compile(ss3,s0++,depth1,0,is_single);
  14706. _cimg_mp_check_list(true);
  14707. } else { p1 = ~0U; s0 = ss2; }
  14708. arg1 = compile(s0,ve1,depth1,0,is_single); // Offset
  14709. _cimg_mp_check_type(arg1,0,1,0);
  14710. arg2 = compile(s + 1,se,depth1,0,is_single); // Value to assign
  14711. if (_cimg_mp_is_vector(arg2)) {
  14712. p2 = ~0U; // 'p2' must be the dimension of the vector-valued operand if any
  14713. if (p1==~0U) p2 = imgin._spectrum;
  14714. else if (_cimg_mp_is_constant(p1)) {
  14715. p3 = (unsigned int)cimg::mod((int)mem[p1],listin.width());
  14716. p2 = listin[p3]._spectrum;
  14717. }
  14718. _cimg_mp_check_vector0(p2);
  14719. } else p2 = 0;
  14720. _cimg_mp_check_type(arg2,2,*ss>='i'?1:3,p2);
  14721. if (p_ref) {
  14722. *p_ref = _cimg_mp_is_vector(arg2)?4:2;
  14723. p_ref[1] = p1;
  14724. p_ref[2] = (unsigned int)is_relative;
  14725. p_ref[3] = arg1;
  14726. if (_cimg_mp_is_vector(arg2))
  14727. set_variable_vector(arg2); // Prevent from being used in further optimization
  14728. else if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2;
  14729. if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -2;
  14730. if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -2;
  14731. }
  14732. if (p1!=~0U) {
  14733. if (!listout) _cimg_mp_return(arg2);
  14734. if (*ss>='i')
  14735. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
  14736. arg2,p1,arg1).move_to(code);
  14737. else if (_cimg_mp_is_scalar(arg2))
  14738. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s),
  14739. arg2,p1,arg1).move_to(code);
  14740. else
  14741. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
  14742. arg2,p1,arg1,_cimg_mp_size(arg2)).move_to(code);
  14743. } else {
  14744. if (!imgout) _cimg_mp_return(arg2);
  14745. if (*ss>='i')
  14746. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
  14747. arg2,arg1).move_to(code);
  14748. else if (_cimg_mp_is_scalar(arg2))
  14749. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s),
  14750. arg2,arg1).move_to(code);
  14751. else
  14752. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
  14753. arg2,arg1,_cimg_mp_size(arg2)).move_to(code);
  14754. }
  14755. _cimg_mp_return(arg2);
  14756. }
  14757. if (*ss1=='(' && *ve1==')') { // i/j/I/J(_#ind,_x,_y,_z,_c) = value
  14758. if (!is_single) is_parallelizable = false;
  14759. if (*ss2=='#') { // Index specified
  14760. s0 = ss3; while (s0<ve1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  14761. p1 = compile(ss3,s0++,depth1,0,is_single);
  14762. _cimg_mp_check_list(true);
  14763. } else { p1 = ~0U; s0 = ss2; }
  14764. arg1 = is_relative?0U:(unsigned int)_cimg_mp_slot_x;
  14765. arg2 = is_relative?0U:(unsigned int)_cimg_mp_slot_y;
  14766. arg3 = is_relative?0U:(unsigned int)_cimg_mp_slot_z;
  14767. arg4 = is_relative?0U:(unsigned int)_cimg_mp_slot_c;
  14768. arg5 = compile(s + 1,se,depth1,0,is_single); // Value to assign
  14769. if (s0<ve1) { // X or [ X,_Y,_Z,_C ]
  14770. s1 = s0; while (s1<ve1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  14771. arg1 = compile(s0,s1,depth1,0,is_single);
  14772. if (_cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
  14773. p2 = _cimg_mp_size(arg1); // Vector size
  14774. ++arg1;
  14775. if (p2>1) {
  14776. arg2 = arg1 + 1;
  14777. if (p2>2) {
  14778. arg3 = arg2 + 1;
  14779. if (p2>3) arg4 = arg3 + 1;
  14780. }
  14781. }
  14782. } else if (s1<ve1) { // Y
  14783. s2 = ++s1; while (s2<ve1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  14784. arg2 = compile(s1,s2,depth1,0,is_single);
  14785. if (s2<ve1) { // Z
  14786. s3 = ++s2; while (s3<ve1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
  14787. arg3 = compile(s2,s3,depth1,0,is_single);
  14788. if (s3<ve1) arg4 = compile(++s3,ve1,depth1,0,is_single); // C
  14789. }
  14790. }
  14791. }
  14792. if (_cimg_mp_is_vector(arg5)) {
  14793. p2 = ~0U; // 'p2' must be the dimension of the vector-valued operand if any
  14794. if (p1==~0U) p2 = imgin._spectrum;
  14795. else if (_cimg_mp_is_constant(p1)) {
  14796. p3 = (unsigned int)cimg::mod((int)mem[p1],listin.width());
  14797. p2 = listin[p3]._spectrum;
  14798. }
  14799. _cimg_mp_check_vector0(p2);
  14800. } else p2 = 0;
  14801. _cimg_mp_check_type(arg5,2,*ss>='i'?1:3,p2);
  14802. if (p_ref) {
  14803. *p_ref = _cimg_mp_is_vector(arg5)?5:3;
  14804. p_ref[1] = p1;
  14805. p_ref[2] = (unsigned int)is_relative;
  14806. p_ref[3] = arg1;
  14807. p_ref[4] = arg2;
  14808. p_ref[5] = arg3;
  14809. p_ref[6] = arg4;
  14810. if (_cimg_mp_is_vector(arg5))
  14811. set_variable_vector(arg5); // Prevent from being used in further optimization
  14812. else if (_cimg_mp_is_comp(arg5)) memtype[arg5] = -2;
  14813. if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -2;
  14814. if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -2;
  14815. if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2;
  14816. if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -2;
  14817. if (_cimg_mp_is_comp(arg4)) memtype[arg4] = -2;
  14818. }
  14819. if (p1!=~0U) {
  14820. if (!listout) _cimg_mp_return(arg5);
  14821. if (*ss>='i')
  14822. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
  14823. arg5,p1,arg1,arg2,arg3,arg4).move_to(code);
  14824. else if (_cimg_mp_is_scalar(arg5))
  14825. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s),
  14826. arg5,p1,arg1,arg2,arg3).move_to(code);
  14827. else
  14828. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
  14829. arg5,p1,arg1,arg2,arg3,_cimg_mp_size(arg5)).move_to(code);
  14830. } else {
  14831. if (!imgout) _cimg_mp_return(arg5);
  14832. if (*ss>='i')
  14833. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
  14834. arg5,arg1,arg2,arg3,arg4).move_to(code);
  14835. else if (_cimg_mp_is_scalar(arg5))
  14836. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s),
  14837. arg5,arg1,arg2,arg3).move_to(code);
  14838. else
  14839. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
  14840. arg5,arg1,arg2,arg3,_cimg_mp_size(arg5)).move_to(code);
  14841. }
  14842. _cimg_mp_return(arg5);
  14843. }
  14844. }
  14845. // Assign vector value (direct).
  14846. if (l_variable_name>3 && *ve1==']' && *ss!='[') {
  14847. s0 = ve1; while (s0>ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0;
  14848. is_sth = true; // is_valid_variable_name?
  14849. if (*ss>='0' && *ss<='9') is_sth = false;
  14850. else for (ns = ss; ns<s0; ++ns)
  14851. if (!is_varchar(*ns)) { is_sth = false; break; }
  14852. if (is_sth && s0>ss) {
  14853. variable_name[s0 - ss] = 0; // Remove brackets in variable name
  14854. arg1 = ~0U; // Vector slot
  14855. arg2 = compile(++s0,ve1,depth1,0,is_single); // Index
  14856. arg3 = compile(s + 1,se,depth1,0,is_single); // Value to assign
  14857. _cimg_mp_check_type(arg3,2,1,0);
  14858. if (variable_name[1]) { // Multi-char variable
  14859. cimglist_for(variable_def,i) if (!std::strcmp(variable_name,variable_def[i])) {
  14860. arg1 = variable_pos[i]; break;
  14861. }
  14862. } else arg1 = reserved_label[*variable_name]; // Single-char variable
  14863. if (arg1==~0U) compile(ss,s0 - 1,depth1,0,is_single); // Variable does not exist -> error
  14864. else { // Variable already exists
  14865. if (_cimg_mp_is_scalar(arg1)) compile(ss,s,depth1,0,is_single); // Variable is not a vector -> error
  14866. if (_cimg_mp_is_constant(arg2)) { // Constant index -> return corresponding variable slot directly
  14867. nb = (int)mem[arg2];
  14868. if (nb>=0 && nb<(int)_cimg_mp_size(arg1)) {
  14869. arg1+=nb + 1;
  14870. CImg<ulongT>::vector((ulongT)mp_copy,arg1,arg3).move_to(code);
  14871. _cimg_mp_return(arg1);
  14872. }
  14873. compile(ss,s,depth1,0,is_single); // Out-of-bounds reference -> error
  14874. }
  14875. // Case of non-constant index -> return assigned value + linked reference
  14876. if (p_ref) {
  14877. *p_ref = 1;
  14878. p_ref[1] = arg1;
  14879. p_ref[2] = arg2;
  14880. if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -2; // Prevent from being used in further optimization
  14881. if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2;
  14882. }
  14883. CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg3,arg1,(ulongT)_cimg_mp_size(arg1),
  14884. arg2,arg3).
  14885. move_to(code);
  14886. _cimg_mp_return(arg3);
  14887. }
  14888. }
  14889. }
  14890. // Assign user-defined macro.
  14891. if (l_variable_name>2 && *ve1==')' && *ss!='(') {
  14892. s0 = ve1; while (s0>ss && *s0!='(') --s0;
  14893. is_sth = std::strncmp(variable_name,"debug(",6) &&
  14894. std::strncmp(variable_name,"print(",6); // is_valid_function_name?
  14895. if (*ss>='0' && *ss<='9') is_sth = false;
  14896. else for (ns = ss; ns<s0; ++ns)
  14897. if (!is_varchar(*ns)) { is_sth = false; break; }
  14898. if (is_sth && s0>ss) { // Looks like a valid function declaration
  14899. s0 = variable_name._data + (s0 - ss);
  14900. *s0 = 0;
  14901. s1 = variable_name._data + l_variable_name - 1; // Pointer to closing parenthesis
  14902. CImg<charT>(variable_name._data,(unsigned int)(s0 - variable_name._data + 1)).move_to(macro_def,0);
  14903. ++s; while (*s && (signed char)*s<=' ') ++s;
  14904. CImg<charT>(s,(unsigned int)(se - s + 1)).move_to(macro_body,0);
  14905. p1 = 1; // Indice of current parsed argument
  14906. for (s = s0 + 1; s<=s1; ++p1, s = ns + 1) { // Parse function arguments
  14907. if (p1>24) {
  14908. *se = saved_char;
  14909. cimg::strellipsize(variable_name,64);
  14910. s0 = ss - 4>expr._data?ss - 4:expr._data;
  14911. cimg::strellipsize(s0,64);
  14912. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  14913. "CImg<%s>::%s: %s: Too much specified arguments (>24) in macro "
  14914. "definition '%s()', in expression '%s%s%s'.",
  14915. pixel_type(),_cimg_mp_calling_function,s_op,
  14916. variable_name._data,
  14917. s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
  14918. }
  14919. while (*s && (signed char)*s<=' ') ++s;
  14920. if (*s==')' && p1==1) break; // Function has no arguments
  14921. s2 = s; // Start of the argument name
  14922. is_sth = true; // is_valid_argument_name?
  14923. if (*s>='0' && *s<='9') is_sth = false;
  14924. else for (ns = s; ns<s1 && *ns!=',' && (signed char)*ns>' '; ++ns)
  14925. if (!is_varchar(*ns)) { is_sth = false; break; }
  14926. s3 = ns; // End of the argument name
  14927. while (*ns && (signed char)*ns<=' ') ++ns;
  14928. if (!is_sth || s2==s3 || (*ns!=',' && ns!=s1)) {
  14929. *se = saved_char;
  14930. cimg::strellipsize(variable_name,64);
  14931. s0 = ss - 4>expr._data?ss - 4:expr._data;
  14932. cimg::strellipsize(s0,64);
  14933. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  14934. "CImg<%s>::%s: %s: %s name specified for argument %u when defining "
  14935. "macro '%s()', in expression '%s%s%s'.",
  14936. pixel_type(),_cimg_mp_calling_function,s_op,
  14937. is_sth?"Empty":"Invalid",p1,
  14938. variable_name._data,
  14939. s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
  14940. }
  14941. if (ns==s1 || *ns==',') { // New argument found
  14942. *s3 = 0;
  14943. p2 = (unsigned int)(s3 - s2); // Argument length
  14944. for (ps = std::strstr(macro_body[0],s2); ps; ps = std::strstr(ps,s2)) { // Replace by arg number
  14945. if (!((ps>macro_body[0]._data && is_varchar(*(ps - 1))) ||
  14946. (ps + p2<macro_body[0].end() && is_varchar(*(ps + p2))))) {
  14947. if (ps>macro_body[0]._data && *(ps - 1)=='#') { // Remove pre-number sign
  14948. *(ps - 1) = (char)p1;
  14949. if (ps + p2<macro_body[0].end() && *(ps + p2)=='#') { // Has pre & post number signs
  14950. std::memmove(ps,ps + p2 + 1,macro_body[0].end() - ps - p2 - 1);
  14951. macro_body[0]._width-=p2 + 1;
  14952. } else { // Has pre number sign only
  14953. std::memmove(ps,ps + p2,macro_body[0].end() - ps - p2);
  14954. macro_body[0]._width-=p2;
  14955. }
  14956. } else if (ps + p2<macro_body[0].end() && *(ps + p2)=='#') { // Remove post-number sign
  14957. *(ps++) = (char)p1;
  14958. std::memmove(ps,ps + p2,macro_body[0].end() - ps - p2);
  14959. macro_body[0]._width-=p2;
  14960. } else { // Not near a number sign
  14961. if (p2<3) {
  14962. ps-=(ulongT)macro_body[0]._data;
  14963. macro_body[0].resize(macro_body[0]._width - p2 + 3,1,1,1,0);
  14964. ps+=(ulongT)macro_body[0]._data;
  14965. } else macro_body[0]._width-=p2 - 3;
  14966. std::memmove(ps + 3,ps + p2,macro_body[0].end() - ps - 3);
  14967. *(ps++) = '(';
  14968. *(ps++) = (char)p1;
  14969. *(ps++) = ')';
  14970. }
  14971. } else ++ps;
  14972. }
  14973. }
  14974. }
  14975. // Store number of arguments.
  14976. macro_def[0].resize(macro_def[0]._width + 1,1,1,1,0).back() = (char)(p1 - 1);
  14977. // Detect parts of function body inside a string.
  14978. is_inside_string(macro_body[0]).move_to(macro_body_is_string,0);
  14979. _cimg_mp_return_nan();
  14980. }
  14981. }
  14982. // Check if the variable name could be valid. If not, this is probably an lvalue assignment.
  14983. is_sth = true; // is_valid_variable_name?
  14984. const bool is_const = l_variable_name>6 && !std::strncmp(variable_name,"const ",6);
  14985. s0 = variable_name._data;
  14986. if (is_const) {
  14987. s0+=6; while ((signed char)*s0<=' ') ++s0;
  14988. variable_name.resize(variable_name.end() - s0,1,1,1,0,0,1);
  14989. }
  14990. if (*variable_name>='0' && *variable_name<='9') is_sth = false;
  14991. else for (ns = variable_name._data; *ns; ++ns)
  14992. if (!is_varchar(*ns)) { is_sth = false; break; }
  14993. // Assign variable (direct).
  14994. if (is_sth) {
  14995. arg3 = variable_name[1]?~0U:*variable_name; // One-char variable
  14996. if (variable_name[1] && !variable_name[2]) { // Two-chars variable
  14997. c1 = variable_name[0];
  14998. c2 = variable_name[1];
  14999. if (c1=='w' && c2=='h') arg3 = 0; // wh
  15000. else if (c1=='p' && c2=='i') arg3 = 3; // pi
  15001. else if (c1=='i') {
  15002. if (c2>='0' && c2<='9') arg3 = 19 + c2 - '0'; // i0...i9
  15003. else if (c2=='m') arg3 = 4; // im
  15004. else if (c2=='M') arg3 = 5; // iM
  15005. else if (c2=='a') arg3 = 6; // ia
  15006. else if (c2=='v') arg3 = 7; // iv
  15007. else if (c2=='s') arg3 = 8; // is
  15008. else if (c2=='p') arg3 = 9; // ip
  15009. else if (c2=='c') arg3 = 10; // ic
  15010. } else if (c2=='m') {
  15011. if (c1=='x') arg3 = 11; // xm
  15012. else if (c1=='y') arg3 = 12; // ym
  15013. else if (c1=='z') arg3 = 13; // zm
  15014. else if (c1=='c') arg3 = 14; // cm
  15015. } else if (c2=='M') {
  15016. if (c1=='x') arg3 = 15; // xM
  15017. else if (c1=='y') arg3 = 16; // yM
  15018. else if (c1=='z') arg3 = 17; // zM
  15019. else if (c1=='c') arg3 = 18; // cM
  15020. }
  15021. } else if (variable_name[1] && variable_name[2] && !variable_name[3]) { // Three-chars variable
  15022. c1 = variable_name[0];
  15023. c2 = variable_name[1];
  15024. c3 = variable_name[2];
  15025. if (c1=='w' && c2=='h' && c3=='d') arg3 = 1; // whd
  15026. } else if (variable_name[1] && variable_name[2] && variable_name[3] &&
  15027. !variable_name[4]) { // Four-chars variable
  15028. c1 = variable_name[0];
  15029. c2 = variable_name[1];
  15030. c3 = variable_name[2];
  15031. c4 = variable_name[3];
  15032. if (c1=='w' && c2=='h' && c3=='d' && c4=='s') arg3 = 2; // whds
  15033. } else if (!std::strcmp(variable_name,"interpolation")) arg3 = 29; // interpolation
  15034. else if (!std::strcmp(variable_name,"boundary")) arg3 = 30; // boundary
  15035. arg1 = ~0U;
  15036. arg2 = compile(s + 1,se,depth1,0,is_single);
  15037. if (is_const) _cimg_mp_check_constant(arg2,2,0);
  15038. if (arg3!=~0U) // One-char variable, or variable in reserved_labels
  15039. arg1 = reserved_label[arg3];
  15040. else // Multi-char variable name : check for existing variable with same name
  15041. cimglist_for(variable_def,i)
  15042. if (!std::strcmp(variable_name,variable_def[i])) { arg1 = variable_pos[i]; break; }
  15043. if (arg1==~0U) { // Create new variable
  15044. if (_cimg_mp_is_vector(arg2)) { // Vector variable
  15045. arg1 = is_comp_vector(arg2)?arg2:vector_copy(arg2);
  15046. set_variable_vector(arg1);
  15047. } else { // Scalar variable
  15048. if (is_const) arg1 = arg2;
  15049. else {
  15050. arg1 = _cimg_mp_is_comp(arg2)?arg2:scalar1(mp_copy,arg2);
  15051. memtype[arg1] = -1;
  15052. }
  15053. }
  15054. if (arg3!=~0U) reserved_label[arg3] = arg1;
  15055. else {
  15056. if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0);
  15057. variable_pos[variable_def._width] = arg1;
  15058. variable_name.move_to(variable_def);
  15059. }
  15060. } else { // Variable already exists -> assign a new value
  15061. if (is_const || _cimg_mp_is_constant(arg1)) {
  15062. *se = saved_char;
  15063. cimg::strellipsize(variable_name,64);
  15064. s0 = ss - 4>expr._data?ss - 4:expr._data;
  15065. cimg::strellipsize(s0,64);
  15066. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  15067. "CImg<%s>::%s: %s: Invalid assignment of %sconst variable '%s'%s, "
  15068. "in expression '%s%s%s'.",
  15069. pixel_type(),_cimg_mp_calling_function,s_op,
  15070. _cimg_mp_is_constant(arg1)?"already-defined ":"non-",
  15071. variable_name._data,
  15072. !_cimg_mp_is_constant(arg1) && is_const?" as a new const variable":"",
  15073. s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
  15074. }
  15075. _cimg_mp_check_type(arg2,2,_cimg_mp_is_vector(arg1)?3:1,_cimg_mp_size(arg1));
  15076. if (_cimg_mp_is_vector(arg1)) { // Vector
  15077. if (_cimg_mp_is_vector(arg2)) // From vector
  15078. CImg<ulongT>::vector((ulongT)mp_vector_copy,arg1,arg2,(ulongT)_cimg_mp_size(arg1)).
  15079. move_to(code);
  15080. else // From scalar
  15081. CImg<ulongT>::vector((ulongT)mp_vector_init,arg1,1,(ulongT)_cimg_mp_size(arg1),arg2).
  15082. move_to(code);
  15083. } else // Scalar
  15084. CImg<ulongT>::vector((ulongT)mp_copy,arg1,arg2).move_to(code);
  15085. }
  15086. _cimg_mp_return(arg1);
  15087. }
  15088. // Assign lvalue (variable name was not valid for a direct assignment).
  15089. arg1 = ~0U;
  15090. is_sth = (bool)std::strchr(variable_name,'?'); // Contains_ternary_operator?
  15091. if (is_sth) break; // Do nothing and make ternary operator prioritary over assignment
  15092. if (l_variable_name>2 && (std::strchr(variable_name,'(') || std::strchr(variable_name,'['))) {
  15093. ref.assign(7);
  15094. arg1 = compile(ss,s,depth1,ref,is_single); // Lvalue slot
  15095. arg2 = compile(s + 1,se,depth1,0,is_single); // Value to assign
  15096. if (*ref==1) { // Vector value (scalar): V[k] = scalar
  15097. _cimg_mp_check_type(arg2,2,1,0);
  15098. arg3 = ref[1]; // Vector slot
  15099. arg4 = ref[2]; // Index
  15100. if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
  15101. CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg2,arg3,(ulongT)_cimg_mp_size(arg3),arg4,arg2).
  15102. move_to(code);
  15103. _cimg_mp_return(arg2);
  15104. }
  15105. if (*ref==2) { // Image value (scalar): i/j[_#ind,off] = scalar
  15106. if (!is_single) is_parallelizable = false;
  15107. _cimg_mp_check_type(arg2,2,1,0);
  15108. p1 = ref[1]; // Index
  15109. is_relative = (bool)ref[2];
  15110. arg3 = ref[3]; // Offset
  15111. if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
  15112. if (p1!=~0U) {
  15113. if (!listout) _cimg_mp_return(arg2);
  15114. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
  15115. arg2,p1,arg3).move_to(code);
  15116. } else {
  15117. if (!imgout) _cimg_mp_return(arg2);
  15118. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
  15119. arg2,arg3).move_to(code);
  15120. }
  15121. _cimg_mp_return(arg2);
  15122. }
  15123. if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c) = scalar
  15124. if (!is_single) is_parallelizable = false;
  15125. _cimg_mp_check_type(arg2,2,1,0);
  15126. p1 = ref[1]; // Index
  15127. is_relative = (bool)ref[2];
  15128. arg3 = ref[3]; // X
  15129. arg4 = ref[4]; // Y
  15130. arg5 = ref[5]; // Z
  15131. arg6 = ref[6]; // C
  15132. if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
  15133. if (p1!=~0U) {
  15134. if (!listout) _cimg_mp_return(arg2);
  15135. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
  15136. arg2,p1,arg3,arg4,arg5,arg6).move_to(code);
  15137. } else {
  15138. if (!imgout) _cimg_mp_return(arg2);
  15139. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
  15140. arg2,arg3,arg4,arg5,arg6).move_to(code);
  15141. }
  15142. _cimg_mp_return(arg2);
  15143. }
  15144. if (*ref==4) { // Image value (vector): I/J[_#ind,off] = value
  15145. if (!is_single) is_parallelizable = false;
  15146. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  15147. p1 = ref[1]; // Index
  15148. is_relative = (bool)ref[2];
  15149. arg3 = ref[3]; // Offset
  15150. if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
  15151. if (p1!=~0U) {
  15152. if (!listout) _cimg_mp_return(arg2);
  15153. if (_cimg_mp_is_scalar(arg2))
  15154. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s),
  15155. arg2,p1,arg3).move_to(code);
  15156. else
  15157. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
  15158. arg2,p1,arg3,_cimg_mp_size(arg2)).move_to(code);
  15159. } else {
  15160. if (!imgout) _cimg_mp_return(arg2);
  15161. if (_cimg_mp_is_scalar(arg2))
  15162. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s),
  15163. arg2,arg3).move_to(code);
  15164. else
  15165. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
  15166. arg2,arg3,_cimg_mp_size(arg2)).move_to(code);
  15167. }
  15168. _cimg_mp_return(arg2);
  15169. }
  15170. if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) = value
  15171. if (!is_single) is_parallelizable = false;
  15172. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  15173. p1 = ref[1]; // Index
  15174. is_relative = (bool)ref[2];
  15175. arg3 = ref[3]; // X
  15176. arg4 = ref[4]; // Y
  15177. arg5 = ref[5]; // Z
  15178. if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
  15179. if (p1!=~0U) {
  15180. if (!listout) _cimg_mp_return(arg2);
  15181. if (_cimg_mp_is_scalar(arg2))
  15182. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s),
  15183. arg2,p1,arg3,arg4,arg5).move_to(code);
  15184. else
  15185. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
  15186. arg2,p1,arg3,arg4,arg5,_cimg_mp_size(arg2)).move_to(code);
  15187. } else {
  15188. if (!imgout) _cimg_mp_return(arg2);
  15189. if (_cimg_mp_is_scalar(arg2))
  15190. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s),
  15191. arg2,arg3,arg4,arg5).move_to(code);
  15192. else
  15193. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
  15194. arg2,arg3,arg4,arg5,_cimg_mp_size(arg2)).move_to(code);
  15195. }
  15196. _cimg_mp_return(arg2);
  15197. }
  15198. if (_cimg_mp_is_vector(arg1)) { // Vector variable: V = value
  15199. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  15200. if (_cimg_mp_is_vector(arg2)) // From vector
  15201. CImg<ulongT>::vector((ulongT)mp_vector_copy,arg1,arg2,(ulongT)_cimg_mp_size(arg1)).
  15202. move_to(code);
  15203. else // From scalar
  15204. CImg<ulongT>::vector((ulongT)mp_vector_init,arg1,1,(ulongT)_cimg_mp_size(arg1),arg2).
  15205. move_to(code);
  15206. _cimg_mp_return(arg1);
  15207. }
  15208. if (_cimg_mp_is_variable(arg1)) { // Scalar variable: s = scalar
  15209. _cimg_mp_check_type(arg2,2,1,0);
  15210. CImg<ulongT>::vector((ulongT)mp_copy,arg1,arg2).move_to(code);
  15211. _cimg_mp_return(arg1);
  15212. }
  15213. }
  15214. // No assignment expressions match -> error
  15215. *se = saved_char;
  15216. cimg::strellipsize(variable_name,64);
  15217. s0 = ss - 4>expr._data?ss - 4:expr._data;
  15218. cimg::strellipsize(s0,64);
  15219. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  15220. "CImg<%s>::%s: %s: Invalid %slvalue '%s', "
  15221. "in expression '%s%s%s'.",
  15222. pixel_type(),_cimg_mp_calling_function,s_op,
  15223. arg1!=~0U && _cimg_mp_is_constant(arg1)?"const ":"",
  15224. variable_name._data,
  15225. s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
  15226. }
  15227. // Apply unary/binary/ternary operators. The operator precedences should be the same as in C++.
  15228. for (s = se2, ps = se3, ns = ps - 1; s>ss1; --s, --ps, --ns) // Here, ns = ps - 1
  15229. if (*s=='=' && (*ps=='*' || *ps=='/' || *ps=='^') && *ns==*ps &&
  15230. level[s - expr._data]==clevel) { // Self-operators for complex numbers only (**=,//=,^^=)
  15231. _cimg_mp_op(*ps=='*'?"Operator '**='":*ps=='/'?"Operator '//='":"Operator '^^='");
  15232. ref.assign(7);
  15233. arg1 = compile(ss,ns,depth1,ref,is_single); // Vector slot
  15234. arg2 = compile(s + 1,se,depth1,0,is_single); // Right operand
  15235. _cimg_mp_check_type(arg1,1,2,2);
  15236. _cimg_mp_check_type(arg2,2,3,2);
  15237. if (_cimg_mp_is_vector(arg2)) { // Complex **= complex
  15238. if (*ps=='*')
  15239. CImg<ulongT>::vector((ulongT)mp_complex_mul,arg1,arg1,arg2).move_to(code);
  15240. else if (*ps=='/')
  15241. CImg<ulongT>::vector((ulongT)mp_complex_div_vv,arg1,arg1,arg2).move_to(code);
  15242. else
  15243. CImg<ulongT>::vector((ulongT)mp_complex_pow_vv,arg1,arg1,arg2).move_to(code);
  15244. } else { // Complex **= scalar
  15245. if (*ps=='*') {
  15246. if (arg2==1) _cimg_mp_return(arg1);
  15247. self_vector_s(arg1,mp_self_mul,arg2);
  15248. } else if (*ps=='/') {
  15249. if (arg2==1) _cimg_mp_return(arg1);
  15250. self_vector_s(arg1,mp_self_div,arg2);
  15251. } else {
  15252. if (arg2==1) _cimg_mp_return(arg1);
  15253. CImg<ulongT>::vector((ulongT)mp_complex_pow_vs,arg1,arg1,arg2).move_to(code);
  15254. }
  15255. }
  15256. // Write computed value back in image if necessary.
  15257. if (*ref==4) { // Image value (vector): I/J[_#ind,off] **= value
  15258. if (!is_single) is_parallelizable = false;
  15259. p1 = ref[1]; // Index
  15260. is_relative = (bool)ref[2];
  15261. arg3 = ref[3]; // Offset
  15262. if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
  15263. if (p1!=~0U) {
  15264. if (!listout) _cimg_mp_return(arg1);
  15265. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
  15266. arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code);
  15267. } else {
  15268. if (!imgout) _cimg_mp_return(arg1);
  15269. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
  15270. arg1,arg3,_cimg_mp_size(arg1)).move_to(code);
  15271. }
  15272. } else if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) **= value
  15273. if (!is_single) is_parallelizable = false;
  15274. p1 = ref[1]; // Index
  15275. is_relative = (bool)ref[2];
  15276. arg3 = ref[3]; // X
  15277. arg4 = ref[4]; // Y
  15278. arg5 = ref[5]; // Z
  15279. if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
  15280. if (p1!=~0U) {
  15281. if (!listout) _cimg_mp_return(arg1);
  15282. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
  15283. arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
  15284. } else {
  15285. if (!imgout) _cimg_mp_return(arg1);
  15286. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
  15287. arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
  15288. }
  15289. }
  15290. _cimg_mp_return(arg1);
  15291. }
  15292. for (s = se2, ps = se3, ns = ps - 1; s>ss1; --s, --ps, --ns) // Here, ns = ps - 1
  15293. if (*s=='=' && (*ps=='+' || *ps=='-' || *ps=='*' || *ps=='/' || *ps=='%' ||
  15294. *ps=='&' || *ps=='^' || *ps=='|' ||
  15295. (*ps=='>' && *ns=='>') || (*ps=='<' && *ns=='<')) &&
  15296. level[s - expr._data]==clevel) { // Self-operators (+=,-=,*=,/=,%=,>>=,<<=,&=,^=,|=)
  15297. switch (*ps) {
  15298. case '+' : op = mp_self_add; _cimg_mp_op("Operator '+='"); break;
  15299. case '-' : op = mp_self_sub; _cimg_mp_op("Operator '-='"); break;
  15300. case '*' : op = mp_self_mul; _cimg_mp_op("Operator '*='"); break;
  15301. case '/' : op = mp_self_div; _cimg_mp_op("Operator '/='"); break;
  15302. case '%' : op = mp_self_modulo; _cimg_mp_op("Operator '%='"); break;
  15303. case '<' : op = mp_self_bitwise_left_shift; _cimg_mp_op("Operator '<<='"); break;
  15304. case '>' : op = mp_self_bitwise_right_shift; _cimg_mp_op("Operator '>>='"); break;
  15305. case '&' : op = mp_self_bitwise_and; _cimg_mp_op("Operator '&='"); break;
  15306. case '|' : op = mp_self_bitwise_or; _cimg_mp_op("Operator '|='"); break;
  15307. default : op = mp_self_pow; _cimg_mp_op("Operator '^='"); break;
  15308. }
  15309. s1 = *ps=='>' || *ps=='<'?ns:ps;
  15310. ref.assign(7);
  15311. arg1 = compile(ss,s1,depth1,ref,is_single); // Variable slot
  15312. arg2 = compile(s + 1,se,depth1,0,is_single); // Value to apply
  15313. // Check for particular case to be simplified.
  15314. if ((op==mp_self_add || op==mp_self_sub) && !arg2) _cimg_mp_return(arg1);
  15315. if ((op==mp_self_mul || op==mp_self_div) && arg2==1) _cimg_mp_return(arg1);
  15316. // Apply operator on a copy to prevent modifying a constant or a variable.
  15317. if (*ref && (_cimg_mp_is_constant(arg1) || _cimg_mp_is_vector(arg1) || _cimg_mp_is_variable(arg1))) {
  15318. if (_cimg_mp_is_vector(arg1)) arg1 = vector_copy(arg1);
  15319. else arg1 = scalar1(mp_copy,arg1);
  15320. }
  15321. if (*ref==1) { // Vector value (scalar): V[k] += scalar
  15322. _cimg_mp_check_type(arg2,2,1,0);
  15323. arg3 = ref[1]; // Vector slot
  15324. arg4 = ref[2]; // Index
  15325. if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
  15326. CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code);
  15327. CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_size(arg3),arg4,arg1).
  15328. move_to(code);
  15329. _cimg_mp_return(arg1);
  15330. }
  15331. if (*ref==2) { // Image value (scalar): i/j[_#ind,off] += scalar
  15332. if (!is_single) is_parallelizable = false;
  15333. _cimg_mp_check_type(arg2,2,1,0);
  15334. p1 = ref[1]; // Index
  15335. is_relative = (bool)ref[2];
  15336. arg3 = ref[3]; // Offset
  15337. if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
  15338. CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code);
  15339. if (p1!=~0U) {
  15340. if (!listout) _cimg_mp_return(arg1);
  15341. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
  15342. arg1,p1,arg3).move_to(code);
  15343. } else {
  15344. if (!imgout) _cimg_mp_return(arg1);
  15345. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
  15346. arg1,arg3).move_to(code);
  15347. }
  15348. _cimg_mp_return(arg1);
  15349. }
  15350. if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c) += scalar
  15351. if (!is_single) is_parallelizable = false;
  15352. _cimg_mp_check_type(arg2,2,1,0);
  15353. p1 = ref[1]; // Index
  15354. is_relative = (bool)ref[2];
  15355. arg3 = ref[3]; // X
  15356. arg4 = ref[4]; // Y
  15357. arg5 = ref[5]; // Z
  15358. arg6 = ref[6]; // C
  15359. if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
  15360. CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code);
  15361. if (p1!=~0U) {
  15362. if (!listout) _cimg_mp_return(arg1);
  15363. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
  15364. arg1,p1,arg3,arg4,arg5,arg6).move_to(code);
  15365. } else {
  15366. if (!imgout) _cimg_mp_return(arg1);
  15367. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
  15368. arg1,arg3,arg4,arg5,arg6).move_to(code);
  15369. }
  15370. _cimg_mp_return(arg1);
  15371. }
  15372. if (*ref==4) { // Image value (vector): I/J[_#ind,off] += value
  15373. if (!is_single) is_parallelizable = false;
  15374. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  15375. p1 = ref[1]; // Index
  15376. is_relative = (bool)ref[2];
  15377. arg3 = ref[3]; // Offset
  15378. if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
  15379. if (_cimg_mp_is_scalar(arg2)) self_vector_s(arg1,op,arg2); else self_vector_v(arg1,op,arg2);
  15380. if (p1!=~0U) {
  15381. if (!listout) _cimg_mp_return(arg1);
  15382. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
  15383. arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code);
  15384. } else {
  15385. if (!imgout) _cimg_mp_return(arg1);
  15386. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
  15387. arg1,arg3,_cimg_mp_size(arg1)).move_to(code);
  15388. }
  15389. _cimg_mp_return(arg1);
  15390. }
  15391. if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) += value
  15392. if (!is_single) is_parallelizable = false;
  15393. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  15394. p1 = ref[1]; // Index
  15395. is_relative = (bool)ref[2];
  15396. arg3 = ref[3]; // X
  15397. arg4 = ref[4]; // Y
  15398. arg5 = ref[5]; // Z
  15399. if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
  15400. if (_cimg_mp_is_scalar(arg2)) self_vector_s(arg1,op,arg2); else self_vector_v(arg1,op,arg2);
  15401. if (p1!=~0U) {
  15402. if (!listout) _cimg_mp_return(arg1);
  15403. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
  15404. arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
  15405. } else {
  15406. if (!imgout) _cimg_mp_return(arg1);
  15407. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
  15408. arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
  15409. }
  15410. _cimg_mp_return(arg1);
  15411. }
  15412. if (_cimg_mp_is_vector(arg1)) { // Vector variable: V += value
  15413. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  15414. if (_cimg_mp_is_vector(arg2)) self_vector_v(arg1,op,arg2); // Vector += vector
  15415. else self_vector_s(arg1,op,arg2); // Vector += scalar
  15416. _cimg_mp_return(arg1);
  15417. }
  15418. if (_cimg_mp_is_variable(arg1)) { // Scalar variable: s += scalar
  15419. _cimg_mp_check_type(arg2,2,1,0);
  15420. CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code);
  15421. _cimg_mp_return(arg1);
  15422. }
  15423. variable_name.assign(ss,(unsigned int)(s - ss)).back() = 0;
  15424. cimg::strpare(variable_name,false,true);
  15425. *se = saved_char;
  15426. s0 = ss - 4>expr._data?ss - 4:expr._data;
  15427. cimg::strellipsize(s0,64);
  15428. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  15429. "CImg<%s>::%s: %s: Invalid %slvalue '%s', "
  15430. "in expression '%s%s%s'.",
  15431. pixel_type(),_cimg_mp_calling_function,s_op,
  15432. _cimg_mp_is_constant(arg1)?"const ":"",
  15433. variable_name._data,
  15434. s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
  15435. }
  15436. for (s = ss1; s<se1; ++s)
  15437. if (*s=='?' && level[s - expr._data]==clevel) { // Ternary operator 'cond?expr1:expr2'
  15438. _cimg_mp_op("Operator '?:'");
  15439. s1 = s + 1; while (s1<se1 && (*s1!=':' || level[s1 - expr._data]!=clevel)) ++s1;
  15440. arg1 = compile(ss,s,depth1,0,is_single);
  15441. _cimg_mp_check_type(arg1,1,1,0);
  15442. if (_cimg_mp_is_constant(arg1)) {
  15443. if ((bool)mem[arg1]) return compile(s + 1,*s1!=':'?se:s1,depth1,0,is_single);
  15444. else return *s1!=':'?0:compile(++s1,se,depth1,0,is_single);
  15445. }
  15446. p2 = code._width;
  15447. arg2 = compile(s + 1,*s1!=':'?se:s1,depth1,0,is_single);
  15448. p3 = code._width;
  15449. arg3 = *s1==':'?compile(++s1,se,depth1,0,is_single):
  15450. _cimg_mp_is_vector(arg2)?vector(_cimg_mp_size(arg2),0):0;
  15451. _cimg_mp_check_type(arg3,3,_cimg_mp_is_vector(arg2)?2:1,_cimg_mp_size(arg2));
  15452. arg4 = _cimg_mp_size(arg2);
  15453. if (arg4) pos = vector(arg4); else pos = scalar();
  15454. CImg<ulongT>::vector((ulongT)mp_if,pos,arg1,arg2,arg3,
  15455. p3 - p2,code._width - p3,arg4).move_to(code,p2);
  15456. _cimg_mp_return(pos);
  15457. }
  15458. for (s = se3, ns = se2; s>ss; --s, --ns)
  15459. if (*s=='|' && *ns=='|' && level[s - expr._data]==clevel) { // Logical or ('||')
  15460. _cimg_mp_op("Operator '||'");
  15461. arg1 = compile(ss,s,depth1,0,is_single);
  15462. _cimg_mp_check_type(arg1,1,1,0);
  15463. if (arg1>0 && arg1<=16) _cimg_mp_return(1);
  15464. p2 = code._width;
  15465. arg2 = compile(s + 2,se,depth1,0,is_single);
  15466. _cimg_mp_check_type(arg2,2,1,0);
  15467. if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
  15468. _cimg_mp_constant(mem[arg1] || mem[arg2]);
  15469. if (!arg1) _cimg_mp_return(arg2);
  15470. pos = scalar();
  15471. CImg<ulongT>::vector((ulongT)mp_logical_or,pos,arg1,arg2,code._width - p2).
  15472. move_to(code,p2);
  15473. _cimg_mp_return(pos);
  15474. }
  15475. for (s = se3, ns = se2; s>ss; --s, --ns)
  15476. if (*s=='&' && *ns=='&' && level[s - expr._data]==clevel) { // Logical and ('&&')
  15477. _cimg_mp_op("Operator '&&'");
  15478. arg1 = compile(ss,s,depth1,0,is_single);
  15479. _cimg_mp_check_type(arg1,1,1,0);
  15480. if (!arg1) _cimg_mp_return(0);
  15481. p2 = code._width;
  15482. arg2 = compile(s + 2,se,depth1,0,is_single);
  15483. _cimg_mp_check_type(arg2,2,1,0);
  15484. if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
  15485. _cimg_mp_constant(mem[arg1] && mem[arg2]);
  15486. if (arg1>0 && arg1<=16) _cimg_mp_return(arg2);
  15487. pos = scalar();
  15488. CImg<ulongT>::vector((ulongT)mp_logical_and,pos,arg1,arg2,code._width - p2).
  15489. move_to(code,p2);
  15490. _cimg_mp_return(pos);
  15491. }
  15492. for (s = se2; s>ss; --s)
  15493. if (*s=='|' && level[s - expr._data]==clevel) { // Bitwise or ('|')
  15494. _cimg_mp_op("Operator '|'");
  15495. arg1 = compile(ss,s,depth1,0,is_single);
  15496. arg2 = compile(s + 1,se,depth1,0,is_single);
  15497. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  15498. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_or,arg1,arg2);
  15499. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) {
  15500. if (!arg2) _cimg_mp_return(arg1);
  15501. _cimg_mp_vector2_vs(mp_bitwise_or,arg1,arg2);
  15502. }
  15503. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) {
  15504. if (!arg1) _cimg_mp_return(arg2);
  15505. _cimg_mp_vector2_sv(mp_bitwise_or,arg1,arg2);
  15506. }
  15507. if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
  15508. _cimg_mp_constant((longT)mem[arg1] | (longT)mem[arg2]);
  15509. if (!arg2) _cimg_mp_return(arg1);
  15510. if (!arg1) _cimg_mp_return(arg2);
  15511. _cimg_mp_scalar2(mp_bitwise_or,arg1,arg2);
  15512. }
  15513. for (s = se2; s>ss; --s)
  15514. if (*s=='&' && level[s - expr._data]==clevel) { // Bitwise and ('&')
  15515. _cimg_mp_op("Operator '&'");
  15516. arg1 = compile(ss,s,depth1,0,is_single);
  15517. arg2 = compile(s + 1,se,depth1,0,is_single);
  15518. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  15519. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_and,arg1,arg2);
  15520. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_bitwise_and,arg1,arg2);
  15521. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_bitwise_and,arg1,arg2);
  15522. if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
  15523. _cimg_mp_constant((longT)mem[arg1] & (longT)mem[arg2]);
  15524. if (!arg1 || !arg2) _cimg_mp_return(0);
  15525. _cimg_mp_scalar2(mp_bitwise_and,arg1,arg2);
  15526. }
  15527. for (s = se3, ns = se2; s>ss; --s, --ns)
  15528. if (*s=='!' && *ns=='=' && level[s - expr._data]==clevel) { // Not equal to ('!=')
  15529. _cimg_mp_op("Operator '!='");
  15530. arg1 = compile(ss,s,depth1,0,is_single);
  15531. arg2 = compile(s + 2,se,depth1,0,is_single);
  15532. if (arg1==arg2) _cimg_mp_return(0);
  15533. p1 = _cimg_mp_size(arg1);
  15534. p2 = _cimg_mp_size(arg2);
  15535. if (p1 || p2) {
  15536. if (p1 && p2 && p1!=p2) _cimg_mp_return(1);
  15537. _cimg_mp_scalar6(mp_vector_neq,arg1,p1,arg2,p2,11,1);
  15538. }
  15539. if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]!=mem[arg2]);
  15540. _cimg_mp_scalar2(mp_neq,arg1,arg2);
  15541. }
  15542. for (s = se3, ns = se2; s>ss; --s, --ns)
  15543. if (*s=='=' && *ns=='=' && level[s - expr._data]==clevel) { // Equal to ('==')
  15544. _cimg_mp_op("Operator '=='");
  15545. arg1 = compile(ss,s,depth1,0,is_single);
  15546. arg2 = compile(s + 2,se,depth1,0,is_single);
  15547. if (arg1==arg2) _cimg_mp_return(1);
  15548. p1 = _cimg_mp_size(arg1);
  15549. p2 = _cimg_mp_size(arg2);
  15550. if (p1 || p2) {
  15551. if (p1 && p2 && p1!=p2) _cimg_mp_return(0);
  15552. _cimg_mp_scalar6(mp_vector_eq,arg1,p1,arg2,p2,11,1);
  15553. }
  15554. if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]==mem[arg2]);
  15555. _cimg_mp_scalar2(mp_eq,arg1,arg2);
  15556. }
  15557. for (s = se3, ns = se2; s>ss; --s, --ns)
  15558. if (*s=='<' && *ns=='=' && level[s - expr._data]==clevel) { // Less or equal than ('<=')
  15559. _cimg_mp_op("Operator '<='");
  15560. arg1 = compile(ss,s,depth1,0,is_single);
  15561. arg2 = compile(s + 2,se,depth1,0,is_single);
  15562. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  15563. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_lte,arg1,arg2);
  15564. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_lte,arg1,arg2);
  15565. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_lte,arg1,arg2);
  15566. if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]<=mem[arg2]);
  15567. if (arg1==arg2) _cimg_mp_return(1);
  15568. _cimg_mp_scalar2(mp_lte,arg1,arg2);
  15569. }
  15570. for (s = se3, ns = se2; s>ss; --s, --ns)
  15571. if (*s=='>' && *ns=='=' && level[s - expr._data]==clevel) { // Greater or equal than ('>=')
  15572. _cimg_mp_op("Operator '>='");
  15573. arg1 = compile(ss,s,depth1,0,is_single);
  15574. arg2 = compile(s + 2,se,depth1,0,is_single);
  15575. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  15576. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_gte,arg1,arg2);
  15577. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_gte,arg1,arg2);
  15578. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_gte,arg1,arg2);
  15579. if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]>=mem[arg2]);
  15580. if (arg1==arg2) _cimg_mp_return(1);
  15581. _cimg_mp_scalar2(mp_gte,arg1,arg2);
  15582. }
  15583. for (s = se2, ns = se1, ps = se3; s>ss; --s, --ns, --ps)
  15584. if (*s=='<' && *ns!='<' && *ps!='<' && level[s - expr._data]==clevel) { // Less than ('<')
  15585. _cimg_mp_op("Operator '<'");
  15586. arg1 = compile(ss,s,depth1,0,is_single);
  15587. arg2 = compile(s + 1,se,depth1,0,is_single);
  15588. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  15589. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_lt,arg1,arg2);
  15590. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_lt,arg1,arg2);
  15591. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_lt,arg1,arg2);
  15592. if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]<mem[arg2]);
  15593. if (arg1==arg2) _cimg_mp_return(0);
  15594. _cimg_mp_scalar2(mp_lt,arg1,arg2);
  15595. }
  15596. for (s = se2, ns = se1, ps = se3; s>ss; --s, --ns, --ps)
  15597. if (*s=='>' && *ns!='>' && *ps!='>' && level[s - expr._data]==clevel) { // Greather than ('>')
  15598. _cimg_mp_op("Operator '>'");
  15599. arg1 = compile(ss,s,depth1,0,is_single);
  15600. arg2 = compile(s + 1,se,depth1,0,is_single);
  15601. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  15602. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_gt,arg1,arg2);
  15603. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_gt,arg1,arg2);
  15604. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_gt,arg1,arg2);
  15605. if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]>mem[arg2]);
  15606. if (arg1==arg2) _cimg_mp_return(0);
  15607. _cimg_mp_scalar2(mp_gt,arg1,arg2);
  15608. }
  15609. for (s = se3, ns = se2; s>ss; --s, --ns)
  15610. if (*s=='<' && *ns=='<' && level[s - expr._data]==clevel) { // Left bit shift ('<<')
  15611. _cimg_mp_op("Operator '<<'");
  15612. arg1 = compile(ss,s,depth1,0,is_single);
  15613. arg2 = compile(s + 2,se,depth1,0,is_single);
  15614. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  15615. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2))
  15616. _cimg_mp_vector2_vv(mp_bitwise_left_shift,arg1,arg2);
  15617. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) {
  15618. if (!arg2) _cimg_mp_return(arg1);
  15619. _cimg_mp_vector2_vs(mp_bitwise_left_shift,arg1,arg2);
  15620. }
  15621. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2))
  15622. _cimg_mp_vector2_sv(mp_bitwise_left_shift,arg1,arg2);
  15623. if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
  15624. _cimg_mp_constant((longT)mem[arg1]<<(unsigned int)mem[arg2]);
  15625. if (!arg1) _cimg_mp_return(0);
  15626. if (!arg2) _cimg_mp_return(arg1);
  15627. _cimg_mp_scalar2(mp_bitwise_left_shift,arg1,arg2);
  15628. }
  15629. for (s = se3, ns = se2; s>ss; --s, --ns)
  15630. if (*s=='>' && *ns=='>' && level[s - expr._data]==clevel) { // Right bit shift ('>>')
  15631. _cimg_mp_op("Operator '>>'");
  15632. arg1 = compile(ss,s,depth1,0,is_single);
  15633. arg2 = compile(s + 2,se,depth1,0,is_single);
  15634. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  15635. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2))
  15636. _cimg_mp_vector2_vv(mp_bitwise_right_shift,arg1,arg2);
  15637. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) {
  15638. if (!arg2) _cimg_mp_return(arg1);
  15639. _cimg_mp_vector2_vs(mp_bitwise_right_shift,arg1,arg2);
  15640. }
  15641. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2))
  15642. _cimg_mp_vector2_sv(mp_bitwise_right_shift,arg1,arg2);
  15643. if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
  15644. _cimg_mp_constant((longT)mem[arg1]>>(unsigned int)mem[arg2]);
  15645. if (!arg1) _cimg_mp_return(0);
  15646. if (!arg2) _cimg_mp_return(arg1);
  15647. _cimg_mp_scalar2(mp_bitwise_right_shift,arg1,arg2);
  15648. }
  15649. for (ns = se1, s = se2, ps = pexpr._data + (se3 - expr._data); s>ss; --ns, --s, --ps)
  15650. if (*s=='+' && (*ns!='+' || ns!=se1) && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' &&
  15651. *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && *ps!='#' &&
  15652. (*ps!='e' || !(ps - pexpr._data>ss - expr._data && (*(ps - 1)=='.' || (*(ps - 1)>='0' &&
  15653. *(ps - 1)<='9')))) &&
  15654. level[s - expr._data]==clevel) { // Addition ('+')
  15655. _cimg_mp_op("Operator '+'");
  15656. arg1 = compile(ss,s,depth1,0,is_single);
  15657. arg2 = compile(s + 1,se,depth1,0,is_single);
  15658. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  15659. if (!arg2) _cimg_mp_return(arg1);
  15660. if (!arg1) _cimg_mp_return(arg2);
  15661. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_add,arg1,arg2);
  15662. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_add,arg1,arg2);
  15663. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_add,arg1,arg2);
  15664. if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1] + mem[arg2]);
  15665. if (code) { // Try to spot linear case 'a*b + c'.
  15666. CImg<ulongT> &pop = code.back();
  15667. if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) {
  15668. arg3 = (unsigned int)pop[1];
  15669. arg4 = (unsigned int)pop[2];
  15670. arg5 = (unsigned int)pop[3];
  15671. code.remove();
  15672. CImg<ulongT>::vector((ulongT)mp_linear_add,arg3,arg4,arg5,arg3==arg2?arg1:arg2).move_to(code);
  15673. _cimg_mp_return(arg3);
  15674. }
  15675. }
  15676. if (arg2==1) _cimg_mp_scalar1(mp_increment,arg1);
  15677. if (arg1==1) _cimg_mp_scalar1(mp_increment,arg2);
  15678. _cimg_mp_scalar2(mp_add,arg1,arg2);
  15679. }
  15680. for (ns = se1, s = se2, ps = pexpr._data + (se3 - expr._data); s>ss; --ns, --s, --ps)
  15681. if (*s=='-' && (*ns!='-' || ns!=se1) && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' &&
  15682. *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && *ps!='#' &&
  15683. (*ps!='e' || !(ps - pexpr._data>ss - expr._data && (*(ps - 1)=='.' || (*(ps - 1)>='0' &&
  15684. *(ps - 1)<='9')))) &&
  15685. level[s - expr._data]==clevel) { // Subtraction ('-')
  15686. _cimg_mp_op("Operator '-'");
  15687. arg1 = compile(ss,s,depth1,0,is_single);
  15688. arg2 = compile(s + 1,se,depth1,0,is_single);
  15689. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  15690. if (!arg2) _cimg_mp_return(arg1);
  15691. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_sub,arg1,arg2);
  15692. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_sub,arg1,arg2);
  15693. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) {
  15694. if (!arg1) _cimg_mp_vector1_v(mp_minus,arg2);
  15695. _cimg_mp_vector2_sv(mp_sub,arg1,arg2);
  15696. }
  15697. if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1] - mem[arg2]);
  15698. if (!arg1) _cimg_mp_scalar1(mp_minus,arg2);
  15699. if (code) { // Try to spot linear cases 'a*b - c' and 'c - a*b'.
  15700. CImg<ulongT> &pop = code.back();
  15701. if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) {
  15702. arg3 = (unsigned int)pop[1];
  15703. arg4 = (unsigned int)pop[2];
  15704. arg5 = (unsigned int)pop[3];
  15705. code.remove();
  15706. CImg<ulongT>::vector((ulongT)(arg3==arg1?mp_linear_sub_left:mp_linear_sub_right),
  15707. arg3,arg4,arg5,arg3==arg1?arg2:arg1).move_to(code);
  15708. _cimg_mp_return(arg3);
  15709. }
  15710. }
  15711. if (arg2==1) _cimg_mp_scalar1(mp_decrement,arg1);
  15712. _cimg_mp_scalar2(mp_sub,arg1,arg2);
  15713. }
  15714. for (s = se3, ns = se2; s>ss; --s, --ns)
  15715. if (*s=='*' && *ns=='*' && level[s - expr._data]==clevel) { // Complex multiplication ('**')
  15716. _cimg_mp_op("Operator '**'");
  15717. arg1 = compile(ss,s,depth1,0,is_single);
  15718. arg2 = compile(s + 2,se,depth1,0,is_single);
  15719. _cimg_mp_check_type(arg1,1,3,2);
  15720. _cimg_mp_check_type(arg2,2,3,2);
  15721. if (arg2==1) _cimg_mp_return(arg1);
  15722. if (arg1==1) _cimg_mp_return(arg2);
  15723. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) {
  15724. pos = vector(2);
  15725. CImg<ulongT>::vector((ulongT)mp_complex_mul,pos,arg1,arg2).move_to(code);
  15726. _cimg_mp_return(pos);
  15727. }
  15728. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_mul,arg1,arg2);
  15729. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_mul,arg1,arg2);
  15730. if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]*mem[arg2]);
  15731. if (!arg1 || !arg2) _cimg_mp_return(0);
  15732. _cimg_mp_scalar2(mp_mul,arg1,arg2);
  15733. }
  15734. for (s = se3, ns = se2; s>ss; --s, --ns)
  15735. if (*s=='/' && *ns=='/' && level[s - expr._data]==clevel) { // Complex division ('//')
  15736. _cimg_mp_op("Operator '//'");
  15737. arg1 = compile(ss,s,depth1,0,is_single);
  15738. arg2 = compile(s + 2,se,depth1,0,is_single);
  15739. _cimg_mp_check_type(arg1,1,3,2);
  15740. _cimg_mp_check_type(arg2,2,3,2);
  15741. if (arg2==1) _cimg_mp_return(arg1);
  15742. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) {
  15743. pos = vector(2);
  15744. CImg<ulongT>::vector((ulongT)mp_complex_div_vv,pos,arg1,arg2).move_to(code);
  15745. _cimg_mp_return(pos);
  15746. }
  15747. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_div,arg1,arg2);
  15748. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) {
  15749. pos = vector(2);
  15750. CImg<ulongT>::vector((ulongT)mp_complex_div_sv,pos,arg1,arg2).move_to(code);
  15751. _cimg_mp_return(pos);
  15752. }
  15753. if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]/mem[arg2]);
  15754. if (!arg1) _cimg_mp_return(0);
  15755. _cimg_mp_scalar2(mp_div,arg1,arg2);
  15756. }
  15757. for (s = se2; s>ss; --s) if (*s=='*' && level[s - expr._data]==clevel) { // Multiplication ('*')
  15758. _cimg_mp_op("Operator '*'");
  15759. arg1 = compile(ss,s,depth1,0,is_single);
  15760. arg2 = compile(s + 1,se,depth1,0,is_single);
  15761. p2 = _cimg_mp_size(arg2);
  15762. if (p2>0 && _cimg_mp_size(arg1)==p2*p2) { // Particular case of matrix multiplication
  15763. pos = vector(p2);
  15764. CImg<ulongT>::vector((ulongT)mp_matrix_mul,pos,arg1,arg2,p2,p2,1).move_to(code);
  15765. _cimg_mp_return(pos);
  15766. }
  15767. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  15768. if (arg2==1) _cimg_mp_return(arg1);
  15769. if (arg1==1) _cimg_mp_return(arg2);
  15770. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_mul,arg1,arg2);
  15771. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_mul,arg1,arg2);
  15772. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_mul,arg1,arg2);
  15773. if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]*mem[arg2]);
  15774. if (code) { // Try to spot double multiplication 'a*b*c'.
  15775. CImg<ulongT> &pop = code.back();
  15776. if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) {
  15777. arg3 = (unsigned int)pop[1];
  15778. arg4 = (unsigned int)pop[2];
  15779. arg5 = (unsigned int)pop[3];
  15780. code.remove();
  15781. CImg<ulongT>::vector((ulongT)mp_mul2,arg3,arg4,arg5,arg3==arg2?arg1:arg2).move_to(code);
  15782. _cimg_mp_return(arg3);
  15783. }
  15784. }
  15785. if (!arg1 || !arg2) _cimg_mp_return(0);
  15786. _cimg_mp_scalar2(mp_mul,arg1,arg2);
  15787. }
  15788. for (s = se2; s>ss; --s) if (*s=='/' && level[s - expr._data]==clevel) { // Division ('/')
  15789. _cimg_mp_op("Operator '/'");
  15790. arg1 = compile(ss,s,depth1,0,is_single);
  15791. arg2 = compile(s + 1,se,depth1,0,is_single);
  15792. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  15793. if (arg2==1) _cimg_mp_return(arg1);
  15794. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_div,arg1,arg2);
  15795. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_div,arg1,arg2);
  15796. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_div,arg1,arg2);
  15797. if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]/mem[arg2]);
  15798. if (!arg1) _cimg_mp_return(0);
  15799. _cimg_mp_scalar2(mp_div,arg1,arg2);
  15800. }
  15801. for (s = se2, ns = se1; s>ss; --s, --ns)
  15802. if (*s=='%' && *ns!='^' && level[s - expr._data]==clevel) { // Modulo ('%')
  15803. _cimg_mp_op("Operator '%'");
  15804. arg1 = compile(ss,s,depth1,0,is_single);
  15805. arg2 = compile(s + 1,se,depth1,0,is_single);
  15806. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  15807. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_modulo,arg1,arg2);
  15808. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_modulo,arg1,arg2);
  15809. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_modulo,arg1,arg2);
  15810. if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
  15811. _cimg_mp_constant(cimg::mod(mem[arg1],mem[arg2]));
  15812. _cimg_mp_scalar2(mp_modulo,arg1,arg2);
  15813. }
  15814. if (se1>ss) {
  15815. if (*ss=='+' && (*ss1!='+' || (ss2<se && *ss2>='0' && *ss2<='9'))) { // Unary plus ('+')
  15816. _cimg_mp_op("Operator '+'");
  15817. _cimg_mp_return(compile(ss1,se,depth1,0,is_single));
  15818. }
  15819. if (*ss=='-' && (*ss1!='-' || (ss2<se && *ss2>='0' && *ss2<='9'))) { // Unary minus ('-')
  15820. _cimg_mp_op("Operator '-'");
  15821. arg1 = compile(ss1,se,depth1,0,is_single);
  15822. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_minus,arg1);
  15823. if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(-mem[arg1]);
  15824. _cimg_mp_scalar1(mp_minus,arg1);
  15825. }
  15826. if (*ss=='!') { // Logical not ('!')
  15827. _cimg_mp_op("Operator '!'");
  15828. if (*ss1=='!') { // '!!expr' optimized as 'bool(expr)'
  15829. arg1 = compile(ss2,se,depth1,0,is_single);
  15830. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bool,arg1);
  15831. if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((bool)mem[arg1]);
  15832. _cimg_mp_scalar1(mp_bool,arg1);
  15833. }
  15834. arg1 = compile(ss1,se,depth1,0,is_single);
  15835. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_logical_not,arg1);
  15836. if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(!mem[arg1]);
  15837. _cimg_mp_scalar1(mp_logical_not,arg1);
  15838. }
  15839. if (*ss=='~') { // Bitwise not ('~')
  15840. _cimg_mp_op("Operator '~'");
  15841. arg1 = compile(ss1,se,depth1,0,is_single);
  15842. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bitwise_not,arg1);
  15843. if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(~(unsigned int)mem[arg1]);
  15844. _cimg_mp_scalar1(mp_bitwise_not,arg1);
  15845. }
  15846. }
  15847. for (s = se3, ns = se2; s>ss; --s, --ns)
  15848. if (*s=='^' && *ns=='^' && level[s - expr._data]==clevel) { // Complex power ('^^')
  15849. _cimg_mp_op("Operator '^^'");
  15850. arg1 = compile(ss,s,depth1,0,is_single);
  15851. arg2 = compile(s + 2,se,depth1,0,is_single);
  15852. _cimg_mp_check_type(arg1,1,3,2);
  15853. _cimg_mp_check_type(arg2,2,3,2);
  15854. if (arg2==1) _cimg_mp_return(arg1);
  15855. pos = vector(2);
  15856. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) {
  15857. CImg<ulongT>::vector((ulongT)mp_complex_pow_vv,pos,arg1,arg2).move_to(code);
  15858. _cimg_mp_return(pos);
  15859. }
  15860. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) {
  15861. CImg<ulongT>::vector((ulongT)mp_complex_pow_vs,pos,arg1,arg2).move_to(code);
  15862. _cimg_mp_return(pos);
  15863. }
  15864. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) {
  15865. CImg<ulongT>::vector((ulongT)mp_complex_pow_sv,pos,arg1,arg2).move_to(code);
  15866. _cimg_mp_return(pos);
  15867. }
  15868. CImg<ulongT>::vector((ulongT)mp_complex_pow_ss,pos,arg1,arg2).move_to(code);
  15869. _cimg_mp_return(pos);
  15870. }
  15871. for (s = se2; s>ss; --s)
  15872. if (*s=='^' && level[s - expr._data]==clevel) { // Power ('^')
  15873. _cimg_mp_op("Operator '^'");
  15874. arg1 = compile(ss,s,depth1,0,is_single);
  15875. arg2 = compile(s + 1,se,depth1,0,is_single);
  15876. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  15877. if (arg2==1) _cimg_mp_return(arg1);
  15878. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_pow,arg1,arg2);
  15879. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_pow,arg1,arg2);
  15880. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_pow,arg1,arg2);
  15881. if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
  15882. _cimg_mp_constant(std::pow(mem[arg1],mem[arg2]));
  15883. switch (arg2) {
  15884. case 0 : _cimg_mp_return(1);
  15885. case 2 : _cimg_mp_scalar1(mp_sqr,arg1);
  15886. case 3 : _cimg_mp_scalar1(mp_pow3,arg1);
  15887. case 4 : _cimg_mp_scalar1(mp_pow4,arg1);
  15888. default :
  15889. if (_cimg_mp_is_constant(arg2)) {
  15890. if (mem[arg2]==0.5) { _cimg_mp_scalar1(mp_sqrt,arg1); }
  15891. else if (mem[arg2]==0.25) { _cimg_mp_scalar1(mp_pow0_25,arg1); }
  15892. }
  15893. _cimg_mp_scalar2(mp_pow,arg1,arg2);
  15894. }
  15895. }
  15896. // Percentage computation.
  15897. if (*se1=='%') {
  15898. arg1 = compile(ss,se1,depth1,0,is_single);
  15899. arg2 = _cimg_mp_is_constant(arg1)?0:constant(100);
  15900. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector2_vs(mp_div,arg1,arg2);
  15901. if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(mem[arg1]/100);
  15902. _cimg_mp_scalar2(mp_div,arg1,arg2);
  15903. }
  15904. is_sth = ss1<se1 && (*ss=='+' || *ss=='-') && *ss1==*ss; // is pre-?
  15905. if (is_sth || (se2>ss && (*se1=='+' || *se1=='-') && *se2==*se1)) { // Pre/post-decrement and increment
  15906. if ((is_sth && *ss=='+') || (!is_sth && *se1=='+')) {
  15907. _cimg_mp_op("Operator '++'");
  15908. op = mp_self_increment;
  15909. } else {
  15910. _cimg_mp_op("Operator '--'");
  15911. op = mp_self_decrement;
  15912. }
  15913. ref.assign(7);
  15914. arg1 = is_sth?compile(ss2,se,depth1,ref,is_single):
  15915. compile(ss,se2,depth1,ref,is_single); // Variable slot
  15916. // Apply operator on a copy to prevent modifying a constant or a variable.
  15917. if (*ref && (_cimg_mp_is_constant(arg1) || _cimg_mp_is_vector(arg1) || _cimg_mp_is_variable(arg1))) {
  15918. if (_cimg_mp_is_vector(arg1)) arg1 = vector_copy(arg1);
  15919. else arg1 = scalar1(mp_copy,arg1);
  15920. }
  15921. if (is_sth) pos = arg1; // Determine return indice, depending on pre/post action
  15922. else {
  15923. if (_cimg_mp_is_vector(arg1)) pos = vector_copy(arg1);
  15924. else pos = scalar1(mp_copy,arg1);
  15925. }
  15926. if (*ref==1) { // Vector value (scalar): V[k]++
  15927. arg3 = ref[1]; // Vector slot
  15928. arg4 = ref[2]; // Index
  15929. if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
  15930. CImg<ulongT>::vector((ulongT)op,arg1,1).move_to(code);
  15931. CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_size(arg3),arg4,arg1).
  15932. move_to(code);
  15933. _cimg_mp_return(pos);
  15934. }
  15935. if (*ref==2) { // Image value (scalar): i/j[_#ind,off]++
  15936. if (!is_single) is_parallelizable = false;
  15937. p1 = ref[1]; // Index
  15938. is_relative = (bool)ref[2];
  15939. arg3 = ref[3]; // Offset
  15940. if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
  15941. CImg<ulongT>::vector((ulongT)op,arg1).move_to(code);
  15942. if (p1!=~0U) {
  15943. if (!listout) _cimg_mp_return(pos);
  15944. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
  15945. arg1,p1,arg3).move_to(code);
  15946. } else {
  15947. if (!imgout) _cimg_mp_return(pos);
  15948. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
  15949. arg1,arg3).move_to(code);
  15950. }
  15951. _cimg_mp_return(pos);
  15952. }
  15953. if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c)++
  15954. if (!is_single) is_parallelizable = false;
  15955. p1 = ref[1]; // Index
  15956. is_relative = (bool)ref[2];
  15957. arg3 = ref[3]; // X
  15958. arg4 = ref[4]; // Y
  15959. arg5 = ref[5]; // Z
  15960. arg6 = ref[6]; // C
  15961. if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
  15962. CImg<ulongT>::vector((ulongT)op,arg1).move_to(code);
  15963. if (p1!=~0U) {
  15964. if (!listout) _cimg_mp_return(pos);
  15965. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
  15966. arg1,p1,arg3,arg4,arg5,arg6).move_to(code);
  15967. } else {
  15968. if (!imgout) _cimg_mp_return(pos);
  15969. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
  15970. arg1,arg3,arg4,arg5,arg6).move_to(code);
  15971. }
  15972. _cimg_mp_return(pos);
  15973. }
  15974. if (*ref==4) { // Image value (vector): I/J[_#ind,off]++
  15975. if (!is_single) is_parallelizable = false;
  15976. p1 = ref[1]; // Index
  15977. is_relative = (bool)ref[2];
  15978. arg3 = ref[3]; // Offset
  15979. if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
  15980. self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1);
  15981. if (p1!=~0U) {
  15982. if (!listout) _cimg_mp_return(pos);
  15983. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
  15984. arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code);
  15985. } else {
  15986. if (!imgout) _cimg_mp_return(pos);
  15987. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
  15988. arg1,arg3,_cimg_mp_size(arg1)).move_to(code);
  15989. }
  15990. _cimg_mp_return(pos);
  15991. }
  15992. if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c)++
  15993. if (!is_single) is_parallelizable = false;
  15994. p1 = ref[1]; // Index
  15995. is_relative = (bool)ref[2];
  15996. arg3 = ref[3]; // X
  15997. arg4 = ref[4]; // Y
  15998. arg5 = ref[5]; // Z
  15999. if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
  16000. self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1);
  16001. if (p1!=~0U) {
  16002. if (!listout) _cimg_mp_return(pos);
  16003. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
  16004. arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
  16005. } else {
  16006. if (!imgout) _cimg_mp_return(pos);
  16007. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
  16008. arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
  16009. }
  16010. _cimg_mp_return(pos);
  16011. }
  16012. if (_cimg_mp_is_vector(arg1)) { // Vector variable: V++
  16013. self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1);
  16014. _cimg_mp_return(pos);
  16015. }
  16016. if (_cimg_mp_is_variable(arg1)) { // Scalar variable: s++
  16017. CImg<ulongT>::vector((ulongT)op,arg1).move_to(code);
  16018. _cimg_mp_return(pos);
  16019. }
  16020. if (is_sth) variable_name.assign(ss2,(unsigned int)(se - ss1));
  16021. else variable_name.assign(ss,(unsigned int)(se1 - ss));
  16022. variable_name.back() = 0;
  16023. cimg::strpare(variable_name,false,true);
  16024. *se = saved_char;
  16025. cimg::strellipsize(variable_name,64);
  16026. s0 = ss - 4>expr._data?ss - 4:expr._data;
  16027. cimg::strellipsize(s0,64);
  16028. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  16029. "CImg<%s>::%s: %s: Invalid %slvalue '%s', "
  16030. "in expression '%s%s%s'.",
  16031. pixel_type(),_cimg_mp_calling_function,s_op,
  16032. _cimg_mp_is_constant(arg1)?"const ":"",
  16033. variable_name._data,
  16034. s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
  16035. }
  16036. // Array-like access to vectors and image values 'i/j/I/J[_#ind,offset,_boundary]' and 'vector[offset]'.
  16037. if (*se1==']' && *ss!='[') {
  16038. _cimg_mp_op("Value accessor '[]'");
  16039. is_relative = *ss=='j' || *ss=='J';
  16040. s0 = s1 = std::strchr(ss,'['); if (s0) { do { --s1; } while ((signed char)*s1<=' '); cimg::swap(*s0,*++s1); }
  16041. if ((*ss=='I' || *ss=='J') && *ss1=='[' &&
  16042. (reserved_label[*ss]==~0U || !_cimg_mp_is_vector(reserved_label[*ss]))) { // Image value as a vector
  16043. if (*ss2=='#') { // Index specified
  16044. s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  16045. p1 = compile(ss3,s0++,depth1,0,is_single);
  16046. _cimg_mp_check_list(false);
  16047. } else { p1 = ~0U; s0 = ss2; }
  16048. s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  16049. p2 = 1 + (p1!=~0U);
  16050. arg1 = compile(s0,s1,depth1,0,is_single); // Offset
  16051. _cimg_mp_check_type(arg1,p2,1,0);
  16052. arg2 = ~0U;
  16053. if (s1<se1) {
  16054. arg2 = compile(++s1,se1,depth1,0,is_single); // Boundary
  16055. _cimg_mp_check_type(arg2,p2 + 1,1,0);
  16056. }
  16057. if (p_ref && arg2==~0U) {
  16058. *p_ref = 4;
  16059. p_ref[1] = p1;
  16060. p_ref[2] = (unsigned int)is_relative;
  16061. p_ref[3] = arg1;
  16062. if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -2; // Prevent from being used in further optimization
  16063. if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -2;
  16064. }
  16065. p2 = ~0U; // 'p2' must be the dimension of the vector-valued operand if any
  16066. if (p1==~0U) p2 = imgin._spectrum;
  16067. else if (_cimg_mp_is_constant(p1)) {
  16068. p3 = (unsigned int)cimg::mod((int)mem[p1],listin.width());
  16069. p2 = listin[p3]._spectrum;
  16070. }
  16071. _cimg_mp_check_vector0(p2);
  16072. pos = vector(p2);
  16073. if (p1!=~0U) {
  16074. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_Joff:mp_list_Ioff),
  16075. pos,p1,arg1,arg2==~0U?_cimg_mp_boundary:arg2,p2).move_to(code);
  16076. } else {
  16077. need_input_copy = true;
  16078. CImg<ulongT>::vector((ulongT)(is_relative?mp_Joff:mp_Ioff),
  16079. pos,arg1,arg2==~0U?_cimg_mp_boundary:arg2,p2).move_to(code);
  16080. }
  16081. _cimg_mp_return(pos);
  16082. }
  16083. if ((*ss=='i' || *ss=='j') && *ss1=='[' &&
  16084. (reserved_label[*ss]==~0U || !_cimg_mp_is_vector(reserved_label[*ss]))) { // Image value as a scalar
  16085. if (*ss2=='#') { // Index specified
  16086. s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  16087. p1 = compile(ss3,s0++,depth1,0,is_single);
  16088. } else { p1 = ~0U; s0 = ss2; }
  16089. s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  16090. arg1 = compile(s0,s1,depth1,0,is_single); // Offset
  16091. arg2 = s1<se1?compile(++s1,se1,depth1,0,is_single):~0U; // Boundary
  16092. if (p_ref && arg2==~0U) {
  16093. *p_ref = 2;
  16094. p_ref[1] = p1;
  16095. p_ref[2] = (unsigned int)is_relative;
  16096. p_ref[3] = arg1;
  16097. if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -2; // Prevent from being used in further optimization
  16098. if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -2;
  16099. }
  16100. if (p1!=~0U) {
  16101. if (!listin) _cimg_mp_return(0);
  16102. pos = scalar3(is_relative?mp_list_joff:mp_list_ioff,p1,arg1,arg2==~0U?_cimg_mp_boundary:arg2);
  16103. } else {
  16104. if (!imgin) _cimg_mp_return(0);
  16105. need_input_copy = true;
  16106. pos = scalar2(is_relative?mp_joff:mp_ioff,arg1,arg2==~0U?_cimg_mp_boundary:arg2);
  16107. }
  16108. memtype[pos] = -2; // Prevent from being used in further optimization
  16109. _cimg_mp_return(pos);
  16110. }
  16111. s0 = se1; while (s0>ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0;
  16112. if (s0>ss) { // Vector value
  16113. arg1 = compile(ss,s0,depth1,0,is_single);
  16114. if (_cimg_mp_is_scalar(arg1)) {
  16115. variable_name.assign(ss,(unsigned int)(s0 - ss + 1)).back() = 0;
  16116. *se = saved_char;
  16117. cimg::strellipsize(variable_name,64);
  16118. s0 = ss - 4>expr._data?ss - 4:expr._data;
  16119. cimg::strellipsize(s0,64);
  16120. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  16121. "CImg<%s>::%s: %s: Array brackets used on non-vector variable '%s', "
  16122. "in expression '%s%s%s'.",
  16123. pixel_type(),_cimg_mp_calling_function,s_op,
  16124. variable_name._data,
  16125. s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
  16126. }
  16127. s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  16128. if (s1<se1) { // Two arguments -> sub-vector extraction
  16129. p1 = _cimg_mp_size(arg1);
  16130. arg2 = compile(++s0,s1,depth1,0,is_single); // Starting indice
  16131. arg3 = compile(++s1,se1,depth1,0,is_single); // Length
  16132. _cimg_mp_check_constant(arg3,2,3);
  16133. arg3 = (unsigned int)mem[arg3];
  16134. pos = vector(arg3);
  16135. CImg<ulongT>::vector((ulongT)mp_vector_crop,pos,arg1,p1,arg2,arg3).move_to(code);
  16136. _cimg_mp_return(pos);
  16137. }
  16138. // One argument -> vector value reference
  16139. arg2 = compile(++s0,se1,depth1,0,is_single);
  16140. if (_cimg_mp_is_constant(arg2)) { // Constant index
  16141. nb = (int)mem[arg2];
  16142. if (nb>=0 && nb<(int)_cimg_mp_size(arg1)) _cimg_mp_return(arg1 + 1 + nb);
  16143. variable_name.assign(ss,(unsigned int)(s0 - ss)).back() = 0;
  16144. *se = saved_char;
  16145. cimg::strellipsize(variable_name,64);
  16146. s0 = ss - 4>expr._data?ss - 4:expr._data;
  16147. cimg::strellipsize(s0,64);
  16148. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  16149. "CImg<%s>::%s: Out-of-bounds reference '%s[%d]' "
  16150. "(vector '%s' has dimension %u), "
  16151. "in expression '%s%s%s'.",
  16152. pixel_type(),_cimg_mp_calling_function,
  16153. variable_name._data,nb,
  16154. variable_name._data,_cimg_mp_size(arg1),
  16155. s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
  16156. }
  16157. if (p_ref) {
  16158. *p_ref = 1;
  16159. p_ref[1] = arg1;
  16160. p_ref[2] = arg2;
  16161. if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2; // Prevent from being used in further optimization
  16162. }
  16163. pos = scalar3(mp_vector_off,arg1,_cimg_mp_size(arg1),arg2);
  16164. memtype[pos] = -2; // Prevent from being used in further optimization
  16165. _cimg_mp_return(pos);
  16166. }
  16167. }
  16168. // Look for a function call, an access to image value, or a parenthesis.
  16169. if (*se1==')') {
  16170. if (*ss=='(') _cimg_mp_return(compile(ss1,se1,depth1,p_ref,is_single)); // Simple parentheses
  16171. _cimg_mp_op("Value accessor '()'");
  16172. is_relative = *ss=='j' || *ss=='J';
  16173. s0 = s1 = std::strchr(ss,'('); if (s0) { do { --s1; } while ((signed char)*s1<=' '); cimg::swap(*s0,*++s1); }
  16174. // I/J(_#ind,_x,_y,_z,_interpolation,_boundary_conditions)
  16175. if ((*ss=='I' || *ss=='J') && *ss1=='(') { // Image value as scalar
  16176. if (*ss2=='#') { // Index specified
  16177. s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  16178. p1 = compile(ss3,s0++,depth1,0,is_single);
  16179. _cimg_mp_check_list(false);
  16180. } else { p1 = ~0U; s0 = ss2; }
  16181. arg1 = is_relative?0U:(unsigned int)_cimg_mp_slot_x;
  16182. arg2 = is_relative?0U:(unsigned int)_cimg_mp_slot_y;
  16183. arg3 = is_relative?0U:(unsigned int)_cimg_mp_slot_z;
  16184. arg4 = arg5 = ~0U;
  16185. if (s0<se1) {
  16186. s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  16187. arg1 = compile(s0,s1,depth1,0,is_single);
  16188. if (_cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
  16189. p2 = _cimg_mp_size(arg1);
  16190. ++arg1;
  16191. if (p2>1) {
  16192. arg2 = arg1 + 1;
  16193. if (p2>2) arg3 = arg2 + 1;
  16194. }
  16195. if (s1<se1) {
  16196. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  16197. arg4 = compile(s1,s2,depth1,0,is_single);
  16198. arg5 = s2<se1?compile(++s2,se1,depth1,0,is_single):~0U;
  16199. }
  16200. } else if (s1<se1) {
  16201. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  16202. arg2 = compile(s1,s2,depth1,0,is_single);
  16203. if (s2<se1) {
  16204. s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
  16205. arg3 = compile(s2,s3,depth1,0,is_single);
  16206. if (s3<se1) {
  16207. s2 = ++s3; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  16208. arg4 = compile(s3,s2,depth1,0,is_single);
  16209. arg5 = s2<se1?compile(++s2,se1,depth1,0,is_single):~0U;
  16210. }
  16211. }
  16212. }
  16213. }
  16214. if (p_ref && arg4==~0U && arg5==~0U) {
  16215. *p_ref = 5;
  16216. p_ref[1] = p1;
  16217. p_ref[2] = (unsigned int)is_relative;
  16218. p_ref[3] = arg1;
  16219. p_ref[4] = arg2;
  16220. p_ref[5] = arg3;
  16221. if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -2; // Prevent from being used in further optimization
  16222. if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -2;
  16223. if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2;
  16224. if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -2;
  16225. }
  16226. p2 = ~0U; // 'p2' must be the dimension of the vector-valued operand if any
  16227. if (p1==~0U) p2 = imgin._spectrum;
  16228. else if (_cimg_mp_is_constant(p1)) {
  16229. p3 = (unsigned int)cimg::mod((int)mem[p1],listin.width());
  16230. p2 = listin[p3]._spectrum;
  16231. }
  16232. _cimg_mp_check_vector0(p2);
  16233. pos = vector(p2);
  16234. if (p1!=~0U)
  16235. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_Jxyz:mp_list_Ixyz),
  16236. pos,p1,arg1,arg2,arg3,
  16237. arg4==~0U?_cimg_mp_interpolation:arg4,
  16238. arg5==~0U?_cimg_mp_boundary:arg5,p2).move_to(code);
  16239. else {
  16240. need_input_copy = true;
  16241. CImg<ulongT>::vector((ulongT)(is_relative?mp_Jxyz:mp_Ixyz),
  16242. pos,arg1,arg2,arg3,
  16243. arg4==~0U?_cimg_mp_interpolation:arg4,
  16244. arg5==~0U?_cimg_mp_boundary:arg5,p2).move_to(code);
  16245. }
  16246. _cimg_mp_return(pos);
  16247. }
  16248. // i/j(_#ind,_x,_y,_z,_c,_interpolation,_boundary_conditions)
  16249. if ((*ss=='i' || *ss=='j') && *ss1=='(') { // Image value as scalar
  16250. if (*ss2=='#') { // Index specified
  16251. s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  16252. p1 = compile(ss3,s0++,depth1,0,is_single);
  16253. } else { p1 = ~0U; s0 = ss2; }
  16254. arg1 = is_relative?0U:(unsigned int)_cimg_mp_slot_x;
  16255. arg2 = is_relative?0U:(unsigned int)_cimg_mp_slot_y;
  16256. arg3 = is_relative?0U:(unsigned int)_cimg_mp_slot_z;
  16257. arg4 = is_relative?0U:(unsigned int)_cimg_mp_slot_c;
  16258. arg5 = arg6 = ~0U;
  16259. if (s0<se1) {
  16260. s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  16261. arg1 = compile(s0,s1,depth1,0,is_single);
  16262. if (_cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
  16263. p2 = _cimg_mp_size(arg1);
  16264. ++arg1;
  16265. if (p2>1) {
  16266. arg2 = arg1 + 1;
  16267. if (p2>2) {
  16268. arg3 = arg2 + 1;
  16269. if (p2>3) arg4 = arg3 + 1;
  16270. }
  16271. }
  16272. if (s1<se1) {
  16273. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  16274. arg5 = compile(s1,s2,depth1,0,is_single);
  16275. arg6 = s2<se1?compile(++s2,se1,depth1,0,is_single):~0U;
  16276. }
  16277. } else if (s1<se1) {
  16278. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  16279. arg2 = compile(s1,s2,depth1,0,is_single);
  16280. if (s2<se1) {
  16281. s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
  16282. arg3 = compile(s2,s3,depth1,0,is_single);
  16283. if (s3<se1) {
  16284. s2 = ++s3; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  16285. arg4 = compile(s3,s2,depth1,0,is_single);
  16286. if (s2<se1) {
  16287. s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
  16288. arg5 = compile(s2,s3,depth1,0,is_single);
  16289. arg6 = s3<se1?compile(++s3,se1,depth1,0,is_single):~0U;
  16290. }
  16291. }
  16292. }
  16293. }
  16294. }
  16295. if (p_ref && arg5==~0U && arg6==~0U) {
  16296. *p_ref = 3;
  16297. p_ref[1] = p1;
  16298. p_ref[2] = (unsigned int)is_relative;
  16299. p_ref[3] = arg1;
  16300. p_ref[4] = arg2;
  16301. p_ref[5] = arg3;
  16302. p_ref[6] = arg4;
  16303. if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -2; // Prevent from being used in further optimization
  16304. if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -2;
  16305. if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2;
  16306. if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -2;
  16307. if (_cimg_mp_is_comp(arg4)) memtype[arg4] = -2;
  16308. }
  16309. if (p1!=~0U) {
  16310. if (!listin) _cimg_mp_return(0);
  16311. pos = scalar7(is_relative?mp_list_jxyzc:mp_list_ixyzc,
  16312. p1,arg1,arg2,arg3,arg4,
  16313. arg5==~0U?_cimg_mp_interpolation:arg5,
  16314. arg6==~0U?_cimg_mp_boundary:arg6);
  16315. } else {
  16316. if (!imgin) _cimg_mp_return(0);
  16317. need_input_copy = true;
  16318. pos = scalar6(is_relative?mp_jxyzc:mp_ixyzc,
  16319. arg1,arg2,arg3,arg4,
  16320. arg5==~0U?_cimg_mp_interpolation:arg5,
  16321. arg6==~0U?_cimg_mp_boundary:arg6);
  16322. }
  16323. memtype[pos] = -2; // Prevent from being used in further optimization
  16324. _cimg_mp_return(pos);
  16325. }
  16326. // Mathematical functions.
  16327. switch (*ss) {
  16328. case '_' :
  16329. if (*ss1=='(') // Skip arguments
  16330. _cimg_mp_return_nan();
  16331. break;
  16332. case 'a' :
  16333. if (!std::strncmp(ss,"abs(",4)) { // Absolute value
  16334. _cimg_mp_op("Function 'abs()'");
  16335. arg1 = compile(ss4,se1,depth1,0,is_single);
  16336. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_abs,arg1);
  16337. if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::abs(mem[arg1]));
  16338. _cimg_mp_scalar1(mp_abs,arg1);
  16339. }
  16340. if (!std::strncmp(ss,"acos(",5)) { // Arccos
  16341. _cimg_mp_op("Function 'acos()'");
  16342. arg1 = compile(ss5,se1,depth1,0,is_single);
  16343. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_acos,arg1);
  16344. if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::acos(mem[arg1]));
  16345. _cimg_mp_scalar1(mp_acos,arg1);
  16346. }
  16347. if (!std::strncmp(ss,"arg(",4)) { // Nth argument
  16348. _cimg_mp_op("Function 'arg()'");
  16349. s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  16350. arg1 = compile(ss4,s1,depth1,0,is_single);
  16351. _cimg_mp_check_type(arg1,1,1,0);
  16352. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  16353. arg2 = compile(s1,s2,depth1,0,is_single);
  16354. p2 = _cimg_mp_size(arg2);
  16355. p3 = 3;
  16356. CImg<ulongT>::vector((ulongT)mp_arg,0,0,p2,arg1,arg2).move_to(l_opcode);
  16357. for (s = ++s2; s<se; ++s) {
  16358. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  16359. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  16360. arg3 = compile(s,ns,depth1,0,is_single);
  16361. _cimg_mp_check_type(arg3,p3,p2?2:1,p2);
  16362. CImg<ulongT>::vector(arg3).move_to(l_opcode);
  16363. ++p3;
  16364. s = ns;
  16365. }
  16366. (l_opcode>'y').move_to(opcode);
  16367. opcode[2] = opcode._height;
  16368. if (_cimg_mp_is_constant(arg1)) {
  16369. p3-=1; // Number of args
  16370. arg1 = (unsigned int)(mem[arg1]<0?mem[arg1] + p3:mem[arg1]);
  16371. if (arg1<p3) _cimg_mp_return(opcode[4 + arg1]);
  16372. if (p2) {
  16373. pos = vector(p2);
  16374. std::memset(&mem[pos] + 1,0,p2*sizeof(double));
  16375. _cimg_mp_return(pos);
  16376. } else _cimg_mp_return(0);
  16377. }
  16378. pos = opcode[1] = p2?vector(p2):scalar();
  16379. opcode.move_to(code);
  16380. _cimg_mp_return(pos);
  16381. }
  16382. if (!std::strncmp(ss,"asin(",5)) { // Arcsin
  16383. _cimg_mp_op("Function 'asin()'");
  16384. arg1 = compile(ss5,se1,depth1,0,is_single);
  16385. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_asin,arg1);
  16386. if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::asin(mem[arg1]));
  16387. _cimg_mp_scalar1(mp_asin,arg1);
  16388. }
  16389. if (!std::strncmp(ss,"atan(",5)) { // Arctan
  16390. _cimg_mp_op("Function 'atan()'");
  16391. arg1 = compile(ss5,se1,depth1,0,is_single);
  16392. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_atan,arg1);
  16393. if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::atan(mem[arg1]));
  16394. _cimg_mp_scalar1(mp_atan,arg1);
  16395. }
  16396. if (!std::strncmp(ss,"atan2(",6)) { // Arctan2
  16397. _cimg_mp_op("Function 'atan2()'");
  16398. s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  16399. arg1 = compile(ss6,s1,depth1,0,is_single);
  16400. arg2 = compile(++s1,se1,depth1,0,is_single);
  16401. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  16402. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_atan2,arg1,arg2);
  16403. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_atan2,arg1,arg2);
  16404. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_atan2,arg1,arg2);
  16405. if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
  16406. _cimg_mp_constant(std::atan2(mem[arg1],mem[arg2]));
  16407. _cimg_mp_scalar2(mp_atan2,arg1,arg2);
  16408. }
  16409. break;
  16410. case 'b' :
  16411. if (!std::strncmp(ss,"bool(",5)) { // Boolean cast
  16412. _cimg_mp_op("Function 'bool()'");
  16413. arg1 = compile(ss5,se1,depth1,0,is_single);
  16414. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bool,arg1);
  16415. if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((bool)mem[arg1]);
  16416. _cimg_mp_scalar1(mp_bool,arg1);
  16417. }
  16418. if (!std::strncmp(ss,"break(",6)) { // Complex absolute value
  16419. if (pexpr[se2 - expr._data]=='(') { // no arguments?
  16420. CImg<ulongT>::vector((ulongT)mp_break,_cimg_mp_slot_nan).move_to(code);
  16421. _cimg_mp_return_nan();
  16422. }
  16423. }
  16424. if (!std::strncmp(ss,"breakpoint(",11)) { // Break point (for abort test)
  16425. _cimg_mp_op("Function 'breakpoint()'");
  16426. if (pexpr[se2 - expr._data]=='(') { // no arguments?
  16427. CImg<ulongT>::vector((ulongT)mp_breakpoint,_cimg_mp_slot_nan).move_to(code);
  16428. _cimg_mp_return_nan();
  16429. }
  16430. }
  16431. break;
  16432. case 'c' :
  16433. if (!std::strncmp(ss,"cabs(",5)) { // Complex absolute value
  16434. _cimg_mp_op("Function 'cabs()'");
  16435. arg1 = compile(ss5,se1,depth1,0,is_single);
  16436. _cimg_mp_check_type(arg1,0,2,2);
  16437. _cimg_mp_scalar2(mp_complex_abs,arg1 + 1,arg1 + 2);
  16438. }
  16439. if (!std::strncmp(ss,"carg(",5)) { // Complex argument
  16440. _cimg_mp_op("Function 'carg()'");
  16441. arg1 = compile(ss5,se1,depth1,0,is_single);
  16442. _cimg_mp_check_type(arg1,0,2,2);
  16443. _cimg_mp_scalar2(mp_atan2,arg1 + 2,arg1 + 1);
  16444. }
  16445. if (!std::strncmp(ss,"cats(",5)) { // Concatenate strings
  16446. _cimg_mp_op("Function 'cats()'");
  16447. CImg<ulongT>::vector((ulongT)mp_cats,0,0,0).move_to(l_opcode);
  16448. arg1 = 0;
  16449. for (s = ss5; s<se; ++s) {
  16450. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  16451. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  16452. arg1 = compile(s,ns,depth1,0,is_single);
  16453. CImg<ulongT>::vector(arg1,_cimg_mp_size(arg1)).move_to(l_opcode);
  16454. s = ns;
  16455. }
  16456. _cimg_mp_check_constant(arg1,1,3); // Last argument = output vector size
  16457. l_opcode.remove();
  16458. (l_opcode>'y').move_to(opcode);
  16459. p1 = (unsigned int)mem[arg1];
  16460. pos = vector(p1);
  16461. opcode[1] = pos;
  16462. opcode[2] = p1;
  16463. opcode[3] = opcode._height;
  16464. opcode.move_to(code);
  16465. _cimg_mp_return(pos);
  16466. }
  16467. if (!std::strncmp(ss,"cbrt(",5)) { // Cubic root
  16468. _cimg_mp_op("Function 'cbrt()'");
  16469. arg1 = compile(ss5,se1,depth1,0,is_single);
  16470. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cbrt,arg1);
  16471. if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::cbrt(mem[arg1]));
  16472. _cimg_mp_scalar1(mp_cbrt,arg1);
  16473. }
  16474. if (!std::strncmp(ss,"cconj(",6)) { // Complex conjugate
  16475. _cimg_mp_op("Function 'cconj()'");
  16476. arg1 = compile(ss6,se1,depth1,0,is_single);
  16477. _cimg_mp_check_type(arg1,0,2,2);
  16478. pos = vector(2);
  16479. CImg<ulongT>::vector((ulongT)mp_complex_conj,pos,arg1).move_to(code);
  16480. _cimg_mp_return(pos);
  16481. }
  16482. if (!std::strncmp(ss,"ceil(",5)) { // Ceil
  16483. _cimg_mp_op("Function 'ceil()'");
  16484. arg1 = compile(ss5,se1,depth1,0,is_single);
  16485. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_ceil,arg1);
  16486. if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::ceil(mem[arg1]));
  16487. _cimg_mp_scalar1(mp_ceil,arg1);
  16488. }
  16489. if (!std::strncmp(ss,"cexp(",5)) { // Complex exponential
  16490. _cimg_mp_op("Function 'cexp()'");
  16491. arg1 = compile(ss5,se1,depth1,0,is_single);
  16492. _cimg_mp_check_type(arg1,0,2,2);
  16493. pos = vector(2);
  16494. CImg<ulongT>::vector((ulongT)mp_complex_exp,pos,arg1).move_to(code);
  16495. _cimg_mp_return(pos);
  16496. }
  16497. if (!std::strncmp(ss,"clog(",5)) { // Complex logarithm
  16498. _cimg_mp_op("Function 'clog()'");
  16499. arg1 = compile(ss5,se1,depth1,0,is_single);
  16500. _cimg_mp_check_type(arg1,0,2,2);
  16501. pos = vector(2);
  16502. CImg<ulongT>::vector((ulongT)mp_complex_log,pos,arg1).move_to(code);
  16503. _cimg_mp_return(pos);
  16504. }
  16505. if (!std::strncmp(ss,"continue(",9)) { // Complex absolute value
  16506. if (pexpr[se2 - expr._data]=='(') { // no arguments?
  16507. CImg<ulongT>::vector((ulongT)mp_continue,_cimg_mp_slot_nan).move_to(code);
  16508. _cimg_mp_return_nan();
  16509. }
  16510. }
  16511. if (!std::strncmp(ss,"copy(",5)) { // Memory copy
  16512. _cimg_mp_op("Function 'copy()'");
  16513. ref.assign(14);
  16514. s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  16515. arg1 = p1 = compile(ss5,s1,depth1,ref,is_single);
  16516. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  16517. arg2 = compile(s1,s2,depth1,ref._data + 7,is_single);
  16518. arg3 = ~0U; arg4 = arg5 = arg6 = 1;
  16519. if (s2<se1) {
  16520. s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
  16521. arg3 = compile(s2,s3,depth1,0,is_single);
  16522. if (s3<se1) {
  16523. s1 = ++s3; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  16524. arg4 = compile(s3,s1,depth1,0,is_single);
  16525. if (s1<se1) {
  16526. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  16527. arg5 = compile(s1,s2,depth1,0,is_single);
  16528. arg6 = s2<se1?compile(++s2,se1,depth1,0,is_single):1;
  16529. }
  16530. }
  16531. }
  16532. if (_cimg_mp_is_vector(arg1) && !ref[0]) ++arg1;
  16533. if (_cimg_mp_is_vector(arg2)) {
  16534. if (arg3==~0U) arg3 = _cimg_mp_size(arg2);
  16535. if (!ref[7]) ++arg2;
  16536. }
  16537. if (arg3==~0U) arg3 = 1;
  16538. _cimg_mp_check_type(arg3,3,1,0);
  16539. _cimg_mp_check_type(arg4,4,1,0);
  16540. _cimg_mp_check_type(arg5,5,1,0);
  16541. _cimg_mp_check_type(arg6,5,1,0);
  16542. CImg<ulongT>(1,22).move_to(code);
  16543. code.back().get_shared_rows(0,7).fill((ulongT)mp_memcopy,p1,arg1,arg2,arg3,arg4,arg5,arg6);
  16544. code.back().get_shared_rows(8,21).fill(ref);
  16545. _cimg_mp_return(p1);
  16546. }
  16547. if (!std::strncmp(ss,"cos(",4)) { // Cosine
  16548. _cimg_mp_op("Function 'cos()'");
  16549. arg1 = compile(ss4,se1,depth1,0,is_single);
  16550. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cos,arg1);
  16551. if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::cos(mem[arg1]));
  16552. _cimg_mp_scalar1(mp_cos,arg1);
  16553. }
  16554. if (!std::strncmp(ss,"cosh(",5)) { // Hyperbolic cosine
  16555. _cimg_mp_op("Function 'cosh()'");
  16556. arg1 = compile(ss5,se1,depth1,0,is_single);
  16557. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cosh,arg1);
  16558. if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::cosh(mem[arg1]));
  16559. _cimg_mp_scalar1(mp_cosh,arg1);
  16560. }
  16561. if (!std::strncmp(ss,"critical(",9)) { // Critical section (single thread at a time)
  16562. _cimg_mp_op("Function 'critical()'");
  16563. p1 = code._width;
  16564. arg1 = compile(ss + 9,se1,depth1,p_ref,true);
  16565. CImg<ulongT>::vector((ulongT)mp_critical,arg1,code._width - p1).move_to(code,p1);
  16566. _cimg_mp_return(arg1);
  16567. }
  16568. if (!std::strncmp(ss,"crop(",5)) { // Image crop
  16569. _cimg_mp_op("Function 'crop()'");
  16570. if (*ss5=='#') { // Index specified
  16571. s0 = ss6; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  16572. p1 = compile(ss6,s0++,depth1,0,is_single);
  16573. _cimg_mp_check_list(false);
  16574. } else { p1 = ~0U; s0 = ss5; need_input_copy = true; }
  16575. pos = 0;
  16576. is_sth = false; // Coordinates specified as a vector?
  16577. if (ss5<se1) for (s = s0; s<se; ++s, ++pos) {
  16578. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  16579. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  16580. arg1 = compile(s,ns,depth1,0,is_single);
  16581. if (!pos && _cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
  16582. opcode = CImg<ulongT>::sequence(_cimg_mp_size(arg1),arg1 + 1,
  16583. arg1 + (ulongT)_cimg_mp_size(arg1));
  16584. opcode.resize(1,std::min(opcode._height,4U),1,1,0).move_to(l_opcode);
  16585. is_sth = true;
  16586. } else {
  16587. _cimg_mp_check_type(arg1,pos + 1,1,0);
  16588. CImg<ulongT>::vector(arg1).move_to(l_opcode);
  16589. }
  16590. s = ns;
  16591. }
  16592. (l_opcode>'y').move_to(opcode);
  16593. arg1 = 0; arg2 = (p1!=~0U);
  16594. switch (opcode._height) {
  16595. case 0 : case 1 :
  16596. CImg<ulongT>::vector(0,0,0,0,~0U,~0U,~0U,~0U,0).move_to(opcode);
  16597. break;
  16598. case 2 :
  16599. CImg<ulongT>::vector(*opcode,0,0,0,opcode[1],~0U,~0U,~0U,_cimg_mp_boundary).move_to(opcode);
  16600. arg1 = arg2?3:2;
  16601. break;
  16602. case 3 :
  16603. CImg<ulongT>::vector(*opcode,0,0,0,opcode[1],~0U,~0U,~0U,opcode[2]).move_to(opcode);
  16604. arg1 = arg2?3:2;
  16605. break;
  16606. case 4 :
  16607. CImg<ulongT>::vector(*opcode,opcode[1],0,0,opcode[2],opcode[3],~0U,~0U,_cimg_mp_boundary).
  16608. move_to(opcode);
  16609. arg1 = (is_sth?2:1) + arg2;
  16610. break;
  16611. case 5 :
  16612. CImg<ulongT>::vector(*opcode,opcode[1],0,0,opcode[2],opcode[3],~0U,~0U,opcode[4]).
  16613. move_to(opcode);
  16614. arg1 = (is_sth?2:1) + arg2;
  16615. break;
  16616. case 6 :
  16617. CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],0,opcode[3],opcode[4],opcode[5],~0U,
  16618. _cimg_mp_boundary).move_to(opcode);
  16619. arg1 = (is_sth?2:4) + arg2;
  16620. break;
  16621. case 7 :
  16622. CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],0,opcode[3],opcode[4],opcode[5],~0U,
  16623. opcode[6]).move_to(opcode);
  16624. arg1 = (is_sth?2:4) + arg2;
  16625. break;
  16626. case 8 :
  16627. CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4],opcode[5],opcode[6],
  16628. opcode[7],_cimg_mp_boundary).move_to(opcode);
  16629. arg1 = (is_sth?2:5) + arg2;
  16630. break;
  16631. case 9 :
  16632. arg1 = (is_sth?2:5) + arg2;
  16633. break;
  16634. default : // Error -> too much arguments
  16635. *se = saved_char;
  16636. s0 = ss - 4>expr._data?ss - 4:expr._data;
  16637. cimg::strellipsize(s0,64);
  16638. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  16639. "CImg<%s>::%s: %s: Too much arguments specified, "
  16640. "in expression '%s%s%s'.",
  16641. pixel_type(),_cimg_mp_calling_function,s_op,
  16642. s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
  16643. }
  16644. _cimg_mp_check_type((unsigned int)*opcode,arg2 + 1,1,0);
  16645. _cimg_mp_check_type((unsigned int)opcode[1],arg2 + 1 + (is_sth?0:1),1,0);
  16646. _cimg_mp_check_type((unsigned int)opcode[2],arg2 + 1 + (is_sth?0:2),1,0);
  16647. _cimg_mp_check_type((unsigned int)opcode[3],arg2 + 1 + (is_sth?0:3),1,0);
  16648. if (opcode[4]!=(ulongT)~0U) {
  16649. _cimg_mp_check_constant((unsigned int)opcode[4],arg1,3);
  16650. opcode[4] = (ulongT)mem[opcode[4]];
  16651. }
  16652. if (opcode[5]!=(ulongT)~0U) {
  16653. _cimg_mp_check_constant((unsigned int)opcode[5],arg1 + 1,3);
  16654. opcode[5] = (ulongT)mem[opcode[5]];
  16655. }
  16656. if (opcode[6]!=(ulongT)~0U) {
  16657. _cimg_mp_check_constant((unsigned int)opcode[6],arg1 + 2,3);
  16658. opcode[6] = (ulongT)mem[opcode[6]];
  16659. }
  16660. if (opcode[7]!=(ulongT)~0U) {
  16661. _cimg_mp_check_constant((unsigned int)opcode[7],arg1 + 3,3);
  16662. opcode[7] = (ulongT)mem[opcode[7]];
  16663. }
  16664. _cimg_mp_check_type((unsigned int)opcode[8],arg1 + 4,1,0);
  16665. if (opcode[4]==(ulongT)~0U || opcode[5]==(ulongT)~0U ||
  16666. opcode[6]==(ulongT)~0U || opcode[7]==(ulongT)~0U) {
  16667. if (p1!=~0U) {
  16668. _cimg_mp_check_constant(p1,1,1);
  16669. p1 = (unsigned int)cimg::mod((int)mem[p1],listin.width());
  16670. }
  16671. const CImg<T> &img = p1!=~0U?listin[p1]:imgin;
  16672. if (!img) {
  16673. *se = saved_char;
  16674. s0 = ss - 4>expr._data?ss - 4:expr._data;
  16675. cimg::strellipsize(s0,64);
  16676. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  16677. "CImg<%s>::%s: %s: Cannot crop empty image when "
  16678. "some xyzc-coordinates are unspecified, in expression '%s%s%s'.",
  16679. pixel_type(),_cimg_mp_calling_function,s_op,
  16680. s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
  16681. }
  16682. if (opcode[4]==(ulongT)~0U) opcode[4] = (ulongT)img._width;
  16683. if (opcode[5]==(ulongT)~0U) opcode[5] = (ulongT)img._height;
  16684. if (opcode[6]==(ulongT)~0U) opcode[6] = (ulongT)img._depth;
  16685. if (opcode[7]==(ulongT)~0U) opcode[7] = (ulongT)img._spectrum;
  16686. }
  16687. pos = vector((unsigned int)(opcode[4]*opcode[5]*opcode[6]*opcode[7]));
  16688. CImg<ulongT>::vector((ulongT)mp_crop,
  16689. pos,p1,
  16690. *opcode,opcode[1],opcode[2],opcode[3],
  16691. opcode[4],opcode[5],opcode[6],opcode[7],
  16692. opcode[8]).move_to(code);
  16693. _cimg_mp_return(pos);
  16694. }
  16695. if (!std::strncmp(ss,"cross(",6)) { // Cross product
  16696. _cimg_mp_op("Function 'cross()'");
  16697. s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  16698. arg1 = compile(ss6,s1,depth1,0,is_single);
  16699. arg2 = compile(++s1,se1,depth1,0,is_single);
  16700. _cimg_mp_check_type(arg1,1,2,3);
  16701. _cimg_mp_check_type(arg2,2,2,3);
  16702. pos = vector(3);
  16703. CImg<ulongT>::vector((ulongT)mp_cross,pos,arg1,arg2).move_to(code);
  16704. _cimg_mp_return(pos);
  16705. }
  16706. if (!std::strncmp(ss,"cut(",4)) { // Cut
  16707. _cimg_mp_op("Function 'cut()'");
  16708. s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  16709. arg1 = compile(ss4,s1,depth1,0,is_single);
  16710. s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  16711. arg2 = compile(++s1,s2,depth1,0,is_single);
  16712. arg3 = compile(++s2,se1,depth1,0,is_single);
  16713. _cimg_mp_check_type(arg2,2,1,0);
  16714. _cimg_mp_check_type(arg3,3,1,0);
  16715. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector3_vss(mp_cut,arg1,arg2,arg3);
  16716. if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2) && _cimg_mp_is_constant(arg3)) {
  16717. val = mem[arg1];
  16718. val1 = mem[arg2];
  16719. val2 = mem[arg3];
  16720. _cimg_mp_constant(val<val1?val1:val>val2?val2:val);
  16721. }
  16722. _cimg_mp_scalar3(mp_cut,arg1,arg2,arg3);
  16723. }
  16724. break;
  16725. case 'd' :
  16726. if (*ss1=='(') { // Image depth
  16727. _cimg_mp_op("Function 'd()'");
  16728. if (*ss2=='#') { // Index specified
  16729. p1 = compile(ss3,se1,depth1,0,is_single);
  16730. _cimg_mp_check_list(false);
  16731. } else { if (ss2!=se1) break; p1 = ~0U; }
  16732. pos = scalar();
  16733. CImg<ulongT>::vector((ulongT)mp_image_d,pos,p1).move_to(code);
  16734. _cimg_mp_return(pos);
  16735. }
  16736. if (!std::strncmp(ss,"date(",5)) { // Current date or file date
  16737. _cimg_mp_op("Function 'date()'");
  16738. s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  16739. arg1 = ss5!=se1?compile(ss5,s1,depth1,0,is_single):~0U;
  16740. is_sth = s1++!=se1; // is_filename
  16741. pos = arg1==~0U || _cimg_mp_is_vector(arg1)?vector(arg1==~0U?7:_cimg_mp_size(arg1)):scalar();
  16742. if (is_sth) {
  16743. *se1 = 0;
  16744. variable_name.assign(CImg<charT>::string(s1,true,true).unroll('y'),true);
  16745. cimg::strpare(variable_name,false,true);
  16746. ((CImg<ulongT>::vector((ulongT)mp_date,pos,0,arg1,_cimg_mp_size(pos)),variable_name)>'y').
  16747. move_to(opcode);
  16748. *se1 = ')';
  16749. } else
  16750. CImg<ulongT>::vector((ulongT)mp_date,pos,0,arg1,_cimg_mp_size(pos)).move_to(opcode);
  16751. opcode[2] = opcode._height;
  16752. opcode.move_to(code);
  16753. _cimg_mp_return(pos);
  16754. }
  16755. if (!std::strncmp(ss,"debug(",6)) { // Print debug info
  16756. _cimg_mp_op("Function 'debug()'");
  16757. p1 = code._width;
  16758. arg1 = compile(ss6,se1,depth1,p_ref,is_single);
  16759. *se1 = 0;
  16760. variable_name.assign(CImg<charT>::string(ss6,true,true).unroll('y'),true);
  16761. cimg::strpare(variable_name,false,true);
  16762. ((CImg<ulongT>::vector((ulongT)mp_debug,arg1,0,code._width - p1),
  16763. variable_name)>'y').move_to(opcode);
  16764. opcode[2] = opcode._height;
  16765. opcode.move_to(code,p1);
  16766. *se1 = ')';
  16767. _cimg_mp_return(arg1);
  16768. }
  16769. if (!std::strncmp(ss,"display(",8)) { // Display memory, vector or image
  16770. _cimg_mp_op("Function 'display()'");
  16771. if (pexpr[se2 - expr._data]=='(') { // no arguments?
  16772. CImg<ulongT>::vector((ulongT)mp_display_memory,_cimg_mp_slot_nan).move_to(code);
  16773. _cimg_mp_return_nan();
  16774. }
  16775. if (*ss8!='#') { // Vector
  16776. s1 = ss8; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  16777. arg1 = compile(ss8,s1,depth1,0,is_single);
  16778. arg2 = 0; arg3 = arg4 = arg5 = 1;
  16779. if (s1<se1) {
  16780. s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  16781. arg2 = compile(s1 + 1,s2,depth1,0,is_single);
  16782. if (s2<se1) {
  16783. s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
  16784. arg3 = compile(s2,s3,depth1,0,is_single);
  16785. if (s3<se1) {
  16786. s2 = ++s3; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  16787. arg4 = compile(s3,s2,depth1,0,is_single);
  16788. arg5 = s2<se1?compile(++s2,se1,depth1,0,is_single):0;
  16789. }
  16790. }
  16791. }
  16792. _cimg_mp_check_type(arg2,2,1,0);
  16793. _cimg_mp_check_type(arg3,3,1,0);
  16794. _cimg_mp_check_type(arg4,4,1,0);
  16795. _cimg_mp_check_type(arg5,5,1,0);
  16796. c1 = *s1; *s1 = 0;
  16797. variable_name.assign(CImg<charT>::string(ss8,true,true).unroll('y'),true);
  16798. cimg::strpare(variable_name,false,true);
  16799. if (_cimg_mp_is_vector(arg1))
  16800. ((CImg<ulongT>::vector((ulongT)mp_vector_print,arg1,0,(ulongT)_cimg_mp_size(arg1),0),
  16801. variable_name)>'y').move_to(opcode);
  16802. else
  16803. ((CImg<ulongT>::vector((ulongT)mp_print,arg1,0,0),
  16804. variable_name)>'y').move_to(opcode);
  16805. opcode[2] = opcode._height;
  16806. opcode.move_to(code);
  16807. ((CImg<ulongT>::vector((ulongT)mp_display,arg1,0,(ulongT)_cimg_mp_size(arg1),
  16808. arg2,arg3,arg4,arg5),
  16809. variable_name)>'y').move_to(opcode);
  16810. opcode[2] = opcode._height;
  16811. opcode.move_to(code);
  16812. *s1 = c1;
  16813. _cimg_mp_return(arg1);
  16814. } else { // Image
  16815. p1 = compile(ss8 + 1,se1,depth1,0,is_single);
  16816. _cimg_mp_check_list(true);
  16817. CImg<ulongT>::vector((ulongT)mp_image_display,_cimg_mp_slot_nan,p1).move_to(code);
  16818. _cimg_mp_return_nan();
  16819. }
  16820. }
  16821. if (!std::strncmp(ss,"det(",4)) { // Matrix determinant
  16822. _cimg_mp_op("Function 'det()'");
  16823. arg1 = compile(ss4,se1,depth1,0,is_single);
  16824. _cimg_mp_check_matrix_square(arg1,1);
  16825. p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1)));
  16826. _cimg_mp_scalar2(mp_det,arg1,p1);
  16827. }
  16828. if (!std::strncmp(ss,"diag(",5)) { // Diagonal matrix
  16829. _cimg_mp_op("Function 'diag()'");
  16830. arg1 = compile(ss5,se1,depth1,0,is_single);
  16831. if (_cimg_mp_is_scalar(arg1)) _cimg_mp_return(arg1);
  16832. p1 = _cimg_mp_size(arg1);
  16833. pos = vector(p1*p1);
  16834. CImg<ulongT>::vector((ulongT)mp_diag,pos,arg1,p1).move_to(code);
  16835. _cimg_mp_return(pos);
  16836. }
  16837. if (!std::strncmp(ss,"dot(",4)) { // Dot product
  16838. _cimg_mp_op("Function 'dot()'");
  16839. s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  16840. arg1 = compile(ss4,s1,depth1,0,is_single);
  16841. arg2 = compile(++s1,se1,depth1,0,is_single);
  16842. _cimg_mp_check_type(arg1,1,2,0);
  16843. _cimg_mp_check_type(arg2,2,2,0);
  16844. if (_cimg_mp_is_vector(arg1)) _cimg_mp_scalar3(mp_dot,arg1,arg2,_cimg_mp_size(arg1));
  16845. _cimg_mp_scalar2(mp_mul,arg1,arg2);
  16846. }
  16847. if (!std::strncmp(ss,"do(",3) || !std::strncmp(ss,"dowhile(",8)) { // Do..while
  16848. _cimg_mp_op("Function 'dowhile()'");
  16849. s0 = *ss2=='('?ss3:ss8;
  16850. s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  16851. arg1 = code._width;
  16852. arg6 = mempos;
  16853. p1 = compile(s0,s1,depth1,0,is_single); // Body
  16854. arg2 = code._width;
  16855. p2 = s1<se1?compile(++s1,se1,depth1,0,is_single):p1; // Condition
  16856. _cimg_mp_check_type(p2,2,1,0);
  16857. CImg<ulongT>::vector((ulongT)mp_dowhile,p1,p2,arg2 - arg1,code._width - arg2,_cimg_mp_size(p1),
  16858. p1>=arg6 && !_cimg_mp_is_constant(p1),
  16859. p2>=arg6 && !_cimg_mp_is_constant(p2)).move_to(code,arg1);
  16860. _cimg_mp_return(p1);
  16861. }
  16862. if (!std::strncmp(ss,"draw(",5)) { // Draw image
  16863. if (!is_single) is_parallelizable = false;
  16864. _cimg_mp_op("Function 'draw()'");
  16865. if (*ss5=='#') { // Index specified
  16866. s0 = ss6; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  16867. p1 = compile(ss6,s0++,depth1,0,is_single);
  16868. _cimg_mp_check_list(true);
  16869. } else { p1 = ~0U; s0 = ss5; }
  16870. s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  16871. arg1 = compile(s0,s1,depth1,0,is_single);
  16872. arg2 = is_relative?0U:(unsigned int)_cimg_mp_slot_x;
  16873. arg3 = is_relative?0U:(unsigned int)_cimg_mp_slot_y;
  16874. arg4 = is_relative?0U:(unsigned int)_cimg_mp_slot_z;
  16875. arg5 = is_relative?0U:(unsigned int)_cimg_mp_slot_c;
  16876. s0 = se1;
  16877. if (s1<se1) {
  16878. s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  16879. arg2 = compile(++s1,s0,depth1,0,is_single);
  16880. if (_cimg_mp_is_vector(arg2)) { // Coordinates specified as a vector
  16881. p2 = _cimg_mp_size(arg2);
  16882. ++arg2;
  16883. if (p2>1) {
  16884. arg3 = arg2 + 1;
  16885. if (p2>2) {
  16886. arg4 = arg3 + 1;
  16887. if (p2>3) arg5 = arg4 + 1;
  16888. }
  16889. }
  16890. ++s0;
  16891. is_sth = true;
  16892. } else {
  16893. if (s0<se1) {
  16894. is_sth = p1!=~0U;
  16895. s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  16896. arg3 = compile(++s0,s1,depth1,0,is_single);
  16897. _cimg_mp_check_type(arg3,is_sth?4:3,1,0);
  16898. if (s1<se1) {
  16899. s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  16900. arg4 = compile(++s1,s0,depth1,0,is_single);
  16901. _cimg_mp_check_type(arg4,is_sth?5:4,1,0);
  16902. if (s0<se1) {
  16903. s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  16904. arg5 = compile(++s0,s1,depth1,0,is_single);
  16905. _cimg_mp_check_type(arg5,is_sth?6:5,1,0);
  16906. s0 = ++s1;
  16907. }
  16908. }
  16909. }
  16910. is_sth = false;
  16911. }
  16912. }
  16913. l_opcode.assign(); // Don't use 'opcode': it can be modified by further calls to 'compile()'!
  16914. CImg<ulongT>::vector((ulongT)mp_draw,arg1,(ulongT)_cimg_mp_size(arg1),p1,arg2,arg3,arg4,arg5,
  16915. 0,0,0,0,1,(ulongT)~0U,0,1).move_to(l_opcode);
  16916. arg2 = arg3 = arg4 = arg5 = ~0U;
  16917. p2 = p1!=~0U?0:1;
  16918. if (s0<se1) {
  16919. s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  16920. arg2 = compile(s0,s1,depth1,0,is_single);
  16921. _cimg_mp_check_type(arg2,p2 + (is_sth?3:6),1,0);
  16922. if (s1<se1) {
  16923. s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  16924. arg3 = compile(++s1,s0,depth1,0,is_single);
  16925. _cimg_mp_check_type(arg3,p2 + (is_sth?4:7),1,0);
  16926. if (s0<se1) {
  16927. s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  16928. arg4 = compile(++s0,s1,depth1,0,is_single);
  16929. _cimg_mp_check_type(arg4,p2 + (is_sth?5:8),1,0);
  16930. if (s1<se1) {
  16931. s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  16932. arg5 = compile(++s1,s0,depth1,0,is_single);
  16933. _cimg_mp_check_type(arg5,p2 + (is_sth?6:9),1,0);
  16934. }
  16935. }
  16936. }
  16937. }
  16938. if (s0<s1) s0 = s1;
  16939. l_opcode(0,8) = (ulongT)arg2;
  16940. l_opcode(0,9) = (ulongT)arg3;
  16941. l_opcode(0,10) = (ulongT)arg4;
  16942. l_opcode(0,11) = (ulongT)arg5;
  16943. if (s0<se1) {
  16944. s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  16945. arg6 = compile(++s0,s1,depth1,0,is_single);
  16946. _cimg_mp_check_type(arg6,0,1,0);
  16947. l_opcode(0,12) = arg6;
  16948. if (s1<se1) {
  16949. s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  16950. p2 = compile(++s1,s0,depth1,0,is_single);
  16951. _cimg_mp_check_type(p2,0,2,0);
  16952. l_opcode(0,13) = p2;
  16953. l_opcode(0,14) = _cimg_mp_size(p2);
  16954. p3 = s0<se1?compile(++s0,se1,depth1,0,is_single):1;
  16955. _cimg_mp_check_type(p3,0,1,0);
  16956. l_opcode(0,15) = p3;
  16957. }
  16958. }
  16959. l_opcode[0].move_to(code);
  16960. _cimg_mp_return(arg1);
  16961. }
  16962. break;
  16963. case 'e' :
  16964. if (!std::strncmp(ss,"echo(",5)) { // Echo
  16965. _cimg_mp_op("Function 'echo()'");
  16966. CImg<ulongT>::vector((ulongT)mp_echo,_cimg_mp_slot_nan,0).move_to(l_opcode);
  16967. for (s = ss5; s<se; ++s) {
  16968. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  16969. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  16970. arg1 = compile(s,ns,depth1,0,is_single);
  16971. CImg<ulongT>::vector(arg1,_cimg_mp_size(arg1)).move_to(l_opcode);
  16972. s = ns;
  16973. }
  16974. (l_opcode>'y').move_to(opcode);
  16975. opcode[2] = opcode._height;
  16976. opcode.move_to(code);
  16977. _cimg_mp_return_nan();
  16978. }
  16979. if (!std::strncmp(ss,"eig(",4)) { // Matrix eigenvalues/eigenvector
  16980. _cimg_mp_op("Function 'eig()'");
  16981. arg1 = compile(ss4,se1,depth1,0,is_single);
  16982. _cimg_mp_check_matrix_square(arg1,1);
  16983. p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1)));
  16984. pos = vector((p1 + 1)*p1);
  16985. CImg<ulongT>::vector((ulongT)mp_matrix_eig,pos,arg1,p1).move_to(code);
  16986. _cimg_mp_return(pos);
  16987. }
  16988. if (!std::strncmp(ss,"end(",4)) { // End
  16989. _cimg_mp_op("Function 'end()'");
  16990. code.swap(code_end);
  16991. compile(ss4,se1,depth1,p_ref,true);
  16992. code.swap(code_end);
  16993. _cimg_mp_return_nan();
  16994. }
  16995. if (!std::strncmp(ss,"ellipse(",8)) { // Ellipse/circle drawing
  16996. if (!is_single) is_parallelizable = false;
  16997. _cimg_mp_op("Function 'ellipse()'");
  16998. if (*ss8=='#') { // Index specified
  16999. s0 = ss + 9; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  17000. p1 = compile(ss + 9,s0++,depth1,0,is_single);
  17001. _cimg_mp_check_list(true);
  17002. } else { p1 = ~0U; s0 = ss8; }
  17003. if (s0==se1) compile(s0,se1,depth1,0,is_single); // 'missing' argument error
  17004. CImg<ulongT>::vector((ulongT)mp_ellipse,_cimg_mp_slot_nan,0,p1).move_to(l_opcode);
  17005. for (s = s0; s<se; ++s) {
  17006. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  17007. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  17008. arg2 = compile(s,ns,depth1,0,is_single);
  17009. if (_cimg_mp_is_vector(arg2))
  17010. CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,
  17011. arg2 + (ulongT)_cimg_mp_size(arg2)).
  17012. move_to(l_opcode);
  17013. else CImg<ulongT>::vector(arg2).move_to(l_opcode);
  17014. s = ns;
  17015. }
  17016. (l_opcode>'y').move_to(opcode);
  17017. opcode[2] = opcode._height;
  17018. opcode.move_to(code);
  17019. _cimg_mp_return_nan();
  17020. }
  17021. if (!std::strncmp(ss,"ext(",4)) { // Extern
  17022. _cimg_mp_op("Function 'ext()'");
  17023. if (!is_single) is_parallelizable = false;
  17024. CImg<ulongT>::vector((ulongT)mp_ext,0,0).move_to(l_opcode);
  17025. pos = 1;
  17026. for (s = ss4; s<se; ++s) {
  17027. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  17028. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  17029. arg1 = compile(s,ns,depth1,0,is_single);
  17030. CImg<ulongT>::vector(arg1,_cimg_mp_size(arg1)).move_to(l_opcode);
  17031. s = ns;
  17032. }
  17033. (l_opcode>'y').move_to(opcode);
  17034. pos = scalar();
  17035. opcode[1] = pos;
  17036. opcode[2] = opcode._height;
  17037. opcode.move_to(code);
  17038. _cimg_mp_return(pos);
  17039. }
  17040. if (!std::strncmp(ss,"exp(",4)) { // Exponential
  17041. _cimg_mp_op("Function 'exp()'");
  17042. arg1 = compile(ss4,se1,depth1,0,is_single);
  17043. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_exp,arg1);
  17044. if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::exp(mem[arg1]));
  17045. _cimg_mp_scalar1(mp_exp,arg1);
  17046. }
  17047. if (!std::strncmp(ss,"eye(",4)) { // Identity matrix
  17048. _cimg_mp_op("Function 'eye()'");
  17049. arg1 = compile(ss4,se1,depth1,0,is_single);
  17050. _cimg_mp_check_constant(arg1,1,3);
  17051. p1 = (unsigned int)mem[arg1];
  17052. pos = vector(p1*p1);
  17053. CImg<ulongT>::vector((ulongT)mp_eye,pos,p1).move_to(code);
  17054. _cimg_mp_return(pos);
  17055. }
  17056. break;
  17057. case 'f' :
  17058. if (!std::strncmp(ss,"fact(",5)) { // Factorial
  17059. _cimg_mp_op("Function 'fact()'");
  17060. arg1 = compile(ss5,se1,depth1,0,is_single);
  17061. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_factorial,arg1);
  17062. if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::factorial((int)mem[arg1]));
  17063. _cimg_mp_scalar1(mp_factorial,arg1);
  17064. }
  17065. if (!std::strncmp(ss,"fibo(",5)) { // Fibonacci
  17066. _cimg_mp_op("Function 'fibo()'");
  17067. arg1 = compile(ss5,se1,depth1,0,is_single);
  17068. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_fibonacci,arg1);
  17069. if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::fibonacci((int)mem[arg1]));
  17070. _cimg_mp_scalar1(mp_fibonacci,arg1);
  17071. }
  17072. if (!std::strncmp(ss,"find(",5)) { // Find
  17073. _cimg_mp_op("Function 'find()'");
  17074. // First argument: data to look at.
  17075. s0 = ss5; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  17076. if (*ss5=='#') { // Index specified
  17077. p1 = compile(ss6,s0,depth1,0,is_single);
  17078. _cimg_mp_check_list(false);
  17079. arg1 = ~0U;
  17080. } else { // Vector specified
  17081. arg1 = compile(ss5,s0,depth1,0,is_single);
  17082. _cimg_mp_check_type(arg1,1,2,0);
  17083. p1 = ~0U;
  17084. }
  17085. // Second argument: data to find.
  17086. s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  17087. arg2 = compile(s0,s1,depth1,0,is_single);
  17088. // Third and fourth arguments: search direction and starting index.
  17089. arg3 = 1; arg4 = _cimg_mp_slot_nan;
  17090. if (s1<se1) {
  17091. s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  17092. arg3 = compile(++s1,s0,depth1,0,is_single);
  17093. _cimg_mp_check_type(arg3,3,1,0);
  17094. if (s0<se1) {
  17095. arg4 = compile(++s0,se1,depth1,0,is_single);
  17096. _cimg_mp_check_type(arg4,4,1,0);
  17097. }
  17098. }
  17099. if (p1!=~0U) {
  17100. if (_cimg_mp_is_vector(arg2))
  17101. _cimg_mp_scalar5(mp_list_find_seq,p1,arg2,_cimg_mp_size(arg2),arg3,arg4);
  17102. _cimg_mp_scalar4(mp_list_find,p1,arg2,arg3,arg4);
  17103. }
  17104. if (_cimg_mp_is_vector(arg2))
  17105. _cimg_mp_scalar6(mp_find_seq,arg1,_cimg_mp_size(arg1),arg2,_cimg_mp_size(arg2),arg3,arg4);
  17106. _cimg_mp_scalar5(mp_find,arg1,_cimg_mp_size(arg1),arg2,arg3,arg4);
  17107. }
  17108. if (*ss1=='o' && *ss2=='r' && *ss3=='(') { // For loop
  17109. _cimg_mp_op("Function 'for()'");
  17110. s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  17111. s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  17112. s3 = s2 + 1; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
  17113. arg1 = code._width;
  17114. p1 = compile(ss4,s1,depth1,0,is_single); // Init
  17115. arg2 = code._width;
  17116. p2 = compile(++s1,s2,depth1,0,is_single); // Cond
  17117. arg3 = code._width;
  17118. arg6 = mempos;
  17119. if (s3<se1) { // Body + post
  17120. p3 = compile(s3 + 1,se1,depth1,0,is_single); // Body
  17121. arg4 = code._width;
  17122. pos = compile(++s2,s3,depth1,0,is_single); // Post
  17123. } else {
  17124. p3 = compile(++s2,se1,depth1,0,is_single); // Body only
  17125. arg4 = pos = code._width;
  17126. }
  17127. _cimg_mp_check_type(p2,2,1,0);
  17128. arg5 = _cimg_mp_size(pos);
  17129. CImg<ulongT>::vector((ulongT)mp_for,p3,(ulongT)_cimg_mp_size(p3),p2,arg2 - arg1,arg3 - arg2,
  17130. arg4 - arg3,code._width - arg4,
  17131. p3>=arg6 && !_cimg_mp_is_constant(p3),
  17132. p2>=arg6 && !_cimg_mp_is_constant(p2)).move_to(code,arg1);
  17133. _cimg_mp_return(p3);
  17134. }
  17135. if (!std::strncmp(ss,"floor(",6)) { // Floor
  17136. _cimg_mp_op("Function 'floor()'");
  17137. arg1 = compile(ss6,se1,depth1,0,is_single);
  17138. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_floor,arg1);
  17139. if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::floor(mem[arg1]));
  17140. _cimg_mp_scalar1(mp_floor,arg1);
  17141. }
  17142. if (!std::strncmp(ss,"fsize(",6)) { // File size
  17143. _cimg_mp_op("Function 'fsize()'");
  17144. *se1 = 0;
  17145. variable_name.assign(CImg<charT>::string(ss6,true,true).unroll('y'),true);
  17146. cimg::strpare(variable_name,false,true);
  17147. pos = scalar();
  17148. ((CImg<ulongT>::vector((ulongT)mp_fsize,pos,0),variable_name)>'y').move_to(opcode);
  17149. *se1 = ')';
  17150. opcode[2] = opcode._height;
  17151. opcode.move_to(code);
  17152. _cimg_mp_return(pos);
  17153. }
  17154. break;
  17155. case 'g' :
  17156. if (!std::strncmp(ss,"gauss(",6)) { // Gaussian function
  17157. _cimg_mp_op("Function 'gauss()'");
  17158. s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  17159. arg1 = compile(ss6,s1,depth1,0,is_single);
  17160. arg2 = arg3 = 1;
  17161. if (s1<se1) {
  17162. s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  17163. arg2 = compile(++s1,s2,depth1,0,is_single);
  17164. arg3 = s2<se1?compile(++s2,se1,depth1,0,is_single):1;
  17165. }
  17166. _cimg_mp_check_type(arg2,2,1,0);
  17167. _cimg_mp_check_type(arg3,3,1,0);
  17168. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector3_vss(mp_gauss,arg1,arg2,arg3);
  17169. if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2) && _cimg_mp_is_constant(arg3)) {
  17170. val1 = mem[arg1];
  17171. val2 = mem[arg2];
  17172. _cimg_mp_constant(std::exp(-val1*val1/(2*val2*val2))/(mem[arg3]?std::sqrt(2*val2*val2*cimg::PI):1));
  17173. }
  17174. _cimg_mp_scalar3(mp_gauss,arg1,arg2,arg3);
  17175. }
  17176. if (!std::strncmp(ss,"gcd(",4)) { // Gcd
  17177. _cimg_mp_op("Function 'gcd()'");
  17178. s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  17179. arg1 = compile(ss4,s1,depth1,0,is_single);
  17180. arg2 = compile(++s1,se1,depth1,0,is_single);
  17181. _cimg_mp_check_type(arg1,1,1,0);
  17182. _cimg_mp_check_type(arg2,2,1,0);
  17183. if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
  17184. _cimg_mp_constant(cimg::gcd((long)mem[arg1],(long)mem[arg2]));
  17185. _cimg_mp_scalar2(mp_gcd,arg1,arg2);
  17186. }
  17187. break;
  17188. case 'h' :
  17189. if (*ss1=='(') { // Image height
  17190. _cimg_mp_op("Function 'h()'");
  17191. if (*ss2=='#') { // Index specified
  17192. p1 = compile(ss3,se1,depth1,0,is_single);
  17193. _cimg_mp_check_list(false);
  17194. } else { if (ss2!=se1) break; p1 = ~0U; }
  17195. pos = scalar();
  17196. CImg<ulongT>::vector((ulongT)mp_image_h,pos,p1).move_to(code);
  17197. _cimg_mp_return(pos);
  17198. }
  17199. break;
  17200. case 'i' :
  17201. if (*ss1=='c' && *ss2=='(') { // Image median
  17202. _cimg_mp_op("Function 'ic()'");
  17203. if (*ss3=='#') { // Index specified
  17204. p1 = compile(ss4,se1,depth1,0,is_single);
  17205. _cimg_mp_check_list(false);
  17206. } else { if (ss3!=se1) break; p1 = ~0U; }
  17207. pos = scalar();
  17208. CImg<ulongT>::vector((ulongT)mp_image_median,pos,p1).move_to(code);
  17209. _cimg_mp_return(pos);
  17210. }
  17211. if (*ss1=='f' && *ss2=='(') { // If..then[..else.]
  17212. _cimg_mp_op("Function 'if()'");
  17213. s1 = ss3; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  17214. s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  17215. arg1 = compile(ss3,s1,depth1,0,is_single);
  17216. _cimg_mp_check_type(arg1,1,1,0);
  17217. if (_cimg_mp_is_constant(arg1)) {
  17218. if ((bool)mem[arg1]) return compile(++s1,s2,depth1,0,is_single);
  17219. else return s2<se1?compile(++s2,se1,depth1,0,is_single):0;
  17220. }
  17221. p2 = code._width;
  17222. arg2 = compile(++s1,s2,depth1,0,is_single);
  17223. p3 = code._width;
  17224. arg3 = s2<se1?compile(++s2,se1,depth1,0,is_single):
  17225. _cimg_mp_is_vector(arg2)?vector(_cimg_mp_size(arg2),0):0;
  17226. _cimg_mp_check_type(arg3,3,_cimg_mp_is_vector(arg2)?2:1,_cimg_mp_size(arg2));
  17227. arg4 = _cimg_mp_size(arg2);
  17228. if (arg4) pos = vector(arg4); else pos = scalar();
  17229. CImg<ulongT>::vector((ulongT)mp_if,pos,arg1,arg2,arg3,
  17230. p3 - p2,code._width - p3,arg4).move_to(code,p2);
  17231. _cimg_mp_return(pos);
  17232. }
  17233. if (!std::strncmp(ss,"init(",5)) { // Init
  17234. _cimg_mp_op("Function 'init()'");
  17235. code.swap(code_init);
  17236. arg1 = compile(ss5,se1,depth1,p_ref,true);
  17237. code.swap(code_init);
  17238. _cimg_mp_return(arg1);
  17239. }
  17240. if (!std::strncmp(ss,"int(",4)) { // Integer cast
  17241. _cimg_mp_op("Function 'int()'");
  17242. arg1 = compile(ss4,se1,depth1,0,is_single);
  17243. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_int,arg1);
  17244. if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((longT)mem[arg1]);
  17245. _cimg_mp_scalar1(mp_int,arg1);
  17246. }
  17247. if (!std::strncmp(ss,"inv(",4)) { // Matrix/scalar inversion
  17248. _cimg_mp_op("Function 'inv()'");
  17249. arg1 = compile(ss4,se1,depth1,0,is_single);
  17250. if (_cimg_mp_is_vector(arg1)) {
  17251. _cimg_mp_check_matrix_square(arg1,1);
  17252. p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1)));
  17253. pos = vector(p1*p1);
  17254. CImg<ulongT>::vector((ulongT)mp_matrix_inv,pos,arg1,p1).move_to(code);
  17255. _cimg_mp_return(pos);
  17256. }
  17257. if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(1/mem[arg1]);
  17258. _cimg_mp_scalar2(mp_div,1,arg1);
  17259. }
  17260. if (*ss1=='s') { // Family of 'is_?()' functions
  17261. if (!std::strncmp(ss,"isbool(",7)) { // Is boolean?
  17262. _cimg_mp_op("Function 'isbool()'");
  17263. if (ss7==se1) _cimg_mp_return(0);
  17264. arg1 = compile(ss7,se1,depth1,0,is_single);
  17265. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isbool,arg1);
  17266. if (_cimg_mp_is_constant(arg1)) _cimg_mp_return(mem[arg1]==0.0 || mem[arg1]==1.0);
  17267. _cimg_mp_scalar1(mp_isbool,arg1);
  17268. }
  17269. if (!std::strncmp(ss,"isdir(",6)) { // Is directory?
  17270. _cimg_mp_op("Function 'isdir()'");
  17271. *se1 = 0;
  17272. is_sth = cimg::is_directory(ss6);
  17273. *se1 = ')';
  17274. _cimg_mp_return(is_sth?1U:0U);
  17275. }
  17276. if (!std::strncmp(ss,"isfile(",7)) { // Is file?
  17277. _cimg_mp_op("Function 'isfile()'");
  17278. *se1 = 0;
  17279. is_sth = cimg::is_file(ss7);
  17280. *se1 = ')';
  17281. _cimg_mp_return(is_sth?1U:0U);
  17282. }
  17283. if (!std::strncmp(ss,"isin(",5)) { // Is in sequence/vector?
  17284. if (ss5>=se1) _cimg_mp_return(0);
  17285. _cimg_mp_op("Function 'isin()'");
  17286. pos = scalar();
  17287. CImg<ulongT>::vector((ulongT)mp_isin,pos,0).move_to(l_opcode);
  17288. for (s = ss5; s<se; ++s) {
  17289. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  17290. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  17291. arg1 = compile(s,ns,depth1,0,is_single);
  17292. if (_cimg_mp_is_vector(arg1))
  17293. CImg<ulongT>::sequence(_cimg_mp_size(arg1),arg1 + 1,
  17294. arg1 + (ulongT)_cimg_mp_size(arg1)).
  17295. move_to(l_opcode);
  17296. else CImg<ulongT>::vector(arg1).move_to(l_opcode);
  17297. s = ns;
  17298. }
  17299. (l_opcode>'y').move_to(opcode);
  17300. opcode[2] = opcode._height;
  17301. opcode.move_to(code);
  17302. _cimg_mp_return(pos);
  17303. }
  17304. if (!std::strncmp(ss,"isinf(",6)) { // Is infinite?
  17305. _cimg_mp_op("Function 'isinf()'");
  17306. if (ss6==se1) _cimg_mp_return(0);
  17307. arg1 = compile(ss6,se1,depth1,0,is_single);
  17308. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isinf,arg1);
  17309. if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)cimg::type<double>::is_inf(mem[arg1]));
  17310. _cimg_mp_scalar1(mp_isinf,arg1);
  17311. }
  17312. if (!std::strncmp(ss,"isint(",6)) { // Is integer?
  17313. _cimg_mp_op("Function 'isint()'");
  17314. if (ss6==se1) _cimg_mp_return(0);
  17315. arg1 = compile(ss6,se1,depth1,0,is_single);
  17316. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isint,arg1);
  17317. if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)(cimg::mod(mem[arg1],1.0)==0));
  17318. _cimg_mp_scalar1(mp_isint,arg1);
  17319. }
  17320. if (!std::strncmp(ss,"isnan(",6)) { // Is NaN?
  17321. _cimg_mp_op("Function 'isnan()'");
  17322. if (ss6==se1) _cimg_mp_return(0);
  17323. arg1 = compile(ss6,se1,depth1,0,is_single);
  17324. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isnan,arg1);
  17325. if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)cimg::type<double>::is_nan(mem[arg1]));
  17326. _cimg_mp_scalar1(mp_isnan,arg1);
  17327. }
  17328. if (!std::strncmp(ss,"isval(",6)) { // Is value?
  17329. _cimg_mp_op("Function 'isval()'");
  17330. val = 0;
  17331. if (cimg_sscanf(ss6,"%lf%c%c",&val,&sep,&end)==2 && sep==')') _cimg_mp_return(1);
  17332. _cimg_mp_return(0);
  17333. }
  17334. }
  17335. break;
  17336. case 'l' :
  17337. if (*ss1=='(') { // Size of image list
  17338. _cimg_mp_op("Function 'l()'");
  17339. if (ss2!=se1) break;
  17340. _cimg_mp_scalar0(mp_list_l);
  17341. }
  17342. if (!std::strncmp(ss,"log(",4)) { // Natural logarithm
  17343. _cimg_mp_op("Function 'log()'");
  17344. arg1 = compile(ss4,se1,depth1,0,is_single);
  17345. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log,arg1);
  17346. if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::log(mem[arg1]));
  17347. _cimg_mp_scalar1(mp_log,arg1);
  17348. }
  17349. if (!std::strncmp(ss,"log2(",5)) { // Base-2 logarithm
  17350. _cimg_mp_op("Function 'log2()'");
  17351. arg1 = compile(ss5,se1,depth1,0,is_single);
  17352. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log2,arg1);
  17353. if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::log2(mem[arg1]));
  17354. _cimg_mp_scalar1(mp_log2,arg1);
  17355. }
  17356. if (!std::strncmp(ss,"log10(",6)) { // Base-10 logarithm
  17357. _cimg_mp_op("Function 'log10()'");
  17358. arg1 = compile(ss6,se1,depth1,0,is_single);
  17359. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log10,arg1);
  17360. if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::log10(mem[arg1]));
  17361. _cimg_mp_scalar1(mp_log10,arg1);
  17362. }
  17363. if (!std::strncmp(ss,"lowercase(",10)) { // Lower case
  17364. _cimg_mp_op("Function 'lowercase()'");
  17365. arg1 = compile(ss + 10,se1,depth1,0,is_single);
  17366. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_lowercase,arg1);
  17367. if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::lowercase(mem[arg1]));
  17368. _cimg_mp_scalar1(mp_lowercase,arg1);
  17369. }
  17370. break;
  17371. case 'm' :
  17372. if (!std::strncmp(ss,"mul(",4)) { // Matrix multiplication
  17373. _cimg_mp_op("Function 'mul()'");
  17374. s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  17375. arg1 = compile(ss4,s1,depth1,0,is_single);
  17376. s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  17377. arg2 = compile(++s1,s2,depth1,0,is_single);
  17378. arg3 = s2<se1?compile(++s2,se1,depth1,0,is_single):1;
  17379. _cimg_mp_check_type(arg1,1,2,0);
  17380. _cimg_mp_check_type(arg2,2,2,0);
  17381. _cimg_mp_check_constant(arg3,3,3);
  17382. p1 = _cimg_mp_size(arg1);
  17383. p2 = _cimg_mp_size(arg2);
  17384. p3 = (unsigned int)mem[arg3];
  17385. arg5 = p2/p3;
  17386. arg4 = p1/arg5;
  17387. if (arg4*arg5!=p1 || arg5*p3!=p2) {
  17388. *se = saved_char;
  17389. s0 = ss - 4>expr._data?ss - 4:expr._data;
  17390. cimg::strellipsize(s0,64);
  17391. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  17392. "CImg<%s>::%s: %s: Types of first and second arguments ('%s' and '%s') "
  17393. "do not match with third argument 'nb_colsB=%u', "
  17394. "in expression '%s%s%s'.",
  17395. pixel_type(),_cimg_mp_calling_function,s_op,
  17396. s_type(arg1)._data,s_type(arg2)._data,p3,
  17397. s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
  17398. }
  17399. pos = vector(arg4*p3);
  17400. CImg<ulongT>::vector((ulongT)mp_matrix_mul,pos,arg1,arg2,arg4,arg5,p3).move_to(code);
  17401. _cimg_mp_return(pos);
  17402. }
  17403. break;
  17404. case 'n' :
  17405. if (!std::strncmp(ss,"narg(",5)) { // Number of arguments
  17406. _cimg_mp_op("Function 'narg()'");
  17407. if (ss5>=se1) _cimg_mp_return(0);
  17408. arg1 = 0;
  17409. for (s = ss5; s<se; ++s) {
  17410. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  17411. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  17412. ++arg1; s = ns;
  17413. }
  17414. _cimg_mp_constant(arg1);
  17415. }
  17416. if ((cimg_sscanf(ss,"norm%u%c",&(arg1=~0U),&sep)==2 && sep=='(') ||
  17417. !std::strncmp(ss,"norminf(",8) || !std::strncmp(ss,"norm(",5) ||
  17418. (!std::strncmp(ss,"norm",4) && ss5<se1 && (s=std::strchr(ss5,'('))!=0)) { // Lp norm
  17419. _cimg_mp_op("Function 'normP()'");
  17420. if (*ss4=='(') { arg1 = 2; s = ss5; }
  17421. else if (*ss4=='i' && *ss5=='n' && *ss6=='f' && *ss7=='(') { arg1 = ~0U; s = ss8; }
  17422. else if (arg1==~0U) {
  17423. arg1 = compile(ss4,s++,depth1,0,is_single);
  17424. _cimg_mp_check_constant(arg1,0,2);
  17425. arg1 = (unsigned int)mem[arg1];
  17426. } else s = std::strchr(ss4,'(') + 1;
  17427. pos = scalar();
  17428. switch (arg1) {
  17429. case 0 :
  17430. CImg<ulongT>::vector((ulongT)mp_norm0,pos,0).move_to(l_opcode); break;
  17431. case 1 :
  17432. CImg<ulongT>::vector((ulongT)mp_norm1,pos,0).move_to(l_opcode); break;
  17433. case 2 :
  17434. CImg<ulongT>::vector((ulongT)mp_norm2,pos,0).move_to(l_opcode); break;
  17435. case ~0U :
  17436. CImg<ulongT>::vector((ulongT)mp_norminf,pos,0).move_to(l_opcode); break;
  17437. default :
  17438. CImg<ulongT>::vector((ulongT)mp_normp,pos,0,(ulongT)(arg1==~0U?-1:(int)arg1)).
  17439. move_to(l_opcode);
  17440. }
  17441. for ( ; s<se; ++s) {
  17442. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  17443. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  17444. arg2 = compile(s,ns,depth1,0,is_single);
  17445. if (_cimg_mp_is_vector(arg2))
  17446. CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,
  17447. arg2 + (ulongT)_cimg_mp_size(arg2)).
  17448. move_to(l_opcode);
  17449. else CImg<ulongT>::vector(arg2).move_to(l_opcode);
  17450. s = ns;
  17451. }
  17452. (l_opcode>'y').move_to(opcode);
  17453. if (arg1>0 && opcode._height==4) // Special case with one argument and p>=1
  17454. _cimg_mp_scalar1(mp_abs,opcode[3]);
  17455. opcode[2] = opcode._height;
  17456. opcode.move_to(code);
  17457. _cimg_mp_return(pos);
  17458. }
  17459. break;
  17460. case 'p' :
  17461. if (!std::strncmp(ss,"permut(",7)) { // Number of permutations
  17462. _cimg_mp_op("Function 'permut()'");
  17463. s1 = ss7; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  17464. s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  17465. arg1 = compile(ss7,s1,depth1,0,is_single);
  17466. arg2 = compile(++s1,s2,depth1,0,is_single);
  17467. arg3 = compile(++s2,se1,depth1,0,is_single);
  17468. _cimg_mp_check_type(arg1,1,1,0);
  17469. _cimg_mp_check_type(arg2,2,1,0);
  17470. _cimg_mp_check_type(arg3,3,1,0);
  17471. if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2) && _cimg_mp_is_constant(arg3))
  17472. _cimg_mp_constant(cimg::permutations((int)mem[arg1],(int)mem[arg2],(bool)mem[arg3]));
  17473. _cimg_mp_scalar3(mp_permutations,arg1,arg2,arg3);
  17474. }
  17475. if (!std::strncmp(ss,"polygon(",8)) { // Polygon/line drawing
  17476. if (!is_single) is_parallelizable = false;
  17477. _cimg_mp_op("Function 'polygon()'");
  17478. if (*ss8=='#') { // Index specified
  17479. s0 = ss + 9; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  17480. p1 = compile(ss + 9,s0++,depth1,0,is_single);
  17481. _cimg_mp_check_list(true);
  17482. } else { p1 = ~0U; s0 = ss8; }
  17483. if (s0==se1) compile(s0,se1,depth1,0,is_single); // 'missing' argument error
  17484. CImg<ulongT>::vector((ulongT)mp_polygon,_cimg_mp_slot_nan,0,p1).move_to(l_opcode);
  17485. for (s = s0; s<se; ++s) {
  17486. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  17487. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  17488. arg2 = compile(s,ns,depth1,0,is_single);
  17489. if (_cimg_mp_is_vector(arg2))
  17490. CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,
  17491. arg2 + (ulongT)_cimg_mp_size(arg2)).
  17492. move_to(l_opcode);
  17493. else CImg<ulongT>::vector(arg2).move_to(l_opcode);
  17494. s = ns;
  17495. }
  17496. (l_opcode>'y').move_to(opcode);
  17497. opcode[2] = opcode._height;
  17498. opcode.move_to(code);
  17499. _cimg_mp_return_nan();
  17500. }
  17501. if (!std::strncmp(ss,"print(",6) || !std::strncmp(ss,"prints(",7)) { // Print expressions
  17502. is_sth = ss[5]=='s'; // is prints()
  17503. _cimg_mp_op(is_sth?"Function 'prints()'":"Function 'print()'");
  17504. s0 = is_sth?ss7:ss6;
  17505. if (*s0!='#' || is_sth) { // Regular expression
  17506. for (s = s0; s<se; ++s) {
  17507. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  17508. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  17509. pos = compile(s,ns,depth1,p_ref,is_single);
  17510. c1 = *ns; *ns = 0;
  17511. variable_name.assign(CImg<charT>::string(s,true,true).unroll('y'),true);
  17512. cimg::strpare(variable_name,false,true);
  17513. if (_cimg_mp_is_vector(pos)) // Vector
  17514. ((CImg<ulongT>::vector((ulongT)mp_vector_print,pos,0,(ulongT)_cimg_mp_size(pos),is_sth?1:0),
  17515. variable_name)>'y').move_to(opcode);
  17516. else // Scalar
  17517. ((CImg<ulongT>::vector((ulongT)mp_print,pos,0,is_sth?1:0),
  17518. variable_name)>'y').move_to(opcode);
  17519. opcode[2] = opcode._height;
  17520. opcode.move_to(code);
  17521. *ns = c1; s = ns;
  17522. }
  17523. _cimg_mp_return(pos);
  17524. } else { // Image
  17525. p1 = compile(ss7,se1,depth1,0,is_single);
  17526. _cimg_mp_check_list(true);
  17527. CImg<ulongT>::vector((ulongT)mp_image_print,_cimg_mp_slot_nan,p1).move_to(code);
  17528. _cimg_mp_return_nan();
  17529. }
  17530. }
  17531. if (!std::strncmp(ss,"pseudoinv(",10)) { // Matrix/scalar pseudo-inversion
  17532. _cimg_mp_op("Function 'pseudoinv()'");
  17533. s1 = ss + 10; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  17534. arg1 = compile(ss + 10,s1,depth1,0,is_single);
  17535. arg2 = s1<se1?compile(++s1,se1,depth1,0,is_single):1;
  17536. _cimg_mp_check_type(arg1,1,2,0);
  17537. _cimg_mp_check_constant(arg2,2,3);
  17538. p1 = _cimg_mp_size(arg1);
  17539. p2 = (unsigned int)mem[arg2];
  17540. p3 = p1/p2;
  17541. if (p3*p2!=p1) {
  17542. *se = saved_char;
  17543. s0 = ss - 4>expr._data?ss - 4:expr._data;
  17544. cimg::strellipsize(s0,64);
  17545. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  17546. "CImg<%s>::%s: %s: Type of first argument ('%s') "
  17547. "does not match with second argument 'nb_colsA=%u', "
  17548. "in expression '%s%s%s'.",
  17549. pixel_type(),_cimg_mp_calling_function,s_op,
  17550. s_type(arg1)._data,p2,
  17551. s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
  17552. }
  17553. pos = vector(p1);
  17554. CImg<ulongT>::vector((ulongT)mp_matrix_pseudoinv,pos,arg1,p2,p3).move_to(code);
  17555. _cimg_mp_return(pos);
  17556. }
  17557. break;
  17558. case 'r' :
  17559. if (!std::strncmp(ss,"resize(",7)) { // Vector or image resize
  17560. _cimg_mp_op("Function 'resize()'");
  17561. if (*ss7!='#') { // Vector
  17562. s1 = ss7; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  17563. arg1 = compile(ss7,s1,depth1,0,is_single);
  17564. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  17565. arg2 = compile(s1,s2,depth1,0,is_single);
  17566. arg3 = 1;
  17567. arg4 = 0;
  17568. if (s2<se1) {
  17569. s1 = ++s2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  17570. arg3 = compile(s2,s1,depth1,0,is_single);
  17571. arg4 = s1<se1?compile(++s1,se1,depth1,0,is_single):0;
  17572. }
  17573. _cimg_mp_check_constant(arg2,2,3);
  17574. arg2 = (unsigned int)mem[arg2];
  17575. _cimg_mp_check_type(arg3,3,1,0);
  17576. _cimg_mp_check_type(arg4,4,1,0);
  17577. pos = vector(arg2);
  17578. CImg<ulongT>::vector((ulongT)mp_vector_resize,pos,arg2,arg1,(ulongT)_cimg_mp_size(arg1),
  17579. arg3,arg4).move_to(code);
  17580. _cimg_mp_return(pos);
  17581. } else { // Image
  17582. if (!is_single) is_parallelizable = false;
  17583. s0 = ss8; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  17584. p1 = compile(ss8,s0++,depth1,0,is_single);
  17585. _cimg_mp_check_list(true);
  17586. l_opcode.assign(); // Don't use 'opcode': it can be modified by further calls to 'compile()'!
  17587. CImg<ulongT>::vector((ulongT)mp_image_resize,_cimg_mp_slot_nan,p1,~0U,~0U,~0U,~0U,1,0,0,0,0,0).
  17588. move_to(l_opcode);
  17589. pos = 0;
  17590. for (s = s0; s<se && pos<10; ++s) {
  17591. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  17592. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  17593. arg1 = compile(s,ns,depth1,0,is_single);
  17594. _cimg_mp_check_type(arg1,pos + 2,1,0);
  17595. l_opcode(0,pos + 3) = arg1;
  17596. s = ns;
  17597. ++pos;
  17598. }
  17599. if (pos<1 || pos>10) {
  17600. *se = saved_char;
  17601. s0 = ss - 4>expr._data?ss - 4:expr._data;
  17602. cimg::strellipsize(s0,64);
  17603. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  17604. "CImg<%s>::%s: %s: %s arguments, in expression '%s%s%s'.",
  17605. pixel_type(),_cimg_mp_calling_function,s_op,
  17606. pos<1?"Missing":"Too much",
  17607. s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
  17608. }
  17609. l_opcode[0].move_to(code);
  17610. _cimg_mp_return_nan();
  17611. }
  17612. }
  17613. if (!std::strncmp(ss,"reverse(",8)) { // Vector reverse
  17614. _cimg_mp_op("Function 'reverse()'");
  17615. arg1 = compile(ss8,se1,depth1,0,is_single);
  17616. if (!_cimg_mp_is_vector(arg1)) _cimg_mp_return(arg1);
  17617. p1 = _cimg_mp_size(arg1);
  17618. pos = vector(p1);
  17619. CImg<ulongT>::vector((ulongT)mp_vector_reverse,pos,arg1,p1).move_to(code);
  17620. _cimg_mp_return(pos);
  17621. }
  17622. if (!std::strncmp(ss,"rol(",4) || !std::strncmp(ss,"ror(",4)) { // Bitwise rotation
  17623. _cimg_mp_op(ss[2]=='l'?"Function 'rol()'":"Function 'ror()'");
  17624. s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
  17625. arg1 = compile(ss4,s1,depth1,0,is_single);
  17626. arg2 = s1<se1?compile(++s1,se1,depth1,0,is_single):1;
  17627. _cimg_mp_check_type(arg2,2,1,0);
  17628. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector2_vs(*ss2=='l'?mp_rol:mp_ror,arg1,arg2);
  17629. if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
  17630. _cimg_mp_constant(*ss2=='l'?cimg::rol(mem[arg1],(unsigned int)mem[arg2]):
  17631. cimg::ror(mem[arg1],(unsigned int)mem[arg2]));
  17632. _cimg_mp_scalar2(*ss2=='l'?mp_rol:mp_ror,arg1,arg2);
  17633. }
  17634. if (!std::strncmp(ss,"rot(",4)) { // 2d/3d rotation matrix
  17635. _cimg_mp_op("Function 'rot()'");
  17636. s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  17637. arg1 = compile(ss4,s1,depth1,0,is_single);
  17638. if (s1<se1) { // 3d rotation
  17639. _cimg_mp_check_type(arg1,1,3,3);
  17640. is_sth = false; // Is coordinates as vector?
  17641. if (_cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
  17642. is_sth = true;
  17643. p2 = _cimg_mp_size(arg1);
  17644. ++arg1;
  17645. arg2 = arg3 = 0;
  17646. if (p2>1) {
  17647. arg2 = arg1 + 1;
  17648. if (p2>2) arg3 = arg2 + 1;
  17649. }
  17650. arg4 = compile(++s1,se1,depth1,0,is_single);
  17651. } else {
  17652. s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  17653. arg2 = compile(++s1,s2,depth1,0,is_single);
  17654. s3 = s2 + 1; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
  17655. arg3 = compile(++s2,s3,depth1,0,is_single);
  17656. arg4 = compile(++s3,se1,depth1,0,is_single);
  17657. _cimg_mp_check_type(arg2,2,1,0);
  17658. _cimg_mp_check_type(arg3,3,1,0);
  17659. }
  17660. _cimg_mp_check_type(arg4,is_sth?2:4,1,0);
  17661. pos = vector(9);
  17662. CImg<ulongT>::vector((ulongT)mp_rot3d,pos,arg1,arg2,arg3,arg4).move_to(code);
  17663. } else { // 2d rotation
  17664. _cimg_mp_check_type(arg1,1,1,0);
  17665. pos = vector(4);
  17666. CImg<ulongT>::vector((ulongT)mp_rot2d,pos,arg1).move_to(code);
  17667. }
  17668. _cimg_mp_return(pos);
  17669. }
  17670. if (!std::strncmp(ss,"round(",6)) { // Value rounding
  17671. _cimg_mp_op("Function 'round()'");
  17672. s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  17673. arg1 = compile(ss6,s1,depth1,0,is_single);
  17674. arg2 = 1;
  17675. arg3 = 0;
  17676. if (s1<se1) {
  17677. s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  17678. arg2 = compile(++s1,s2,depth1,0,is_single);
  17679. arg3 = s2<se1?compile(++s2,se1,depth1,0,is_single):0;
  17680. }
  17681. _cimg_mp_check_type(arg2,2,1,0);
  17682. _cimg_mp_check_type(arg3,3,1,0);
  17683. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector3_vss(mp_round,arg1,arg2,arg3);
  17684. if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2) && _cimg_mp_is_constant(arg3))
  17685. _cimg_mp_constant(cimg::round(mem[arg1],mem[arg2],(int)mem[arg3]));
  17686. _cimg_mp_scalar3(mp_round,arg1,arg2,arg3);
  17687. }
  17688. break;
  17689. case 's' :
  17690. if (*ss1=='(') { // Image spectrum
  17691. _cimg_mp_op("Function 's()'");
  17692. if (*ss2=='#') { // Index specified
  17693. p1 = compile(ss3,se1,depth1,0,is_single);
  17694. _cimg_mp_check_list(false);
  17695. } else { if (ss2!=se1) break; p1 = ~0U; }
  17696. pos = scalar();
  17697. CImg<ulongT>::vector((ulongT)mp_image_s,pos,p1).move_to(code);
  17698. _cimg_mp_return(pos);
  17699. }
  17700. if (!std::strncmp(ss,"same(",5)) { // Test if operands have the same values
  17701. _cimg_mp_op("Function 'same()'");
  17702. s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  17703. arg1 = compile(ss5,s1,depth1,0,is_single);
  17704. s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  17705. arg2 = compile(++s1,s2,depth1,0,is_single);
  17706. arg3 = 11;
  17707. arg4 = 1;
  17708. if (s2<se1) {
  17709. s3 = s2 + 1; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
  17710. arg3 = compile(++s2,s3,depth1,0,is_single);
  17711. _cimg_mp_check_type(arg3,3,1,0);
  17712. arg4 = s3<se1?compile(++s3,se1,depth1,0,is_single):1;
  17713. }
  17714. p1 = _cimg_mp_size(arg1);
  17715. p2 = _cimg_mp_size(arg2);
  17716. _cimg_mp_scalar6(mp_vector_eq,arg1,p1,arg2,p2,arg3,arg4);
  17717. }
  17718. if (!std::strncmp(ss,"shift(",6)) { // Shift vector
  17719. _cimg_mp_op("Function 'shift()'");
  17720. s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  17721. arg1 = compile(ss6,s1,depth1,0,is_single);
  17722. arg2 = 1; arg3 = 0;
  17723. if (s1<se1) {
  17724. s0 = ++s1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  17725. arg2 = compile(s1,s0,depth1,0,is_single);
  17726. arg3 = s0<se1?compile(++s0,se1,depth1,0,is_single):0;
  17727. }
  17728. _cimg_mp_check_type(arg1,1,2,0);
  17729. _cimg_mp_check_type(arg2,2,1,0);
  17730. _cimg_mp_check_type(arg3,3,1,0);
  17731. p1 = _cimg_mp_size(arg1);
  17732. pos = vector(p1);
  17733. CImg<ulongT>::vector((ulongT)mp_shift,pos,arg1,p1,arg2,arg3).move_to(code);
  17734. _cimg_mp_return(pos);
  17735. }
  17736. if (!std::strncmp(ss,"sign(",5)) { // Sign
  17737. _cimg_mp_op("Function 'sign()'");
  17738. arg1 = compile(ss5,se1,depth1,0,is_single);
  17739. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sign,arg1);
  17740. if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sign(mem[arg1]));
  17741. _cimg_mp_scalar1(mp_sign,arg1);
  17742. }
  17743. if (!std::strncmp(ss,"sin(",4)) { // Sine
  17744. _cimg_mp_op("Function 'sin()'");
  17745. arg1 = compile(ss4,se1,depth1,0,is_single);
  17746. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sin,arg1);
  17747. if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sin(mem[arg1]));
  17748. _cimg_mp_scalar1(mp_sin,arg1);
  17749. }
  17750. if (!std::strncmp(ss,"sinc(",5)) { // Sine cardinal
  17751. _cimg_mp_op("Function 'sinc()'");
  17752. arg1 = compile(ss5,se1,depth1,0,is_single);
  17753. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sinc,arg1);
  17754. if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sinc(mem[arg1]));
  17755. _cimg_mp_scalar1(mp_sinc,arg1);
  17756. }
  17757. if (!std::strncmp(ss,"sinh(",5)) { // Hyperbolic sine
  17758. _cimg_mp_op("Function 'sinh()'");
  17759. arg1 = compile(ss5,se1,depth1,0,is_single);
  17760. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sinh,arg1);
  17761. if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sinh(mem[arg1]));
  17762. _cimg_mp_scalar1(mp_sinh,arg1);
  17763. }
  17764. if (!std::strncmp(ss,"size(",5)) { // Vector size.
  17765. _cimg_mp_op("Function 'size()'");
  17766. arg1 = compile(ss5,se1,depth1,0,is_single);
  17767. _cimg_mp_constant(_cimg_mp_is_scalar(arg1)?0:_cimg_mp_size(arg1));
  17768. }
  17769. if (!std::strncmp(ss,"solve(",6)) { // Solve linear system
  17770. _cimg_mp_op("Function 'solve()'");
  17771. s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  17772. arg1 = compile(ss6,s1,depth1,0,is_single);
  17773. s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  17774. arg2 = compile(++s1,s2,depth1,0,is_single);
  17775. arg3 = s2<se1?compile(++s2,se1,depth1,0,is_single):1;
  17776. _cimg_mp_check_type(arg1,1,2,0);
  17777. _cimg_mp_check_type(arg2,2,2,0);
  17778. _cimg_mp_check_constant(arg3,3,3);
  17779. p1 = _cimg_mp_size(arg1);
  17780. p2 = _cimg_mp_size(arg2);
  17781. p3 = (unsigned int)mem[arg3];
  17782. arg5 = p2/p3;
  17783. arg4 = p1/arg5;
  17784. if (arg4*arg5!=p1 || arg5*p3!=p2) {
  17785. *se = saved_char;
  17786. s0 = ss - 4>expr._data?ss - 4:expr._data;
  17787. cimg::strellipsize(s0,64);
  17788. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  17789. "CImg<%s>::%s: %s: Types of first and second arguments ('%s' and '%s') "
  17790. "do not match with third argument 'nb_colsB=%u', "
  17791. "in expression '%s%s%s'.",
  17792. pixel_type(),_cimg_mp_calling_function,s_op,
  17793. s_type(arg1)._data,s_type(arg2)._data,p3,
  17794. s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
  17795. }
  17796. pos = vector(arg4*p3);
  17797. CImg<ulongT>::vector((ulongT)mp_solve,pos,arg1,arg2,arg4,arg5,p3).move_to(code);
  17798. _cimg_mp_return(pos);
  17799. }
  17800. if (!std::strncmp(ss,"sort(",5)) { // Sort vector
  17801. _cimg_mp_op("Function 'sort()'");
  17802. if (*ss5!='#') { // Vector
  17803. s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  17804. arg1 = compile(ss5,s1,depth1,0,is_single);
  17805. arg2 = arg3 = 1;
  17806. if (s1<se1) {
  17807. s0 = ++s1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  17808. arg2 = compile(s1,s0,depth1,0,is_single);
  17809. arg3 = s0<se1?compile(++s0,se1,depth1,0,is_single):1;
  17810. }
  17811. _cimg_mp_check_type(arg1,1,2,0);
  17812. _cimg_mp_check_type(arg2,2,1,0);
  17813. _cimg_mp_check_constant(arg3,3,3);
  17814. arg3 = (unsigned int)mem[arg3];
  17815. p1 = _cimg_mp_size(arg1);
  17816. if (p1%arg3) {
  17817. *se = saved_char;
  17818. s0 = ss - 4>expr._data?ss - 4:expr._data;
  17819. cimg::strellipsize(s0,64);
  17820. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  17821. "CImg<%s>::%s: %s: Invalid specified chunk size (%u) for first argument "
  17822. "('%s'), in expression '%s%s%s'.",
  17823. pixel_type(),_cimg_mp_calling_function,s_op,
  17824. arg3,s_type(arg1)._data,
  17825. s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
  17826. }
  17827. pos = vector(p1);
  17828. CImg<ulongT>::vector((ulongT)mp_sort,pos,arg1,p1,arg2,arg3).move_to(code);
  17829. _cimg_mp_return(pos);
  17830. } else { // Image
  17831. s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  17832. p1 = compile(ss6,s1,depth1,0,is_single);
  17833. arg1 = 1;
  17834. arg2 = constant(-1.0);
  17835. if (s1<se1) {
  17836. s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  17837. arg1 = compile(++s1,s2,depth1,0,is_single);
  17838. if (s2<se1) arg2 = compile(++s2,se1,depth1,0,is_single);
  17839. }
  17840. _cimg_mp_check_type(arg1,2,1,0);
  17841. _cimg_mp_check_type(arg2,3,1,0);
  17842. _cimg_mp_check_list(true);
  17843. CImg<ulongT>::vector((ulongT)mp_image_sort,_cimg_mp_slot_nan,p1,arg1,arg2).move_to(code);
  17844. _cimg_mp_return_nan();
  17845. }
  17846. }
  17847. if (!std::strncmp(ss,"sqr(",4)) { // Square
  17848. _cimg_mp_op("Function 'sqr()'");
  17849. arg1 = compile(ss4,se1,depth1,0,is_single);
  17850. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sqr,arg1);
  17851. if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sqr(mem[arg1]));
  17852. _cimg_mp_scalar1(mp_sqr,arg1);
  17853. }
  17854. if (!std::strncmp(ss,"sqrt(",5)) { // Square root
  17855. _cimg_mp_op("Function 'sqrt()'");
  17856. arg1 = compile(ss5,se1,depth1,0,is_single);
  17857. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sqrt,arg1);
  17858. if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sqrt(mem[arg1]));
  17859. _cimg_mp_scalar1(mp_sqrt,arg1);
  17860. }
  17861. if (!std::strncmp(ss,"srand(",6)) { // Set RNG seed
  17862. _cimg_mp_op("Function 'srand()'");
  17863. arg1 = ss6<se1?compile(ss6,se1,depth1,0,is_single):~0U;
  17864. if (arg1!=~0U) { _cimg_mp_check_type(arg1,1,1,0); _cimg_mp_scalar1(mp_srand,arg1); }
  17865. _cimg_mp_scalar0(mp_srand0);
  17866. }
  17867. if (!std::strncmp(ss,"stats(",6)) { // Image statistics
  17868. _cimg_mp_op("Function 'stats()'");
  17869. if (*ss6=='#') { // Index specified
  17870. p1 = compile(ss7,se1,depth1,0,is_single);
  17871. _cimg_mp_check_list(false);
  17872. } else { if (ss6!=se1) break; p1 = ~0U; }
  17873. pos = vector(14);
  17874. CImg<ulongT>::vector((ulongT)mp_image_stats,pos,p1).move_to(code);
  17875. _cimg_mp_return(pos);
  17876. }
  17877. if (!std::strncmp(ss,"stov(",5)) { // String to double
  17878. _cimg_mp_op("Function 'stov()'");
  17879. s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  17880. arg1 = compile(ss5,s1,depth1,0,is_single);
  17881. arg2 = arg3 = 0;
  17882. if (s1<se1) {
  17883. s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  17884. arg2 = compile(++s1,s2,depth1,0,is_single);
  17885. arg3 = s2<se1?compile(++s2,se1,depth1,0,is_single):0;
  17886. }
  17887. _cimg_mp_check_type(arg2,2,1,0);
  17888. _cimg_mp_check_type(arg3,3,1,0);
  17889. p1 = _cimg_mp_size(arg1);
  17890. _cimg_mp_scalar4(mp_stov,arg1,p1,arg2,arg3);
  17891. }
  17892. if (!std::strncmp(ss,"svd(",4)) { // Matrix SVD
  17893. _cimg_mp_op("Function 'svd()'");
  17894. s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  17895. arg1 = compile(ss4,s1,depth1,0,is_single);
  17896. arg2 = s1<se1?compile(++s1,se1,depth1,0,is_single):1;
  17897. _cimg_mp_check_type(arg1,1,2,0);
  17898. _cimg_mp_check_constant(arg2,2,3);
  17899. p1 = _cimg_mp_size(arg1);
  17900. p2 = (unsigned int)mem[arg2];
  17901. p3 = p1/p2;
  17902. if (p3*p2!=p1) {
  17903. *se = saved_char;
  17904. s0 = ss - 4>expr._data?ss - 4:expr._data;
  17905. cimg::strellipsize(s0,64);
  17906. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  17907. "CImg<%s>::%s: %s: Type of first argument ('%s') "
  17908. "does not match with second argument 'nb_colsA=%u', "
  17909. "in expression '%s%s%s'.",
  17910. pixel_type(),_cimg_mp_calling_function,s_op,
  17911. s_type(arg1)._data,p2,
  17912. s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
  17913. }
  17914. pos = vector(p1 + p2 + p2*p2);
  17915. CImg<ulongT>::vector((ulongT)mp_matrix_svd,pos,arg1,p2,p3).move_to(code);
  17916. _cimg_mp_return(pos);
  17917. }
  17918. break;
  17919. case 't' :
  17920. if (!std::strncmp(ss,"tan(",4)) { // Tangent
  17921. _cimg_mp_op("Function 'tan()'");
  17922. arg1 = compile(ss4,se1,depth1,0,is_single);
  17923. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_tan,arg1);
  17924. if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::tan(mem[arg1]));
  17925. _cimg_mp_scalar1(mp_tan,arg1);
  17926. }
  17927. if (!std::strncmp(ss,"tanh(",5)) { // Hyperbolic tangent
  17928. _cimg_mp_op("Function 'tanh()'");
  17929. arg1 = compile(ss5,se1,depth1,0,is_single);
  17930. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_tanh,arg1);
  17931. if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::tanh(mem[arg1]));
  17932. _cimg_mp_scalar1(mp_tanh,arg1);
  17933. }
  17934. if (!std::strncmp(ss,"trace(",6)) { // Matrix trace
  17935. _cimg_mp_op("Function 'trace()'");
  17936. arg1 = compile(ss6,se1,depth1,0,is_single);
  17937. _cimg_mp_check_matrix_square(arg1,1);
  17938. p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1)));
  17939. _cimg_mp_scalar2(mp_trace,arg1,p1);
  17940. }
  17941. if (!std::strncmp(ss,"transp(",7)) { // Matrix transpose
  17942. _cimg_mp_op("Function 'transp()'");
  17943. s1 = ss7; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  17944. arg1 = compile(ss7,s1,depth1,0,is_single);
  17945. arg2 = compile(++s1,se1,depth1,0,is_single);
  17946. _cimg_mp_check_type(arg1,1,2,0);
  17947. _cimg_mp_check_constant(arg2,2,3);
  17948. p1 = _cimg_mp_size(arg1);
  17949. p2 = (unsigned int)mem[arg2];
  17950. p3 = p1/p2;
  17951. if (p2*p3!=p1) {
  17952. *se = saved_char;
  17953. s0 = ss - 4>expr._data?ss - 4:expr._data;
  17954. cimg::strellipsize(s0,64);
  17955. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  17956. "CImg<%s>::%s: %s: Size of first argument ('%s') does not match "
  17957. "second argument 'nb_cols=%u', in expression '%s%s%s'.",
  17958. pixel_type(),_cimg_mp_calling_function,s_op,
  17959. s_type(arg1)._data,p2,
  17960. s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
  17961. }
  17962. pos = vector(p3*p2);
  17963. CImg<ulongT>::vector((ulongT)mp_transp,pos,arg1,p2,p3).move_to(code);
  17964. _cimg_mp_return(pos);
  17965. }
  17966. break;
  17967. case 'u' :
  17968. if (*ss1=='(') { // Random value with uniform distribution
  17969. _cimg_mp_op("Function 'u()'");
  17970. if (*ss2==')') _cimg_mp_scalar2(mp_u,0,1);
  17971. s1 = ss2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  17972. arg1 = compile(ss2,s1,depth1,0,is_single);
  17973. if (s1<se1) arg2 = compile(++s1,se1,depth1,0,is_single); else { arg2 = arg1; arg1 = 0; }
  17974. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  17975. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_u,arg1,arg2);
  17976. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_u,arg1,arg2);
  17977. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_u,arg1,arg2);
  17978. _cimg_mp_scalar2(mp_u,arg1,arg2);
  17979. }
  17980. if (!std::strncmp(ss,"unref(",6)) { // Un-reference variable
  17981. _cimg_mp_op("Function 'unref()'");
  17982. arg1 = ~0U;
  17983. for (s0 = ss6; s0<se1; s0 = s1) {
  17984. if (s0>ss6 && *s0==',') ++s0;
  17985. s1 = s0; while (s1<se1 && *s1!=',') ++s1;
  17986. c1 = *s1;
  17987. if (s1>s0) {
  17988. *s1 = 0;
  17989. arg2 = arg3 = ~0U;
  17990. if (s0[0]=='w' && s0[1]=='h' && !s0[2]) arg1 = reserved_label[arg3 = 0];
  17991. else if (s0[0]=='w' && s0[1]=='h' && s0[2]=='d' && !s0[3]) arg1 = reserved_label[arg3 = 1];
  17992. else if (s0[0]=='w' && s0[1]=='h' && s0[2]=='d' && s0[3]=='s' && !s0[4])
  17993. arg1 = reserved_label[arg3 = 2];
  17994. else if (s0[0]=='p' && s0[1]=='i' && !s0[2]) arg1 = reserved_label[arg3 = 3];
  17995. else if (s0[0]=='i' && s0[1]=='m' && !s0[2]) arg1 = reserved_label[arg3 = 4];
  17996. else if (s0[0]=='i' && s0[1]=='M' && !s0[2]) arg1 = reserved_label[arg3 = 5];
  17997. else if (s0[0]=='i' && s0[1]=='a' && !s0[2]) arg1 = reserved_label[arg3 = 6];
  17998. else if (s0[0]=='i' && s0[1]=='v' && !s0[2]) arg1 = reserved_label[arg3 = 7];
  17999. else if (s0[0]=='i' && s0[1]=='s' && !s0[2]) arg1 = reserved_label[arg3 = 8];
  18000. else if (s0[0]=='i' && s0[1]=='p' && !s0[2]) arg1 = reserved_label[arg3 = 9];
  18001. else if (s0[0]=='i' && s0[1]=='c' && !s0[2]) arg1 = reserved_label[arg3 = 10];
  18002. else if (s0[0]=='x' && s0[1]=='m' && !s0[2]) arg1 = reserved_label[arg3 = 11];
  18003. else if (s0[0]=='y' && s0[1]=='m' && !s0[2]) arg1 = reserved_label[arg3 = 12];
  18004. else if (s0[0]=='z' && s0[1]=='m' && !s0[2]) arg1 = reserved_label[arg3 = 13];
  18005. else if (s0[0]=='c' && s0[1]=='m' && !s0[2]) arg1 = reserved_label[arg3 = 14];
  18006. else if (s0[0]=='x' && s0[1]=='M' && !s0[2]) arg1 = reserved_label[arg3 = 15];
  18007. else if (s0[0]=='y' && s0[1]=='M' && !s0[2]) arg1 = reserved_label[arg3 = 16];
  18008. else if (s0[0]=='z' && s0[1]=='M' && !s0[2]) arg1 = reserved_label[arg3 = 17];
  18009. else if (s0[0]=='c' && s0[1]=='M' && !s0[2]) arg1 = reserved_label[arg3 = 18];
  18010. else if (s0[0]=='i' && s0[1]>='0' && s0[1]<='9' && !s0[2])
  18011. arg1 = reserved_label[arg3 = 19 + s0[1] - '0'];
  18012. else if (!std::strcmp(s0,"interpolation")) arg1 = reserved_label[arg3 = 29];
  18013. else if (!std::strcmp(s0,"boundary")) arg1 = reserved_label[arg3 = 30];
  18014. else if (s0[1]) { // Multi-char variable
  18015. cimglist_for(variable_def,i) if (!std::strcmp(s0,variable_def[i])) {
  18016. arg1 = variable_pos[i]; arg2 = i; break;
  18017. }
  18018. } else arg1 = reserved_label[arg3 = *s0]; // Single-char variable
  18019. if (arg1!=~0U) {
  18020. if (arg2==~0U) { if (arg3!=~0U) reserved_label[arg3] = ~0U; }
  18021. else {
  18022. variable_def.remove(arg2);
  18023. if (arg2<variable_pos._width - 1)
  18024. std::memmove(variable_pos._data + arg2,variable_pos._data + arg2 + 1,
  18025. sizeof(uintT)*(variable_pos._width - arg2 - 1));
  18026. --variable_pos._width;
  18027. }
  18028. }
  18029. *s1 = c1;
  18030. } else compile(s0,s1,depth1,0,is_single); // Will throw a 'missing argument' exception
  18031. }
  18032. _cimg_mp_return(arg1!=~0U?arg1:_cimg_mp_slot_nan); // Return value of last specified variable.
  18033. }
  18034. if (!std::strncmp(ss,"uppercase(",10)) { // Upper case
  18035. _cimg_mp_op("Function 'uppercase()'");
  18036. arg1 = compile(ss + 10,se1,depth1,0,is_single);
  18037. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_uppercase,arg1);
  18038. if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::uppercase(mem[arg1]));
  18039. _cimg_mp_scalar1(mp_uppercase,arg1);
  18040. }
  18041. break;
  18042. case 'v' :
  18043. if ((cimg_sscanf(ss,"vector%u%c",&(arg1=~0U),&sep)==2 && sep=='(' && arg1>0) ||
  18044. !std::strncmp(ss,"vector(",7) ||
  18045. (!std::strncmp(ss,"vector",6) && ss7<se1 && (s=std::strchr(ss7,'('))!=0)) { // Vector
  18046. _cimg_mp_op("Function 'vector()'");
  18047. arg2 = 0; // Number of specified values.
  18048. if (arg1==~0U && *ss6!='(') {
  18049. arg1 = compile(ss6,s++,depth1,0,is_single);
  18050. _cimg_mp_check_constant(arg1,0,3);
  18051. arg1 = (unsigned int)mem[arg1];
  18052. } else s = std::strchr(ss6,'(') + 1;
  18053. if (s<se1 || arg1==~0U) for ( ; s<se; ++s) {
  18054. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  18055. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  18056. arg3 = compile(s,ns,depth1,0,is_single);
  18057. if (_cimg_mp_is_vector(arg3)) {
  18058. arg4 = _cimg_mp_size(arg3);
  18059. CImg<ulongT>::sequence(arg4,arg3 + 1,arg3 + arg4).move_to(l_opcode);
  18060. arg2+=arg4;
  18061. } else { CImg<ulongT>::vector(arg3).move_to(l_opcode); ++arg2; }
  18062. s = ns;
  18063. }
  18064. if (arg1==~0U) arg1 = arg2;
  18065. _cimg_mp_check_vector0(arg1);
  18066. pos = vector(arg1);
  18067. l_opcode.insert(CImg<ulongT>::vector((ulongT)mp_vector_init,pos,0,arg1),0);
  18068. (l_opcode>'y').move_to(opcode);
  18069. opcode[2] = opcode._height;
  18070. opcode.move_to(code);
  18071. _cimg_mp_return(pos);
  18072. }
  18073. if (!std::strncmp(ss,"vtos(",5)) { // Double(s) to string
  18074. _cimg_mp_op("Function 'vtos()'");
  18075. s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  18076. arg1 = compile(ss5,s1,depth1,0,is_single);
  18077. arg2 = 0; arg3 = ~0U;
  18078. if (s1<se1) {
  18079. s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  18080. arg2 = compile(++s1,s2,depth1,0,is_single);
  18081. arg3 = s2<se1?compile(++s2,se1,depth1,0,is_single):~0U;
  18082. }
  18083. _cimg_mp_check_type(arg2,2,1,0);
  18084. if (arg3==~0U) { // Auto-guess best output vector size
  18085. p1 = _cimg_mp_size(arg1);
  18086. p1 = p1?19*p1 - 1:18;
  18087. } else {
  18088. _cimg_mp_check_constant(arg3,3,3);
  18089. p1 = (unsigned int)mem[arg3];
  18090. }
  18091. pos = vector(p1);
  18092. CImg<ulongT>::vector((ulongT)mp_vtos,pos,p1,arg1,_cimg_mp_size(arg1),arg2).move_to(code);
  18093. _cimg_mp_return(pos);
  18094. }
  18095. break;
  18096. case 'w' :
  18097. if (*ss1=='(') { // Image width
  18098. _cimg_mp_op("Function 'w()'");
  18099. if (*ss2=='#') { // Index specified
  18100. p1 = compile(ss3,se1,depth1,0,is_single);
  18101. _cimg_mp_check_list(false);
  18102. } else { if (ss2!=se1) break; p1 = ~0U; }
  18103. pos = scalar();
  18104. CImg<ulongT>::vector((ulongT)mp_image_w,pos,p1).move_to(code);
  18105. _cimg_mp_return(pos);
  18106. }
  18107. if (*ss1=='h' && *ss2=='(') { // Image width*height
  18108. _cimg_mp_op("Function 'wh()'");
  18109. if (*ss3=='#') { // Index specified
  18110. p1 = compile(ss4,se1,depth1,0,is_single);
  18111. _cimg_mp_check_list(false);
  18112. } else { if (ss3!=se1) break; p1 = ~0U; }
  18113. pos = scalar();
  18114. CImg<ulongT>::vector((ulongT)mp_image_wh,pos,p1).move_to(code);
  18115. _cimg_mp_return(pos);
  18116. }
  18117. if (*ss1=='h' && *ss2=='d' && *ss3=='(') { // Image width*height*depth
  18118. _cimg_mp_op("Function 'whd()'");
  18119. if (*ss4=='#') { // Index specified
  18120. p1 = compile(ss5,se1,depth1,0,is_single);
  18121. _cimg_mp_check_list(false);
  18122. } else { if (ss4!=se1) break; p1 = ~0U; }
  18123. pos = scalar();
  18124. CImg<ulongT>::vector((ulongT)mp_image_whd,pos,p1).move_to(code);
  18125. _cimg_mp_return(pos);
  18126. }
  18127. if (*ss1=='h' && *ss2=='d' && *ss3=='s' && *ss4=='(') { // Image width*height*depth*spectrum
  18128. _cimg_mp_op("Function 'whds()'");
  18129. if (*ss5=='#') { // Index specified
  18130. p1 = compile(ss6,se1,depth1,0,is_single);
  18131. _cimg_mp_check_list(false);
  18132. } else { if (ss5!=se1) break; p1 = ~0U; }
  18133. pos = scalar();
  18134. CImg<ulongT>::vector((ulongT)mp_image_whds,pos,p1).move_to(code);
  18135. _cimg_mp_return(pos);
  18136. }
  18137. if (!std::strncmp(ss,"while(",6) || !std::strncmp(ss,"whiledo(",8)) { // While...do
  18138. _cimg_mp_op("Function 'whiledo()'");
  18139. s0 = *ss5=='('?ss6:ss8;
  18140. s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  18141. p1 = code._width;
  18142. arg1 = compile(s0,s1,depth1,0,is_single);
  18143. p2 = code._width;
  18144. arg6 = mempos;
  18145. pos = compile(++s1,se1,depth1,0,is_single);
  18146. _cimg_mp_check_type(arg1,1,1,0);
  18147. arg2 = _cimg_mp_size(pos);
  18148. CImg<ulongT>::vector((ulongT)mp_whiledo,pos,arg1,p2 - p1,code._width - p2,arg2,
  18149. pos>=arg6 && !_cimg_mp_is_constant(pos),
  18150. arg1>=arg6 && !_cimg_mp_is_constant(arg1)).move_to(code,p1);
  18151. _cimg_mp_return(pos);
  18152. }
  18153. break;
  18154. case 'x' :
  18155. if (!std::strncmp(ss,"xor(",4)) { // Xor
  18156. _cimg_mp_op("Function 'xor()'");
  18157. s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  18158. arg1 = compile(ss4,s1,depth1,0,is_single);
  18159. arg2 = compile(++s1,se1,depth1,0,is_single);
  18160. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  18161. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_xor,arg1,arg2);
  18162. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_bitwise_xor,arg1,arg2);
  18163. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_bitwise_xor,arg1,arg2);
  18164. if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
  18165. _cimg_mp_constant((longT)mem[arg1] ^ (longT)mem[arg2]);
  18166. _cimg_mp_scalar2(mp_bitwise_xor,arg1,arg2);
  18167. }
  18168. break;
  18169. }
  18170. if (!std::strncmp(ss,"min(",4) || !std::strncmp(ss,"max(",4) ||
  18171. !std::strncmp(ss,"med(",4) || !std::strncmp(ss,"kth(",4) ||
  18172. !std::strncmp(ss,"sum(",4) || !std::strncmp(ss,"avg(",4) ||
  18173. !std::strncmp(ss,"std(",4) || !std::strncmp(ss,"var(",4) ||
  18174. !std::strncmp(ss,"prod(",5) ||
  18175. !std::strncmp(ss,"argmin(",7) || !std::strncmp(ss,"argmax(",7) ||
  18176. !std::strncmp(ss,"argkth(",7)) { // Multi-argument functions
  18177. _cimg_mp_op(*ss=='a'?(ss[1]=='v'?"Function 'avg()'":
  18178. ss[3]=='k'?"Function 'argkth()'":
  18179. ss[4]=='i'?"Function 'argmin()'":
  18180. "Function 'argmax()'"):
  18181. *ss=='s'?(ss[1]=='u'?"Function 'sum()'":"Function 'std()'"):
  18182. *ss=='k'?"Function 'kth()'":
  18183. *ss=='p'?"Function 'prod()'":
  18184. *ss=='v'?"Function 'var()'":
  18185. ss[1]=='i'?"Function 'min()'":
  18186. ss[1]=='a'?"Function 'max()'":"Function 'med()'");
  18187. op = *ss=='a'?(ss[1]=='v'?mp_avg:ss[3]=='k'?mp_argkth:ss[4]=='i'?mp_argmin:mp_argmax):
  18188. *ss=='s'?(ss[1]=='u'?mp_sum:mp_std):
  18189. *ss=='k'?mp_kth:
  18190. *ss=='p'?mp_prod:
  18191. *ss=='v'?mp_var:
  18192. ss[1]=='i'?mp_min:
  18193. ss[1]=='a'?mp_max:
  18194. ss[2]=='a'?mp_avg:
  18195. mp_median;
  18196. is_sth = true; // Tell if all arguments are constant
  18197. pos = scalar();
  18198. CImg<ulongT>::vector((ulongT)op,pos,0).move_to(l_opcode);
  18199. for (s = std::strchr(ss,'(') + 1; s<se; ++s) {
  18200. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  18201. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  18202. arg2 = compile(s,ns,depth1,0,is_single);
  18203. if (_cimg_mp_is_vector(arg2))
  18204. CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,
  18205. arg2 + (ulongT)_cimg_mp_size(arg2)).
  18206. move_to(l_opcode);
  18207. else CImg<ulongT>::vector(arg2).move_to(l_opcode);
  18208. is_sth&=_cimg_mp_is_constant(arg2);
  18209. s = ns;
  18210. }
  18211. (l_opcode>'y').move_to(opcode);
  18212. opcode[2] = opcode._height;
  18213. if (is_sth) _cimg_mp_constant(op(*this));
  18214. opcode.move_to(code);
  18215. _cimg_mp_return(pos);
  18216. }
  18217. // No corresponding built-in function -> Look for a user-defined macro call.
  18218. s0 = strchr(ss,'(');
  18219. if (s0) {
  18220. variable_name.assign(ss,(unsigned int)(s0 - ss + 1)).back() = 0;
  18221. // Count number of specified arguments.
  18222. p1 = 0;
  18223. for (s = s0 + 1; s<=se1; ++p1, s = ns + 1) {
  18224. while (*s && (signed char)*s<=' ') ++s;
  18225. if (*s==')' && !p1) break;
  18226. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  18227. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  18228. }
  18229. arg3 = 0; // Number of possible name matches
  18230. cimglist_for(macro_def,l) if (!std::strcmp(macro_def[l],variable_name) && ++arg3 &&
  18231. macro_def[l].back()==(char)p1) {
  18232. p2 = (unsigned int)macro_def[l].back(); // Number of required arguments
  18233. CImg<charT> _expr = macro_body[l]; // Expression to be substituted
  18234. p1 = 1; // Indice of current parsed argument
  18235. for (s = s0 + 1; s<=se1; ++p1, s = ns + 1) { // Parse function arguments
  18236. while (*s && (signed char)*s<=' ') ++s;
  18237. if (*s==')' && p1==1) break; // Function has no arguments
  18238. if (p1>p2) { ++p1; break; }
  18239. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  18240. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  18241. variable_name.assign(s,(unsigned int)(ns - s + 1)).back() = 0; // Argument to write
  18242. arg2 = 0;
  18243. cimg_forX(_expr,k) {
  18244. if (_expr[k]==(char)p1) { // Perform argument substitution
  18245. arg1 = _expr._width;
  18246. _expr.resize(arg1 + variable_name._width - 2,1,1,1,0);
  18247. std::memmove(_expr._data + k + variable_name._width - 1,_expr._data + k + 1,arg1 - k - 1);
  18248. std::memcpy(_expr._data + k,variable_name,variable_name._width - 1);
  18249. k+=variable_name._width - 2;
  18250. }
  18251. ++arg2;
  18252. }
  18253. }
  18254. // Recompute 'pexpr' and 'level' for evaluating substituted expression.
  18255. CImg<charT> _pexpr(_expr._width);
  18256. ns = _pexpr._data;
  18257. for (ps = _expr._data, c1 = ' '; *ps; ++ps) {
  18258. if ((signed char)*ps>' ') c1 = *ps;
  18259. *(ns++) = c1;
  18260. }
  18261. *ns = 0;
  18262. CImg<uintT> _level = get_level(_expr);
  18263. expr.swap(_expr);
  18264. pexpr.swap(_pexpr);
  18265. level.swap(_level);
  18266. s0 = user_macro;
  18267. user_macro = macro_def[l];
  18268. pos = compile(expr._data,expr._data + expr._width - 1,depth1,p_ref,is_single);
  18269. user_macro = s0;
  18270. level.swap(_level);
  18271. pexpr.swap(_pexpr);
  18272. expr.swap(_expr);
  18273. _cimg_mp_return(pos);
  18274. }
  18275. if (arg3) { // Macro name matched but number of arguments does not
  18276. CImg<uintT> sig_nargs(arg3);
  18277. arg1 = 0;
  18278. cimglist_for(macro_def,l) if (!std::strcmp(macro_def[l],variable_name))
  18279. sig_nargs[arg1++] = (unsigned int)macro_def[l].back();
  18280. *se = saved_char;
  18281. cimg::strellipsize(variable_name,64);
  18282. s0 = ss - 4>expr._data?ss - 4:expr._data;
  18283. cimg::strellipsize(s0,64);
  18284. if (sig_nargs._width>1) {
  18285. sig_nargs.sort();
  18286. arg1 = sig_nargs.back();
  18287. --sig_nargs._width;
  18288. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  18289. "CImg<%s>::%s: Function '%s()': Number of specified arguments (%u) "
  18290. "does not match macro declaration (defined for %s or %u arguments), "
  18291. "in expression '%s%s%s'.",
  18292. pixel_type(),_cimg_mp_calling_function,variable_name._data,
  18293. p1,sig_nargs.value_string()._data,arg1,
  18294. s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
  18295. } else
  18296. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  18297. "CImg<%s>::%s: Function '%s()': Number of specified arguments (%u) "
  18298. "does not match macro declaration (defined for %u argument%s), "
  18299. "in expression '%s%s%s'.",
  18300. pixel_type(),_cimg_mp_calling_function,variable_name._data,
  18301. p1,*sig_nargs,*sig_nargs!=1?"s":"",
  18302. s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
  18303. }
  18304. }
  18305. } // if (se1==')')
  18306. // Char / string initializer.
  18307. if (*se1=='\'' &&
  18308. ((se1>ss && *ss=='\'') ||
  18309. (se1>ss1 && *ss=='_' && *ss1=='\''))) {
  18310. if (*ss=='_') { _cimg_mp_op("Char initializer"); s1 = ss2; }
  18311. else { _cimg_mp_op("String initializer"); s1 = ss1; }
  18312. arg1 = (unsigned int)(se1 - s1); // Original string length.
  18313. if (arg1) {
  18314. CImg<charT>(s1,arg1 + 1).move_to(variable_name).back() = 0;
  18315. cimg::strunescape(variable_name);
  18316. arg1 = (unsigned int)std::strlen(variable_name);
  18317. }
  18318. if (!arg1) _cimg_mp_return(0); // Empty string -> 0
  18319. if (*ss=='_') {
  18320. if (arg1==1) _cimg_mp_constant(*variable_name);
  18321. *se = saved_char;
  18322. cimg::strellipsize(variable_name,64);
  18323. s0 = ss - 4>expr._data?ss - 4:expr._data;
  18324. cimg::strellipsize(s0,64);
  18325. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  18326. "CImg<%s>::%s: %s: Literal %s contains more than one character, "
  18327. "in expression '%s%s%s'.",
  18328. pixel_type(),_cimg_mp_calling_function,s_op,
  18329. ss1,
  18330. s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
  18331. }
  18332. pos = vector(arg1);
  18333. CImg<ulongT>::vector((ulongT)mp_string_init,pos,arg1).move_to(l_opcode);
  18334. CImg<ulongT>(1,arg1/sizeof(ulongT) + (arg1%sizeof(ulongT)?1:0)).move_to(l_opcode);
  18335. std::memcpy((char*)l_opcode[1]._data,variable_name,arg1);
  18336. (l_opcode>'y').move_to(code);
  18337. _cimg_mp_return(pos);
  18338. }
  18339. // Vector initializer [ ... ].
  18340. if (*ss=='[' && *se1==']') {
  18341. _cimg_mp_op("Vector initializer");
  18342. s1 = ss1; while (s1<se2 && (signed char)*s1<=' ') ++s1;
  18343. s2 = se2; while (s2>s1 && (signed char)*s2<=' ') --s2;
  18344. if (s2>s1 && *s1=='\'' && *s2=='\'') { // Vector values provided as a string
  18345. arg1 = (unsigned int)(s2 - s1 - 1); // Original string length.
  18346. if (arg1) {
  18347. CImg<charT>(s1 + 1,arg1 + 1).move_to(variable_name).back() = 0;
  18348. cimg::strunescape(variable_name);
  18349. arg1 = (unsigned int)std::strlen(variable_name);
  18350. }
  18351. if (!arg1) _cimg_mp_return(0); // Empty string -> 0
  18352. pos = vector(arg1);
  18353. CImg<ulongT>::vector((ulongT)mp_string_init,pos,arg1).move_to(l_opcode);
  18354. CImg<ulongT>(1,arg1/sizeof(ulongT) + (arg1%sizeof(ulongT)?1:0)).move_to(l_opcode);
  18355. std::memcpy((char*)l_opcode[1]._data,variable_name,arg1);
  18356. (l_opcode>'y').move_to(code);
  18357. } else { // Vector values provided as list of items
  18358. arg1 = 0; // Number of specified values.
  18359. if (*ss1!=']') for (s = ss1; s<se; ++s) {
  18360. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  18361. (*ns!=']' || level[ns - expr._data]!=clevel)) ++ns;
  18362. arg2 = compile(s,ns,depth1,0,is_single);
  18363. if (_cimg_mp_is_vector(arg2)) {
  18364. arg3 = _cimg_mp_size(arg2);
  18365. CImg<ulongT>::sequence(arg3,arg2 + 1,arg2 + arg3).move_to(l_opcode);
  18366. arg1+=arg3;
  18367. } else { CImg<ulongT>::vector(arg2).move_to(l_opcode); ++arg1; }
  18368. s = ns;
  18369. }
  18370. _cimg_mp_check_vector0(arg1);
  18371. pos = vector(arg1);
  18372. l_opcode.insert(CImg<ulongT>::vector((ulongT)mp_vector_init,pos,0,arg1),0);
  18373. (l_opcode>'y').move_to(opcode);
  18374. opcode[2] = opcode._height;
  18375. opcode.move_to(code);
  18376. }
  18377. _cimg_mp_return(pos);
  18378. }
  18379. // Variables related to the input list of images.
  18380. if (*ss1=='#' && ss2<se) {
  18381. arg1 = compile(ss2,se,depth1,0,is_single);
  18382. p1 = (unsigned int)(listin._width && _cimg_mp_is_constant(arg1)?cimg::mod((int)mem[arg1],listin.width()):~0U);
  18383. switch (*ss) {
  18384. case 'w' : // w#ind
  18385. if (!listin) _cimg_mp_return(0);
  18386. if (p1!=~0U) _cimg_mp_constant(listin[p1]._width);
  18387. _cimg_mp_scalar1(mp_list_width,arg1);
  18388. case 'h' : // h#ind
  18389. if (!listin) _cimg_mp_return(0);
  18390. if (p1!=~0U) _cimg_mp_constant(listin[p1]._height);
  18391. _cimg_mp_scalar1(mp_list_height,arg1);
  18392. case 'd' : // d#ind
  18393. if (!listin) _cimg_mp_return(0);
  18394. if (p1!=~0U) _cimg_mp_constant(listin[p1]._depth);
  18395. _cimg_mp_scalar1(mp_list_depth,arg1);
  18396. case 'r' : // r#ind
  18397. if (!listin) _cimg_mp_return(0);
  18398. if (p1!=~0U) _cimg_mp_constant(listin[p1]._is_shared);
  18399. _cimg_mp_scalar1(mp_list_is_shared,arg1);
  18400. case 's' : // s#ind
  18401. if (!listin) _cimg_mp_return(0);
  18402. if (p1!=~0U) _cimg_mp_constant(listin[p1]._spectrum);
  18403. _cimg_mp_scalar1(mp_list_spectrum,arg1);
  18404. case 'i' : // i#ind
  18405. if (!listin) _cimg_mp_return(0);
  18406. _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,_cimg_mp_slot_c,
  18407. 0,_cimg_mp_boundary);
  18408. case 'I' : // I#ind
  18409. p2 = p1!=~0U?listin[p1]._spectrum:listin._width?~0U:0;
  18410. _cimg_mp_check_vector0(p2);
  18411. pos = vector(p2);
  18412. CImg<ulongT>::vector((ulongT)mp_list_Joff,pos,p1,0,0,p2).move_to(code);
  18413. _cimg_mp_return(pos);
  18414. case 'R' : // R#ind
  18415. if (!listin) _cimg_mp_return(0);
  18416. _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,0,
  18417. 0,_cimg_mp_boundary);
  18418. case 'G' : // G#ind
  18419. if (!listin) _cimg_mp_return(0);
  18420. _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,1,
  18421. 0,_cimg_mp_boundary);
  18422. case 'B' : // B#ind
  18423. if (!listin) _cimg_mp_return(0);
  18424. _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,2,
  18425. 0,_cimg_mp_boundary);
  18426. case 'A' : // A#ind
  18427. if (!listin) _cimg_mp_return(0);
  18428. _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,3,
  18429. 0,_cimg_mp_boundary);
  18430. }
  18431. }
  18432. if (*ss1 && *ss2=='#' && ss3<se) {
  18433. arg1 = compile(ss3,se,depth1,0,is_single);
  18434. p1 = (unsigned int)(listin._width && _cimg_mp_is_constant(arg1)?cimg::mod((int)mem[arg1],listin.width()):~0U);
  18435. if (*ss=='w' && *ss1=='h') { // wh#ind
  18436. if (!listin) _cimg_mp_return(0);
  18437. if (p1!=~0U) _cimg_mp_constant(listin[p1]._width*listin[p1]._height);
  18438. _cimg_mp_scalar1(mp_list_wh,arg1);
  18439. }
  18440. arg2 = ~0U;
  18441. if (*ss=='i') {
  18442. if (*ss1=='c') { // ic#ind
  18443. if (!listin) _cimg_mp_return(0);
  18444. if (_cimg_mp_is_constant(arg1)) {
  18445. if (!list_median) list_median.assign(listin._width);
  18446. if (!list_median[p1]) CImg<doubleT>::vector(listin[p1].median()).move_to(list_median[p1]);
  18447. _cimg_mp_constant(*list_median[p1]);
  18448. }
  18449. _cimg_mp_scalar1(mp_list_median,arg1);
  18450. }
  18451. if (*ss1>='0' && *ss1<='9') { // i0#ind...i9#ind
  18452. if (!listin) _cimg_mp_return(0);
  18453. _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,*ss1 - '0',
  18454. 0,_cimg_mp_boundary);
  18455. }
  18456. switch (*ss1) {
  18457. case 'm' : arg2 = 0; break; // im#ind
  18458. case 'M' : arg2 = 1; break; // iM#ind
  18459. case 'a' : arg2 = 2; break; // ia#ind
  18460. case 'v' : arg2 = 3; break; // iv#ind
  18461. case 's' : arg2 = 12; break; // is#ind
  18462. case 'p' : arg2 = 13; break; // ip#ind
  18463. }
  18464. } else if (*ss1=='m') switch (*ss) {
  18465. case 'x' : arg2 = 4; break; // xm#ind
  18466. case 'y' : arg2 = 5; break; // ym#ind
  18467. case 'z' : arg2 = 6; break; // zm#ind
  18468. case 'c' : arg2 = 7; break; // cm#ind
  18469. } else if (*ss1=='M') switch (*ss) {
  18470. case 'x' : arg2 = 8; break; // xM#ind
  18471. case 'y' : arg2 = 9; break; // yM#ind
  18472. case 'z' : arg2 = 10; break; // zM#ind
  18473. case 'c' : arg2 = 11; break; // cM#ind
  18474. }
  18475. if (arg2!=~0U) {
  18476. if (!listin) _cimg_mp_return(0);
  18477. if (_cimg_mp_is_constant(arg1)) {
  18478. if (!list_stats) list_stats.assign(listin._width);
  18479. if (!list_stats[p1]) list_stats[p1].assign(1,14,1,1,0).fill(listin[p1].get_stats(),false);
  18480. _cimg_mp_constant(list_stats(p1,arg2));
  18481. }
  18482. _cimg_mp_scalar2(mp_list_stats,arg1,arg2);
  18483. }
  18484. }
  18485. if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='#' && ss4<se) { // whd#ind
  18486. arg1 = compile(ss4,se,depth1,0,is_single);
  18487. if (!listin) _cimg_mp_return(0);
  18488. p1 = (unsigned int)(_cimg_mp_is_constant(arg1)?cimg::mod((int)mem[arg1],listin.width()):~0U);
  18489. if (p1!=~0U) _cimg_mp_constant(listin[p1]._width*listin[p1]._height*listin[p1]._depth);
  18490. _cimg_mp_scalar1(mp_list_whd,arg1);
  18491. }
  18492. if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='s' && *ss4=='#' && ss5<se) { // whds#ind
  18493. arg1 = compile(ss5,se,depth1,0,is_single);
  18494. if (!listin) _cimg_mp_return(0);
  18495. p1 = (unsigned int)(_cimg_mp_is_constant(arg1)?cimg::mod((int)mem[arg1],listin.width()):~0U);
  18496. if (p1!=~0U) _cimg_mp_constant(listin[p1]._width*listin[p1]._height*listin[p1]._depth*listin[p1]._spectrum);
  18497. _cimg_mp_scalar1(mp_list_whds,arg1);
  18498. }
  18499. if (!std::strcmp(ss,"interpolation")) _cimg_mp_return(_cimg_mp_interpolation); // interpolation
  18500. if (!std::strcmp(ss,"boundary")) _cimg_mp_return(_cimg_mp_boundary); // boundary
  18501. // No known item found, assuming this is an already initialized variable.
  18502. variable_name.assign(ss,(unsigned int)(se - ss + 1)).back() = 0;
  18503. if (variable_name[1]) { // Multi-char variable
  18504. cimglist_for(variable_def,i) if (!std::strcmp(variable_name,variable_def[i]))
  18505. _cimg_mp_return(variable_pos[i]);
  18506. } else if (reserved_label[*variable_name]!=~0U) // Single-char variable
  18507. _cimg_mp_return(reserved_label[*variable_name]);
  18508. // Reached an unknown item -> error.
  18509. is_sth = true; // is_valid_variable_name
  18510. if (*variable_name>='0' && *variable_name<='9') is_sth = false;
  18511. else for (ns = variable_name._data; *ns; ++ns)
  18512. if (!is_varchar(*ns)) { is_sth = false; break; }
  18513. *se = saved_char;
  18514. c1 = *se1;
  18515. cimg::strellipsize(variable_name,64);
  18516. s0 = ss - 4>expr._data?ss - 4:expr._data;
  18517. cimg::strellipsize(s0,64);
  18518. if (is_sth)
  18519. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  18520. "CImg<%s>::%s: Undefined variable '%s' in expression '%s%s%s'.",
  18521. pixel_type(),_cimg_mp_calling_function,
  18522. variable_name._data,
  18523. s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
  18524. s1 = std::strchr(ss,'(');
  18525. s_op = s1 && c1==')'?"function call":"item";
  18526. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  18527. "CImg<%s>::%s: Unrecognized %s '%s' in expression '%s%s%s'.",
  18528. pixel_type(),_cimg_mp_calling_function,
  18529. s_op,variable_name._data,
  18530. s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
  18531. }
  18532. // Evaluation procedure.
  18533. double operator()(const double x, const double y, const double z, const double c) {
  18534. mem[_cimg_mp_slot_x] = x; mem[_cimg_mp_slot_y] = y; mem[_cimg_mp_slot_z] = z; mem[_cimg_mp_slot_c] = c;
  18535. for (p_code = code; p_code<p_code_end; ++p_code) {
  18536. opcode._data = p_code->_data;
  18537. const ulongT target = opcode[1];
  18538. mem[target] = _cimg_mp_defunc(*this);
  18539. }
  18540. return *result;
  18541. }
  18542. // Evaluation procedure (return output values in vector 'output').
  18543. template<typename t>
  18544. void operator()(const double x, const double y, const double z, const double c, t *const output) {
  18545. mem[_cimg_mp_slot_x] = x; mem[_cimg_mp_slot_y] = y; mem[_cimg_mp_slot_z] = z; mem[_cimg_mp_slot_c] = c;
  18546. for (p_code = code; p_code<p_code_end; ++p_code) {
  18547. opcode._data = p_code->_data;
  18548. const ulongT target = opcode[1];
  18549. mem[target] = _cimg_mp_defunc(*this);
  18550. }
  18551. if (result_dim) {
  18552. const double *ptrs = result + 1;
  18553. t *ptrd = output;
  18554. for (unsigned int k = 0; k<result_dim; ++k) *(ptrd++) = (t)*(ptrs++);
  18555. } else *output = (t)*result;
  18556. }
  18557. // Evaluation procedure for the end() blocks.
  18558. void end() {
  18559. if (code_end.is_empty()) return;
  18560. if (imgin) {
  18561. mem[_cimg_mp_slot_x] = imgin._width - 1.0;
  18562. mem[_cimg_mp_slot_y] = imgin._height - 1.0f;
  18563. mem[_cimg_mp_slot_z] = imgin._depth - 1.0f;
  18564. mem[_cimg_mp_slot_c] = imgin._spectrum - 1.0f;
  18565. } else mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0;
  18566. p_code_end = code_end.end();
  18567. for (p_code = code_end; p_code<p_code_end; ++p_code) {
  18568. opcode._data = p_code->_data;
  18569. const ulongT target = opcode[1];
  18570. mem[target] = _cimg_mp_defunc(*this);
  18571. }
  18572. }
  18573. // Return type of a memory element as a string.
  18574. CImg<charT> s_type(const unsigned int arg) const {
  18575. CImg<charT> res;
  18576. if (_cimg_mp_is_vector(arg)) { // Vector
  18577. CImg<charT>::string("vectorXXXXXXXXXXXXXXXX").move_to(res);
  18578. std::sprintf(res._data + 6,"%u",_cimg_mp_size(arg));
  18579. } else CImg<charT>::string("scalar").move_to(res);
  18580. return res;
  18581. }
  18582. // Insert constant value in memory.
  18583. unsigned int constant(const double val) {
  18584. // Search for built-in constant.
  18585. if (val==(double)(int)val) {
  18586. if (val>=0 && val<=10) return (unsigned int)val;
  18587. if (val<0 && val>=-5) return (unsigned int)(10 - val);
  18588. }
  18589. if (val==0.5) return 16;
  18590. if (cimg::type<double>::is_nan(val)) return _cimg_mp_slot_nan;
  18591. // Search for constant already requested before (in const cache).
  18592. unsigned int ind = ~0U;
  18593. if (constcache_size<1024) {
  18594. if (!constcache_size) {
  18595. constcache_vals.assign(16,1,1,1,0);
  18596. constcache_inds.assign(16,1,1,1,0);
  18597. *constcache_vals = val;
  18598. constcache_size = 1;
  18599. ind = 0;
  18600. } else { // Dichotomic search
  18601. const double val_beg = *constcache_vals, val_end = constcache_vals[constcache_size - 1];
  18602. if (val_beg>=val) ind = 0;
  18603. else if (val_end==val) ind = constcache_size - 1;
  18604. else if (val_end<val) ind = constcache_size;
  18605. else {
  18606. unsigned int i0 = 1, i1 = constcache_size - 2;
  18607. while (i0<=i1) {
  18608. const unsigned int mid = (i0 + i1)/2;
  18609. if (constcache_vals[mid]==val) { i0 = mid; break; }
  18610. else if (constcache_vals[mid]<val) i0 = mid + 1;
  18611. else i1 = mid - 1;
  18612. }
  18613. ind = i0;
  18614. }
  18615. if (ind>=constcache_size || constcache_vals[ind]!=val) {
  18616. ++constcache_size;
  18617. if (constcache_size>constcache_vals._width) {
  18618. constcache_vals.resize(-200,1,1,1,0);
  18619. constcache_inds.resize(-200,1,1,1,0);
  18620. }
  18621. const int l = constcache_size - (int)ind - 1;
  18622. if (l>0) {
  18623. std::memmove(&constcache_vals[ind + 1],&constcache_vals[ind],l*sizeof(double));
  18624. std::memmove(&constcache_inds[ind + 1],&constcache_inds[ind],l*sizeof(unsigned int));
  18625. }
  18626. constcache_vals[ind] = val;
  18627. constcache_inds[ind] = 0;
  18628. }
  18629. }
  18630. if (constcache_inds[ind]) return constcache_inds[ind];
  18631. }
  18632. // Insert new constant in memory if necessary.
  18633. if (mempos>=mem._width) { mem.resize(-200,1,1,1,0); memtype.resize(-200,1,1,1,0); }
  18634. const unsigned int pos = mempos++;
  18635. mem[pos] = val;
  18636. memtype[pos] = 1; // Set constant property
  18637. if (ind!=~0U) constcache_inds[ind] = pos;
  18638. return pos;
  18639. }
  18640. // Insert code instructions for processing scalars.
  18641. unsigned int scalar() { // Insert new scalar in memory.
  18642. if (mempos>=mem._width) { mem.resize(-200,1,1,1,0); memtype.resize(mem._width,1,1,1,0); }
  18643. return mempos++;
  18644. }
  18645. unsigned int scalar0(const mp_func op) {
  18646. const unsigned int pos = scalar();
  18647. CImg<ulongT>::vector((ulongT)op,pos).move_to(code);
  18648. return pos;
  18649. }
  18650. unsigned int scalar1(const mp_func op, const unsigned int arg1) {
  18651. const unsigned int pos =
  18652. arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1) && op!=mp_copy?arg1:scalar();
  18653. CImg<ulongT>::vector((ulongT)op,pos,arg1).move_to(code);
  18654. return pos;
  18655. }
  18656. unsigned int scalar2(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
  18657. const unsigned int pos =
  18658. arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
  18659. arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:scalar();
  18660. CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2).move_to(code);
  18661. return pos;
  18662. }
  18663. unsigned int scalar3(const mp_func op,
  18664. const unsigned int arg1, const unsigned int arg2, const unsigned int arg3) {
  18665. const unsigned int pos =
  18666. arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
  18667. arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
  18668. arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:scalar();
  18669. CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3).move_to(code);
  18670. return pos;
  18671. }
  18672. unsigned int scalar4(const mp_func op,
  18673. const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
  18674. const unsigned int arg4) {
  18675. const unsigned int pos =
  18676. arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
  18677. arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
  18678. arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
  18679. arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:scalar();
  18680. CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4).move_to(code);
  18681. return pos;
  18682. }
  18683. unsigned int scalar5(const mp_func op,
  18684. const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
  18685. const unsigned int arg4, const unsigned int arg5) {
  18686. const unsigned int pos =
  18687. arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
  18688. arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
  18689. arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
  18690. arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:
  18691. arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5:scalar();
  18692. CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5).move_to(code);
  18693. return pos;
  18694. }
  18695. unsigned int scalar6(const mp_func op,
  18696. const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
  18697. const unsigned int arg4, const unsigned int arg5, const unsigned int arg6) {
  18698. const unsigned int pos =
  18699. arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
  18700. arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
  18701. arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
  18702. arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:
  18703. arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5:
  18704. arg6>_cimg_mp_slot_c && _cimg_mp_is_comp(arg6)?arg6:scalar();
  18705. CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5,arg6).move_to(code);
  18706. return pos;
  18707. }
  18708. unsigned int scalar7(const mp_func op,
  18709. const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
  18710. const unsigned int arg4, const unsigned int arg5, const unsigned int arg6,
  18711. const unsigned int arg7) {
  18712. const unsigned int pos =
  18713. arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
  18714. arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
  18715. arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
  18716. arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:
  18717. arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5:
  18718. arg6>_cimg_mp_slot_c && _cimg_mp_is_comp(arg6)?arg6:
  18719. arg7>_cimg_mp_slot_c && _cimg_mp_is_comp(arg7)?arg7:scalar();
  18720. CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5,arg6,arg7).move_to(code);
  18721. return pos;
  18722. }
  18723. // Return a string that defines the calling function + the user-defined function scope.
  18724. CImg<charT> calling_function_s() const {
  18725. CImg<charT> res;
  18726. const unsigned int
  18727. l1 = calling_function?(unsigned int)std::strlen(calling_function):0U,
  18728. l2 = user_macro?(unsigned int)std::strlen(user_macro):0U;
  18729. if (l2) {
  18730. res.assign(l1 + l2 + 48);
  18731. cimg_snprintf(res,res._width,"%s(): When substituting function '%s()'",calling_function,user_macro);
  18732. } else {
  18733. res.assign(l1 + l2 + 4);
  18734. cimg_snprintf(res,res._width,"%s()",calling_function);
  18735. }
  18736. return res;
  18737. }
  18738. // Return true if specified argument can be a part of an allowed variable name.
  18739. bool is_varchar(const char c) const {
  18740. return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_';
  18741. }
  18742. // Insert code instructions for processing vectors.
  18743. bool is_comp_vector(const unsigned int arg) const {
  18744. unsigned int siz = _cimg_mp_size(arg);
  18745. if (siz>8) return false;
  18746. const int *ptr = memtype.data(arg + 1);
  18747. bool is_tmp = true;
  18748. while (siz-->0) if (*(ptr++)) { is_tmp = false; break; }
  18749. return is_tmp;
  18750. }
  18751. void set_variable_vector(const unsigned int arg) {
  18752. unsigned int siz = _cimg_mp_size(arg);
  18753. int *ptr = memtype.data(arg + 1);
  18754. while (siz-->0) *(ptr++) = -1;
  18755. }
  18756. unsigned int vector(const unsigned int siz) { // Insert new vector of specified size in memory
  18757. if (mempos + siz>=mem._width) {
  18758. mem.resize(2*mem._width + siz,1,1,1,0);
  18759. memtype.resize(mem._width,1,1,1,0);
  18760. }
  18761. const unsigned int pos = mempos++;
  18762. mem[pos] = cimg::type<double>::nan();
  18763. memtype[pos] = siz + 1;
  18764. mempos+=siz;
  18765. return pos;
  18766. }
  18767. unsigned int vector(const unsigned int siz, const double value) { // Insert new initialized vector
  18768. const unsigned int pos = vector(siz);
  18769. double *ptr = &mem[pos] + 1;
  18770. for (unsigned int i = 0; i<siz; ++i) *(ptr++) = value;
  18771. return pos;
  18772. }
  18773. unsigned int vector_copy(const unsigned int arg) { // Insert new copy of specified vector in memory
  18774. const unsigned int
  18775. siz = _cimg_mp_size(arg),
  18776. pos = vector(siz);
  18777. CImg<ulongT>::vector((ulongT)mp_vector_copy,pos,arg,siz).move_to(code);
  18778. return pos;
  18779. }
  18780. void self_vector_s(const unsigned int pos, const mp_func op, const unsigned int arg1) {
  18781. const unsigned int siz = _cimg_mp_size(pos);
  18782. if (siz>24) CImg<ulongT>::vector((ulongT)mp_self_map_vector_s,pos,siz,(ulongT)op,arg1).move_to(code);
  18783. else {
  18784. code.insert(siz);
  18785. for (unsigned int k = 1; k<=siz; ++k)
  18786. CImg<ulongT>::vector((ulongT)op,pos + k,arg1).move_to(code[code._width - 1 - siz + k]);
  18787. }
  18788. }
  18789. void self_vector_v(const unsigned int pos, const mp_func op, const unsigned int arg1) {
  18790. const unsigned int siz = _cimg_mp_size(pos);
  18791. if (siz>24) CImg<ulongT>::vector((ulongT)mp_self_map_vector_v,pos,siz,(ulongT)op,arg1).move_to(code);
  18792. else {
  18793. code.insert(siz);
  18794. for (unsigned int k = 1; k<=siz; ++k)
  18795. CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k).move_to(code[code._width - 1 - siz + k]);
  18796. }
  18797. }
  18798. unsigned int vector1_v(const mp_func op, const unsigned int arg1) {
  18799. const unsigned int
  18800. siz = _cimg_mp_size(arg1),
  18801. pos = is_comp_vector(arg1)?arg1:vector(siz);
  18802. if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_v,pos,siz,(ulongT)op,arg1).move_to(code);
  18803. else {
  18804. code.insert(siz);
  18805. for (unsigned int k = 1; k<=siz; ++k)
  18806. CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k).move_to(code[code._width - 1 - siz + k]);
  18807. }
  18808. return pos;
  18809. }
  18810. unsigned int vector2_vv(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
  18811. const unsigned int
  18812. siz = _cimg_mp_size(arg1),
  18813. pos = is_comp_vector(arg1)?arg1:is_comp_vector(arg2)?arg2:vector(siz);
  18814. if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_vv,pos,siz,(ulongT)op,arg1,arg2).move_to(code);
  18815. else {
  18816. code.insert(siz);
  18817. for (unsigned int k = 1; k<=siz; ++k)
  18818. CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k,arg2 + k).move_to(code[code._width - 1 - siz + k]);
  18819. }
  18820. return pos;
  18821. }
  18822. unsigned int vector2_vs(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
  18823. const unsigned int
  18824. siz = _cimg_mp_size(arg1),
  18825. pos = is_comp_vector(arg1)?arg1:vector(siz);
  18826. if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_vs,pos,siz,(ulongT)op,arg1,arg2).move_to(code);
  18827. else {
  18828. code.insert(siz);
  18829. for (unsigned int k = 1; k<=siz; ++k)
  18830. CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k,arg2).move_to(code[code._width - 1 - siz + k]);
  18831. }
  18832. return pos;
  18833. }
  18834. unsigned int vector2_sv(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
  18835. const unsigned int
  18836. siz = _cimg_mp_size(arg2),
  18837. pos = is_comp_vector(arg2)?arg2:vector(siz);
  18838. if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_sv,pos,siz,(ulongT)op,arg1,arg2).move_to(code);
  18839. else {
  18840. code.insert(siz);
  18841. for (unsigned int k = 1; k<=siz; ++k)
  18842. CImg<ulongT>::vector((ulongT)op,pos + k,arg1,arg2 + k).move_to(code[code._width - 1 - siz + k]);
  18843. }
  18844. return pos;
  18845. }
  18846. unsigned int vector3_vss(const mp_func op, const unsigned int arg1, const unsigned int arg2,
  18847. const unsigned int arg3) {
  18848. const unsigned int
  18849. siz = _cimg_mp_size(arg1),
  18850. pos = is_comp_vector(arg1)?arg1:vector(siz);
  18851. if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_vss,pos,siz,(ulongT)op,arg1,arg2,arg3).move_to(code);
  18852. else {
  18853. code.insert(siz);
  18854. for (unsigned int k = 1; k<=siz; ++k)
  18855. CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k,arg2,arg3).move_to(code[code._width - 1 - siz + k]);
  18856. }
  18857. return pos;
  18858. }
  18859. // Check if a memory slot is a positive integer constant scalar value.
  18860. // 'mode' can be:
  18861. // { 0=constant | 1=integer constant | 2=positive integer constant | 3=strictly-positive integer constant }
  18862. void check_constant(const unsigned int arg, const unsigned int n_arg,
  18863. const unsigned int mode,
  18864. char *const ss, char *const se, const char saved_char) {
  18865. _cimg_mp_check_type(arg,n_arg,1,0);
  18866. if (!(_cimg_mp_is_constant(arg) &&
  18867. (!mode || (double)(int)mem[arg]==mem[arg]) &&
  18868. (mode<2 || mem[arg]>=(mode==3)))) {
  18869. const char *s_arg = !n_arg?"":n_arg==1?"First ":n_arg==2?"Second ":n_arg==3?"Third ":
  18870. n_arg==4?"Fourth ":n_arg==5?"Fifth ":n_arg==6?"Sixth ":n_arg==7?"Seventh ":n_arg==8?"Eighth ":
  18871. n_arg==9?"Ninth ":"One of the ";
  18872. *se = saved_char;
  18873. char *const s0 = ss - 4>expr._data?ss - 4:expr._data;
  18874. cimg::strellipsize(s0,64);
  18875. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  18876. "CImg<%s>::%s: %s%s %s%s (of type '%s') is not a%s constant, "
  18877. "in expression '%s%s%s'.",
  18878. pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
  18879. s_arg,*s_arg?"argument":"Argument",s_type(arg)._data,
  18880. !mode?"":mode==1?"n integer":
  18881. mode==2?" positive integer":" strictly positive integer",
  18882. s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
  18883. }
  18884. }
  18885. // Check a matrix is square.
  18886. void check_matrix_square(const unsigned int arg, const unsigned int n_arg,
  18887. char *const ss, char *const se, const char saved_char) {
  18888. _cimg_mp_check_type(arg,n_arg,2,0);
  18889. const unsigned int
  18890. siz = _cimg_mp_size(arg),
  18891. n = (unsigned int)cimg::round(std::sqrt((float)siz));
  18892. if (n*n!=siz) {
  18893. const char *s_arg;
  18894. if (*s_op!='F') s_arg = !n_arg?"":n_arg==1?"Left-hand ":"Right-hand ";
  18895. else s_arg = !n_arg?"":n_arg==1?"First ":n_arg==2?"Second ":n_arg==3?"Third ":"One ";
  18896. *se = saved_char;
  18897. char *const s0 = ss - 4>expr._data?ss - 4:expr._data;
  18898. cimg::strellipsize(s0,64);
  18899. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  18900. "CImg<%s>::%s: %s%s %s%s (of type '%s') "
  18901. "cannot be considered as a square matrix, in expression '%s%s%s'.",
  18902. pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
  18903. s_arg,*s_op=='F'?(*s_arg?"argument":"Argument"):(*s_arg?"operand":"Operand"),
  18904. s_type(arg)._data,
  18905. s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
  18906. }
  18907. }
  18908. // Check type compatibility for one argument.
  18909. // Bits of 'mode' tells what types are allowed:
  18910. // { 1 = scalar | 2 = vectorN }.
  18911. // If 'N' is not zero, it also restricts the vectors to be of size N only.
  18912. void check_type(const unsigned int arg, const unsigned int n_arg,
  18913. const unsigned int mode, const unsigned int N,
  18914. char *const ss, char *const se, const char saved_char) {
  18915. const bool
  18916. is_scalar = _cimg_mp_is_scalar(arg),
  18917. is_vector = _cimg_mp_is_vector(arg) && (!N || _cimg_mp_size(arg)==N);
  18918. bool cond = false;
  18919. if (mode&1) cond|=is_scalar;
  18920. if (mode&2) cond|=is_vector;
  18921. if (!cond) {
  18922. const char *s_arg;
  18923. if (*s_op!='F') s_arg = !n_arg?"":n_arg==1?"Left-hand ":"Right-hand ";
  18924. else s_arg = !n_arg?"":n_arg==1?"First ":n_arg==2?"Second ":n_arg==3?"Third ":
  18925. n_arg==4?"Fourth ":n_arg==5?"Fifth ":n_arg==6?"Sixth ":n_arg==7?"Seventh ":n_arg==8?"Eighth":
  18926. n_arg==9?"Ninth":"One of the ";
  18927. CImg<charT> sb_type(32);
  18928. if (mode==1) cimg_snprintf(sb_type,sb_type._width,"'scalar'");
  18929. else if (mode==2) {
  18930. if (N) cimg_snprintf(sb_type,sb_type._width,"'vector%u'",N);
  18931. else cimg_snprintf(sb_type,sb_type._width,"'vector'");
  18932. } else {
  18933. if (N) cimg_snprintf(sb_type,sb_type._width,"'scalar' or 'vector%u'",N);
  18934. else cimg_snprintf(sb_type,sb_type._width,"'scalar' or 'vector'");
  18935. }
  18936. *se = saved_char;
  18937. char *const s0 = ss - 4>expr._data?ss - 4:expr._data;
  18938. cimg::strellipsize(s0,64);
  18939. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  18940. "CImg<%s>::%s: %s%s %s%s has invalid type '%s' (should be %s), "
  18941. "in expression '%s%s%s'.",
  18942. pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
  18943. s_arg,*s_op=='F'?(*s_arg?"argument":"Argument"):(*s_arg?"operand":"Operand"),
  18944. s_type(arg)._data,sb_type._data,
  18945. s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
  18946. }
  18947. }
  18948. // Check that listin or listout are not empty.
  18949. void check_list(const bool is_out,
  18950. char *const ss, char *const se, const char saved_char) {
  18951. if ((!is_out && !listin) || (is_out && !listout)) {
  18952. *se = saved_char;
  18953. char *const s0 = ss - 4>expr._data?ss - 4:expr._data;
  18954. cimg::strellipsize(s0,64);
  18955. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  18956. "CImg<%s>::%s: %s%s Invalid call with an empty image list, "
  18957. "in expression '%s%s%s'.",
  18958. pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
  18959. s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
  18960. }
  18961. }
  18962. // Check a vector is not 0-dimensional, or with unknown dimension at compile time.
  18963. void check_vector0(const unsigned int dim,
  18964. char *const ss, char *const se, const char saved_char) {
  18965. char *s0 = 0;
  18966. if (!dim) {
  18967. *se = saved_char;
  18968. s0 = ss - 4>expr._data?ss - 4:expr._data;
  18969. cimg::strellipsize(s0,64);
  18970. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  18971. "CImg<%s>::%s: %s%s Invalid construction of a 0-dimensional vector, "
  18972. "in expression '%s%s%s'.",
  18973. pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
  18974. s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
  18975. } else if (dim==~0U) {
  18976. *se = saved_char;
  18977. s0 = ss - 4>expr._data?ss - 4:expr._data;
  18978. cimg::strellipsize(s0,64);
  18979. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  18980. "CImg<%s>::%s: %s%s Invalid construction of a vector with possible dynamic size, "
  18981. "in expression '%s%s%s'.",
  18982. pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
  18983. s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
  18984. }
  18985. }
  18986. // Evaluation functions, known by the parser.
  18987. // Defining these functions 'static' ensures that sizeof(mp_func)==sizeof(ulongT),
  18988. // so we can store pointers to them directly in the opcode vectors.
  18989. #ifdef _mp_arg
  18990. #undef _mp_arg
  18991. #endif
  18992. #define _mp_arg(x) mp.mem[mp.opcode[x]]
  18993. static double mp_abs(_cimg_math_parser& mp) {
  18994. return cimg::abs(_mp_arg(2));
  18995. }
  18996. static double mp_add(_cimg_math_parser& mp) {
  18997. return _mp_arg(2) + _mp_arg(3);
  18998. }
  18999. static double mp_acos(_cimg_math_parser& mp) {
  19000. return std::acos(_mp_arg(2));
  19001. }
  19002. static double mp_arg(_cimg_math_parser& mp) {
  19003. const int _ind = (int)_mp_arg(4);
  19004. const unsigned int
  19005. nb_args = (unsigned int)mp.opcode[2] - 4,
  19006. ind = _ind<0?_ind + nb_args:(unsigned int)_ind,
  19007. siz = (unsigned int)mp.opcode[3];
  19008. if (siz>0) {
  19009. if (ind>=nb_args) std::memset(&_mp_arg(1) + 1,0,siz*sizeof(double));
  19010. else std::memcpy(&_mp_arg(1) + 1,&_mp_arg(ind + 4) + 1,siz*sizeof(double));
  19011. return cimg::type<double>::nan();
  19012. }
  19013. if (ind>=nb_args) return 0;
  19014. return _mp_arg(ind + 4);
  19015. }
  19016. static double mp_argkth(_cimg_math_parser& mp) {
  19017. const unsigned int i_end = (unsigned int)mp.opcode[2];
  19018. const double val = mp_kth(mp);
  19019. for (unsigned int i = 4; i<i_end; ++i) if (val==_mp_arg(i)) return i - 3.0;
  19020. return 1;
  19021. }
  19022. static double mp_argmin(_cimg_math_parser& mp) {
  19023. const unsigned int i_end = (unsigned int)mp.opcode[2];
  19024. double val = _mp_arg(3);
  19025. unsigned int argval = 0;
  19026. for (unsigned int i = 4; i<i_end; ++i) {
  19027. const double _val = _mp_arg(i);
  19028. if (_val<val) { val = _val; argval = i - 3; }
  19029. }
  19030. return (double)argval;
  19031. }
  19032. static double mp_argmax(_cimg_math_parser& mp) {
  19033. const unsigned int i_end = (unsigned int)mp.opcode[2];
  19034. double val = _mp_arg(3);
  19035. unsigned int argval = 0;
  19036. for (unsigned int i = 4; i<i_end; ++i) {
  19037. const double _val = _mp_arg(i);
  19038. if (_val>val) { val = _val; argval = i - 3; }
  19039. }
  19040. return (double)argval;
  19041. }
  19042. static double mp_asin(_cimg_math_parser& mp) {
  19043. return std::asin(_mp_arg(2));
  19044. }
  19045. static double mp_atan(_cimg_math_parser& mp) {
  19046. return std::atan(_mp_arg(2));
  19047. }
  19048. static double mp_atan2(_cimg_math_parser& mp) {
  19049. return std::atan2(_mp_arg(2),_mp_arg(3));
  19050. }
  19051. static double mp_avg(_cimg_math_parser& mp) {
  19052. const unsigned int i_end = (unsigned int)mp.opcode[2];
  19053. double val = _mp_arg(3);
  19054. for (unsigned int i = 4; i<i_end; ++i) val+=_mp_arg(i);
  19055. return val/(i_end - 3);
  19056. }
  19057. static double mp_bitwise_and(_cimg_math_parser& mp) {
  19058. return (double)((longT)_mp_arg(2) & (longT)_mp_arg(3));
  19059. }
  19060. static double mp_bitwise_left_shift(_cimg_math_parser& mp) {
  19061. return (double)((longT)_mp_arg(2)<<(unsigned int)_mp_arg(3));
  19062. }
  19063. static double mp_bitwise_not(_cimg_math_parser& mp) {
  19064. // Limit result to 32bits such that it can be entirely represented as a 'double'.
  19065. return (double)~(unsigned int)_mp_arg(2);
  19066. }
  19067. static double mp_bitwise_or(_cimg_math_parser& mp) {
  19068. return (double)((longT)_mp_arg(2) | (longT)_mp_arg(3));
  19069. }
  19070. static double mp_bitwise_right_shift(_cimg_math_parser& mp) {
  19071. return (double)((longT)_mp_arg(2)>>(unsigned int)_mp_arg(3));
  19072. }
  19073. static double mp_bitwise_xor(_cimg_math_parser& mp) {
  19074. return (double)((longT)_mp_arg(2) ^ (longT)_mp_arg(3));
  19075. }
  19076. static double mp_bool(_cimg_math_parser& mp) {
  19077. return (double)(bool)_mp_arg(2);
  19078. }
  19079. static double mp_break(_cimg_math_parser& mp) {
  19080. mp.break_type = 1;
  19081. mp.p_code = mp.p_break - 1;
  19082. return cimg::type<double>::nan();
  19083. }
  19084. static double mp_breakpoint(_cimg_math_parser& mp) {
  19085. cimg_abort_init;
  19086. cimg_abort_test;
  19087. cimg::unused(mp);
  19088. return cimg::type<double>::nan();
  19089. }
  19090. static double mp_cats(_cimg_math_parser& mp) {
  19091. const double *ptrd = &_mp_arg(1) + 1;
  19092. const unsigned int
  19093. sizd = (unsigned int)mp.opcode[2],
  19094. nb_args = (unsigned int)(mp.opcode[3] - 4)/2;
  19095. CImgList<charT> _str;
  19096. for (unsigned int n = 0; n<nb_args; ++n) {
  19097. const unsigned int siz = (unsigned int)mp.opcode[5 + 2*n];
  19098. if (siz) { // Vector argument
  19099. const double *ptrs = &_mp_arg(4 + 2*n) + 1;
  19100. unsigned int l = 0;
  19101. while (l<siz && ptrs[l]) ++l;
  19102. CImg<doubleT>(ptrs,l,1,1,1,true).move_to(_str);
  19103. } else CImg<charT>::vector((char)_mp_arg(4 + 2*n)).move_to(_str); // Scalar argument
  19104. }
  19105. CImg(1,1,1,1,0).move_to(_str);
  19106. const CImg<charT> str = _str>'x';
  19107. const unsigned int l = std::min(str._width,sizd);
  19108. CImg<doubleT>(ptrd,l,1,1,1,true) = str.get_shared_points(0,l - 1);
  19109. return cimg::type<double>::nan();
  19110. }
  19111. static double mp_cbrt(_cimg_math_parser& mp) {
  19112. return cimg::cbrt(_mp_arg(2));
  19113. }
  19114. static double mp_ceil(_cimg_math_parser& mp) {
  19115. return std::ceil(_mp_arg(2));
  19116. }
  19117. static double mp_complex_abs(_cimg_math_parser& mp) {
  19118. return cimg::_hypot(_mp_arg(2),_mp_arg(3));
  19119. }
  19120. static double mp_complex_conj(_cimg_math_parser& mp) {
  19121. const double *ptrs = &_mp_arg(2) + 1;
  19122. double *ptrd = &_mp_arg(1) + 1;
  19123. *(ptrd++) = *(ptrs++);
  19124. *ptrd = -*(ptrs);
  19125. return cimg::type<double>::nan();
  19126. }
  19127. static double mp_complex_div_sv(_cimg_math_parser& mp) {
  19128. const double
  19129. *ptr2 = &_mp_arg(3) + 1,
  19130. r1 = _mp_arg(2),
  19131. r2 = *(ptr2++), i2 = *ptr2;
  19132. double *ptrd = &_mp_arg(1) + 1;
  19133. const double denom = r2*r2 + i2*i2;
  19134. *(ptrd++) = r1*r2/denom;
  19135. *ptrd = -r1*i2/denom;
  19136. return cimg::type<double>::nan();
  19137. }
  19138. static double mp_complex_div_vv(_cimg_math_parser& mp) {
  19139. const double
  19140. *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1,
  19141. r1 = *(ptr1++), i1 = *ptr1,
  19142. r2 = *(ptr2++), i2 = *ptr2;
  19143. double *ptrd = &_mp_arg(1) + 1;
  19144. const double denom = r2*r2 + i2*i2;
  19145. *(ptrd++) = (r1*r2 + i1*i2)/denom;
  19146. *ptrd = (r2*i1 - r1*i2)/denom;
  19147. return cimg::type<double>::nan();
  19148. }
  19149. static double mp_complex_exp(_cimg_math_parser& mp) {
  19150. double *ptrd = &_mp_arg(1) + 1;
  19151. const double *ptrs = &_mp_arg(2) + 1, r = *(ptrs++), i = *(ptrs), er = std::exp(r);
  19152. *(ptrd++) = er*std::cos(i);
  19153. *(ptrd++) = er*std::sin(i);
  19154. return cimg::type<double>::nan();
  19155. }
  19156. static double mp_complex_log(_cimg_math_parser& mp) {
  19157. double *ptrd = &_mp_arg(1) + 1;
  19158. const double *ptrs = &_mp_arg(2) + 1, r = *(ptrs++), i = *(ptrs);
  19159. *(ptrd++) = 0.5*std::log(r*r + i*i);
  19160. *(ptrd++) = std::atan2(i,r);
  19161. return cimg::type<double>::nan();
  19162. }
  19163. static double mp_complex_mul(_cimg_math_parser& mp) {
  19164. const double
  19165. *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1,
  19166. r1 = *(ptr1++), i1 = *ptr1,
  19167. r2 = *(ptr2++), i2 = *ptr2;
  19168. double *ptrd = &_mp_arg(1) + 1;
  19169. *(ptrd++) = r1*r2 - i1*i2;
  19170. *(ptrd++) = r1*i2 + r2*i1;
  19171. return cimg::type<double>::nan();
  19172. }
  19173. static void _mp_complex_pow(const double r1, const double i1,
  19174. const double r2, const double i2,
  19175. double *ptrd) {
  19176. double ro, io;
  19177. if (cimg::abs(i2)<1e-15) { // Exponent is real
  19178. if (cimg::abs(r1)<1e-15 && cimg::abs(i1)<1e-15) {
  19179. if (cimg::abs(r2)<1e-15) { ro = 1; io = 0; }
  19180. else ro = io = 0;
  19181. } else {
  19182. const double
  19183. mod1_2 = r1*r1 + i1*i1,
  19184. phi1 = std::atan2(i1,r1),
  19185. modo = std::pow(mod1_2,0.5*r2),
  19186. phio = r2*phi1;
  19187. ro = modo*std::cos(phio);
  19188. io = modo*std::sin(phio);
  19189. }
  19190. } else { // Exponent is complex
  19191. if (cimg::abs(r1)<1e-15 && cimg::abs(i1)<1e-15) ro = io = 0;
  19192. const double
  19193. mod1_2 = r1*r1 + i1*i1,
  19194. phi1 = std::atan2(i1,r1),
  19195. modo = std::pow(mod1_2,0.5*r2)*std::exp(-i2*phi1),
  19196. phio = r2*phi1 + 0.5*i2*std::log(mod1_2);
  19197. ro = modo*std::cos(phio);
  19198. io = modo*std::sin(phio);
  19199. }
  19200. *(ptrd++) = ro;
  19201. *ptrd = io;
  19202. }
  19203. static double mp_complex_pow_ss(_cimg_math_parser& mp) {
  19204. const double val1 = _mp_arg(2), val2 = _mp_arg(3);
  19205. double *ptrd = &_mp_arg(1) + 1;
  19206. _mp_complex_pow(val1,0,val2,0,ptrd);
  19207. return cimg::type<double>::nan();
  19208. }
  19209. static double mp_complex_pow_sv(_cimg_math_parser& mp) {
  19210. const double val1 = _mp_arg(2), *ptr2 = &_mp_arg(3) + 1;
  19211. double *ptrd = &_mp_arg(1) + 1;
  19212. _mp_complex_pow(val1,0,ptr2[0],ptr2[1],ptrd);
  19213. return cimg::type<double>::nan();
  19214. }
  19215. static double mp_complex_pow_vs(_cimg_math_parser& mp) {
  19216. const double *ptr1 = &_mp_arg(2) + 1, val2 = _mp_arg(3);
  19217. double *ptrd = &_mp_arg(1) + 1;
  19218. _mp_complex_pow(ptr1[0],ptr1[1],val2,0,ptrd);
  19219. return cimg::type<double>::nan();
  19220. }
  19221. static double mp_complex_pow_vv(_cimg_math_parser& mp) {
  19222. const double *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1;
  19223. double *ptrd = &_mp_arg(1) + 1;
  19224. _mp_complex_pow(ptr1[0],ptr1[1],ptr2[0],ptr2[1],ptrd);
  19225. return cimg::type<double>::nan();
  19226. }
  19227. static double mp_continue(_cimg_math_parser& mp) {
  19228. mp.break_type = 2;
  19229. mp.p_code = mp.p_break - 1;
  19230. return cimg::type<double>::nan();
  19231. }
  19232. static double mp_cos(_cimg_math_parser& mp) {
  19233. return std::cos(_mp_arg(2));
  19234. }
  19235. static double mp_cosh(_cimg_math_parser& mp) {
  19236. return std::cosh(_mp_arg(2));
  19237. }
  19238. static double mp_critical(_cimg_math_parser& mp) {
  19239. const double res = _mp_arg(1);
  19240. cimg_pragma_openmp(critical(mp_critical))
  19241. {
  19242. for (const CImg<ulongT> *const p_end = ++mp.p_code + mp.opcode[2];
  19243. mp.p_code<p_end; ++mp.p_code) { // Evaluate body
  19244. mp.opcode._data = mp.p_code->_data;
  19245. const ulongT target = mp.opcode[1];
  19246. mp.mem[target] = _cimg_mp_defunc(mp);
  19247. }
  19248. }
  19249. --mp.p_code;
  19250. return res;
  19251. }
  19252. static double mp_crop(_cimg_math_parser& mp) {
  19253. double *ptrd = &_mp_arg(1) + 1;
  19254. const int x = (int)_mp_arg(3), y = (int)_mp_arg(4), z = (int)_mp_arg(5), c = (int)_mp_arg(6);
  19255. const unsigned int
  19256. dx = (unsigned int)mp.opcode[7],
  19257. dy = (unsigned int)mp.opcode[8],
  19258. dz = (unsigned int)mp.opcode[9],
  19259. dc = (unsigned int)mp.opcode[10];
  19260. const bool boundary_conditions = (bool)_mp_arg(11);
  19261. unsigned int ind = (unsigned int)mp.opcode[2];
  19262. if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
  19263. const CImg<T> &img = ind==~0U?mp.imgin:mp.listin[ind];
  19264. if (!img) std::memset(ptrd,0,dx*dy*dz*dc*sizeof(double));
  19265. else CImg<doubleT>(ptrd,dx,dy,dz,dc,true) = img.get_crop(x,y,z,c,
  19266. x + dx - 1,y + dy - 1,
  19267. z + dz - 1,c + dc - 1,
  19268. boundary_conditions);
  19269. return cimg::type<double>::nan();
  19270. }
  19271. static double mp_cross(_cimg_math_parser& mp) {
  19272. CImg<doubleT>
  19273. vout(&_mp_arg(1) + 1,1,3,1,1,true),
  19274. v1(&_mp_arg(2) + 1,1,3,1,1,true),
  19275. v2(&_mp_arg(3) + 1,1,3,1,1,true);
  19276. (vout = v1).cross(v2);
  19277. return cimg::type<double>::nan();
  19278. }
  19279. static double mp_cut(_cimg_math_parser& mp) {
  19280. double val = _mp_arg(2), cmin = _mp_arg(3), cmax = _mp_arg(4);
  19281. return val<cmin?cmin:val>cmax?cmax:val;
  19282. }
  19283. static double mp_date(_cimg_math_parser& mp) {
  19284. const unsigned int
  19285. _arg = (unsigned int)mp.opcode[3],
  19286. _siz = (unsigned int)mp.opcode[4],
  19287. siz = _siz?_siz:1;
  19288. const double *const arg_in = _arg==~0U?0:&_mp_arg(3) + (_siz?1:0);
  19289. double *const arg_out = &_mp_arg(1) + (_siz?1:0);
  19290. if (arg_in) std::memcpy(arg_out,arg_in,siz*sizeof(double));
  19291. else for (unsigned int i = 0; i<siz; ++i) arg_out[i] = i;
  19292. CImg<charT> filename(mp.opcode[2] - 5);
  19293. if (filename) {
  19294. const ulongT *ptrs = mp.opcode._data + 5;
  19295. cimg_for(filename,ptrd,char) *ptrd = (char)*(ptrs++);
  19296. cimg::fdate(filename,arg_out,siz);
  19297. } else cimg::date(arg_out,siz);
  19298. return _siz?cimg::type<double>::nan():*arg_out;
  19299. }
  19300. static double mp_debug(_cimg_math_parser& mp) {
  19301. CImg<charT> expr(mp.opcode[2] - 4);
  19302. const ulongT *ptrs = mp.opcode._data + 4;
  19303. cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++);
  19304. cimg::strellipsize(expr);
  19305. const ulongT g_target = mp.opcode[1];
  19306. #ifndef cimg_use_openmp
  19307. const unsigned int n_thread = 0;
  19308. #else
  19309. const unsigned int n_thread = omp_get_thread_num();
  19310. #endif
  19311. cimg_pragma_openmp(critical(mp_debug))
  19312. {
  19313. std::fprintf(cimg::output(),
  19314. "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c"
  19315. "Start debugging expression '%s', code length %u -> mem[%u] (memsize: %u)",
  19316. (void*)&mp,n_thread,mp.debug_indent,' ',
  19317. expr._data,(unsigned int)mp.opcode[3],(unsigned int)g_target,mp.mem._width);
  19318. std::fflush(cimg::output());
  19319. mp.debug_indent+=3;
  19320. }
  19321. const CImg<ulongT> *const p_end = (++mp.p_code) + mp.opcode[3];
  19322. CImg<ulongT> _op;
  19323. for ( ; mp.p_code<p_end; ++mp.p_code) {
  19324. const CImg<ulongT> &op = *mp.p_code;
  19325. mp.opcode._data = op._data;
  19326. _op.assign(1,op._height - 1);
  19327. const ulongT *ptrs = op._data + 1;
  19328. for (ulongT *ptrd = _op._data, *const ptrde = _op._data + _op._height; ptrd<ptrde; ++ptrd)
  19329. *ptrd = *(ptrs++);
  19330. const ulongT target = mp.opcode[1];
  19331. mp.mem[target] = _cimg_mp_defunc(mp);
  19332. cimg_pragma_openmp(critical(mp_debug))
  19333. {
  19334. std::fprintf(cimg::output(),
  19335. "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c"
  19336. "Opcode %p = [ %p,%s ] -> mem[%u] = %g",
  19337. (void*)&mp,n_thread,mp.debug_indent,' ',
  19338. (void*)mp.opcode._data,(void*)*mp.opcode,_op.value_string().data(),
  19339. (unsigned int)target,mp.mem[target]);
  19340. std::fflush(cimg::output());
  19341. }
  19342. }
  19343. cimg_pragma_openmp(critical(mp_debug))
  19344. {
  19345. mp.debug_indent-=3;
  19346. std::fprintf(cimg::output(),
  19347. "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c"
  19348. "End debugging expression '%s' -> mem[%u] = %g (memsize: %u)",
  19349. (void*)&mp,n_thread,mp.debug_indent,' ',
  19350. expr._data,(unsigned int)g_target,mp.mem[g_target],mp.mem._width);
  19351. std::fflush(cimg::output());
  19352. }
  19353. --mp.p_code;
  19354. return mp.mem[g_target];
  19355. }
  19356. static double mp_decrement(_cimg_math_parser& mp) {
  19357. return _mp_arg(2) - 1;
  19358. }
  19359. static double mp_det(_cimg_math_parser& mp) {
  19360. const double *ptrs = &_mp_arg(2) + 1;
  19361. const unsigned int k = (unsigned int)mp.opcode[3];
  19362. return CImg<doubleT>(ptrs,k,k,1,1,true).det();
  19363. }
  19364. static double mp_diag(_cimg_math_parser& mp) {
  19365. double *ptrd = &_mp_arg(1) + 1;
  19366. const double *ptrs = &_mp_arg(2) + 1;
  19367. const unsigned int k = (unsigned int)mp.opcode[3];
  19368. CImg<doubleT>(ptrd,k,k,1,1,true) = CImg<doubleT>(ptrs,1,k,1,1,true).get_diagonal();
  19369. return cimg::type<double>::nan();
  19370. }
  19371. static double mp_display_memory(_cimg_math_parser& mp) {
  19372. cimg::unused(mp);
  19373. std::fputc('\n',cimg::output());
  19374. mp.mem.display("[" cimg_appname "_math_parser] Memory snapshot");
  19375. return cimg::type<double>::nan();
  19376. }
  19377. static double mp_display(_cimg_math_parser& mp) {
  19378. const unsigned int
  19379. _siz = (unsigned int)mp.opcode[3],
  19380. siz = _siz?_siz:1;
  19381. const double *const ptr = &_mp_arg(1) + (_siz?1:0);
  19382. const int
  19383. w = (int)_mp_arg(4),
  19384. h = (int)_mp_arg(5),
  19385. d = (int)_mp_arg(6),
  19386. s = (int)_mp_arg(7);
  19387. CImg<doubleT> img;
  19388. if (w>0 && h>0 && d>0 && s>0) {
  19389. if ((unsigned int)w*h*d*s<=siz) img.assign(ptr,w,h,d,s,true);
  19390. else img.assign(ptr,siz).resize(w,h,d,s,-1);
  19391. } else img.assign(ptr,1,siz,1,1,true);
  19392. CImg<charT> expr(mp.opcode[2] - 8);
  19393. const ulongT *ptrs = mp.opcode._data + 8;
  19394. cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++);
  19395. ((CImg<charT>::string("[" cimg_appname "_math_parser] ",false,true),expr)>'x').move_to(expr);
  19396. cimg::strellipsize(expr);
  19397. std::fputc('\n',cimg::output());
  19398. img.display(expr._data);
  19399. return cimg::type<double>::nan();
  19400. }
  19401. static double mp_div(_cimg_math_parser& mp) {
  19402. return _mp_arg(2)/_mp_arg(3);
  19403. }
  19404. static double mp_dot(_cimg_math_parser& mp) {
  19405. const unsigned int siz = (unsigned int)mp.opcode[4];
  19406. return CImg<doubleT>(&_mp_arg(2) + 1,1,siz,1,1,true).
  19407. dot(CImg<doubleT>(&_mp_arg(3) + 1,1,siz,1,1,true));
  19408. }
  19409. static double mp_dowhile(_cimg_math_parser& mp) {
  19410. const ulongT
  19411. mem_body = mp.opcode[1],
  19412. mem_cond = mp.opcode[2];
  19413. const CImg<ulongT>
  19414. *const p_body = ++mp.p_code,
  19415. *const p_cond = p_body + mp.opcode[3],
  19416. *const p_end = p_cond + mp.opcode[4];
  19417. const unsigned int vsiz = (unsigned int)mp.opcode[5];
  19418. if (mp.opcode[6]) { // Set default value for result and condition if necessary
  19419. if (vsiz) CImg<doubleT>(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type<double>::nan());
  19420. else mp.mem[mem_body] = cimg::type<double>::nan();
  19421. }
  19422. if (mp.opcode[7]) mp.mem[mem_cond] = 0;
  19423. const unsigned int _break_type = mp.break_type;
  19424. mp.break_type = 0;
  19425. do {
  19426. for (mp.p_code = p_body; mp.p_code<p_cond; ++mp.p_code) { // Evaluate body
  19427. mp.opcode._data = mp.p_code->_data;
  19428. const ulongT target = mp.opcode[1];
  19429. mp.mem[target] = _cimg_mp_defunc(mp);
  19430. }
  19431. if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
  19432. for (mp.p_code = p_cond; mp.p_code<p_end; ++mp.p_code) { // Evaluate condition
  19433. mp.opcode._data = mp.p_code->_data;
  19434. const ulongT target = mp.opcode[1];
  19435. mp.mem[target] = _cimg_mp_defunc(mp);
  19436. }
  19437. if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
  19438. } while (mp.mem[mem_cond]);
  19439. mp.break_type = _break_type;
  19440. mp.p_code = p_end - 1;
  19441. return mp.mem[mem_body];
  19442. }
  19443. static double mp_draw(_cimg_math_parser& mp) {
  19444. const int x = (int)_mp_arg(4), y = (int)_mp_arg(5), z = (int)_mp_arg(6), c = (int)_mp_arg(7);
  19445. unsigned int ind = (unsigned int)mp.opcode[3];
  19446. if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.listin.width());
  19447. CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
  19448. unsigned int
  19449. dx = (unsigned int)mp.opcode[8],
  19450. dy = (unsigned int)mp.opcode[9],
  19451. dz = (unsigned int)mp.opcode[10],
  19452. dc = (unsigned int)mp.opcode[11];
  19453. dx = dx==~0U?img._width:(unsigned int)_mp_arg(8);
  19454. dy = dy==~0U?img._height:(unsigned int)_mp_arg(9);
  19455. dz = dz==~0U?img._depth:(unsigned int)_mp_arg(10);
  19456. dc = dc==~0U?img._spectrum:(unsigned int)_mp_arg(11);
  19457. const ulongT sizS = mp.opcode[2];
  19458. if (sizS<(ulongT)dx*dy*dz*dc)
  19459. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': "
  19460. "Sprite dimension (%lu values) and specified sprite geometry (%u,%u,%u,%u) "
  19461. "(%lu values) do not match.",
  19462. mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc);
  19463. CImg<doubleT> S(&_mp_arg(1) + 1,dx,dy,dz,dc,true);
  19464. const float opacity = (float)_mp_arg(12);
  19465. if (img._data) {
  19466. if (mp.opcode[13]!=~0U) { // Opacity mask specified
  19467. const ulongT sizM = mp.opcode[14];
  19468. if (sizM<(ulongT)dx*dy*dz)
  19469. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': "
  19470. "Mask dimension (%lu values) and specified sprite geometry (%u,%u,%u,%u) "
  19471. "(%lu values) do not match.",
  19472. mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc);
  19473. const CImg<doubleT> M(&_mp_arg(13) + 1,dx,dy,dz,(unsigned int)(sizM/(dx*dy*dz)),true);
  19474. img.draw_image(x,y,z,c,S,M,opacity,(float)_mp_arg(15));
  19475. } else img.draw_image(x,y,z,c,S,opacity);
  19476. }
  19477. return cimg::type<double>::nan();
  19478. }
  19479. static double mp_echo(_cimg_math_parser& mp) {
  19480. const unsigned int nb_args = (unsigned int)(mp.opcode[2] - 3)/2;
  19481. CImgList<charT> _str;
  19482. CImg<charT> it;
  19483. for (unsigned int n = 0; n<nb_args; ++n) {
  19484. const unsigned int siz = (unsigned int)mp.opcode[4 + 2*n];
  19485. if (siz) { // Vector argument -> string
  19486. const double *ptr = &_mp_arg(3 + 2*n) + 1;
  19487. unsigned int l = 0;
  19488. while (l<siz && ptr[l]) ++l;
  19489. CImg<doubleT>(ptr,l,1,1,1,true).move_to(_str);
  19490. } else { // Scalar argument -> number
  19491. it.assign(256);
  19492. cimg_snprintf(it,it._width,"%.17g",_mp_arg(3 + 2*n));
  19493. CImg<charT>::string(it,false,true).move_to(_str);
  19494. }
  19495. }
  19496. CImg(1,1,1,1,0).move_to(_str);
  19497. const CImg<charT> str = _str>'x';
  19498. std::fprintf(cimg::output(),"\n%s",str._data);
  19499. return cimg::type<double>::nan();
  19500. }
  19501. static double mp_ellipse(_cimg_math_parser& mp) {
  19502. const unsigned int i_end = (unsigned int)mp.opcode[2];
  19503. unsigned int ind = (unsigned int)mp.opcode[3];
  19504. if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.listin.width());
  19505. CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
  19506. CImg<T> color(img._spectrum,1,1,1,0);
  19507. bool is_invalid_arguments = false;
  19508. unsigned int i = 4;
  19509. float r1, r2, angle = 0, opacity = 1;
  19510. int x0, y0;
  19511. if (i>=i_end) is_invalid_arguments = true;
  19512. else {
  19513. x0 = (int)cimg::round(_mp_arg(i++));
  19514. if (i>=i_end) is_invalid_arguments = true;
  19515. else {
  19516. y0 = (int)cimg::round(_mp_arg(i++));
  19517. if (i>=i_end) is_invalid_arguments = true;
  19518. else {
  19519. r1 = (float)_mp_arg(i++);
  19520. if (i>=i_end) r2 = r1;
  19521. else {
  19522. r2 = (float)_mp_arg(i++);
  19523. if (i<i_end) {
  19524. angle = (float)_mp_arg(i++);
  19525. if (i<i_end) {
  19526. opacity = (float)_mp_arg(i++);
  19527. if (i<i_end) {
  19528. cimg_forX(color,k) if (i<i_end) color[k] = (T)_mp_arg(i++);
  19529. else { color.resize(k,1,1,1,-1); break; }
  19530. color.resize(img._spectrum,1,1,1,0,2);
  19531. }
  19532. }
  19533. }
  19534. }
  19535. }
  19536. }
  19537. }
  19538. if (!is_invalid_arguments) img.draw_ellipse(x0,y0,r1,r2,angle,color._data,opacity);
  19539. else {
  19540. CImg<doubleT> args(i_end - 4);
  19541. cimg_forX(args,k) args[k] = _mp_arg(4 + k);
  19542. if (ind==~0U)
  19543. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'ellipse()': "
  19544. "Invalid arguments '%s'. ",
  19545. mp.imgin.pixel_type(),args.value_string()._data);
  19546. else
  19547. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'ellipse()': "
  19548. "Invalid arguments '#%u%s%s'. ",
  19549. mp.imgin.pixel_type(),ind,args._width?",":"",args.value_string()._data);
  19550. }
  19551. return cimg::type<double>::nan();
  19552. }
  19553. static double mp_eq(_cimg_math_parser& mp) {
  19554. return (double)(_mp_arg(2)==_mp_arg(3));
  19555. }
  19556. static double mp_ext(_cimg_math_parser& mp) {
  19557. const unsigned int nb_args = (unsigned int)(mp.opcode[2] - 3)/2;
  19558. CImgList<charT> _str;
  19559. CImg<charT> it;
  19560. for (unsigned int n = 0; n<nb_args; ++n) {
  19561. const unsigned int siz = (unsigned int)mp.opcode[4 + 2*n];
  19562. if (siz) { // Vector argument -> string
  19563. const double *ptr = &_mp_arg(3 + 2*n) + 1;
  19564. unsigned int l = 0;
  19565. while (l<siz && ptr[l]) ++l;
  19566. CImg<doubleT>(ptr,l,1,1,1,true).move_to(_str);
  19567. } else { // Scalar argument -> number
  19568. it.assign(256);
  19569. cimg_snprintf(it,it._width,"%.17g",_mp_arg(3 + 2*n));
  19570. CImg<charT>::string(it,false,true).move_to(_str);
  19571. }
  19572. }
  19573. CImg(1,1,1,1,0).move_to(_str);
  19574. CImg<charT> str = _str>'x';
  19575. #ifdef cimg_mp_ext_function
  19576. cimg_mp_ext_function(str);
  19577. #endif
  19578. return cimg::type<double>::nan();
  19579. }
  19580. static double mp_exp(_cimg_math_parser& mp) {
  19581. return std::exp(_mp_arg(2));
  19582. }
  19583. static double mp_eye(_cimg_math_parser& mp) {
  19584. double *ptrd = &_mp_arg(1) + 1;
  19585. const unsigned int k = (unsigned int)mp.opcode[2];
  19586. CImg<doubleT>(ptrd,k,k,1,1,true).identity_matrix();
  19587. return cimg::type<double>::nan();
  19588. }
  19589. static double mp_factorial(_cimg_math_parser& mp) {
  19590. return cimg::factorial((int)_mp_arg(2));
  19591. }
  19592. static double mp_fibonacci(_cimg_math_parser& mp) {
  19593. return cimg::fibonacci((int)_mp_arg(2));
  19594. }
  19595. static double mp_find(_cimg_math_parser& mp) {
  19596. const bool is_forward = (bool)_mp_arg(5);
  19597. const ulongT siz = (ulongT)mp.opcode[3];
  19598. longT ind = (longT)(mp.opcode[6]!=_cimg_mp_slot_nan?_mp_arg(6):is_forward?0:siz - 1);
  19599. if (ind<0 || ind>=(longT)siz) return -1.;
  19600. const double
  19601. *const ptrb = &_mp_arg(2) + 1,
  19602. *const ptre = ptrb + siz,
  19603. val = _mp_arg(4),
  19604. *ptr = ptrb + ind;
  19605. // Forward search
  19606. if (is_forward) {
  19607. while (ptr<ptre && *ptr!=val) ++ptr;
  19608. return ptr==ptre?-1.:(double)(ptr - ptrb);
  19609. }
  19610. // Backward search.
  19611. while (ptr>=ptrb && *ptr!=val) --ptr;
  19612. return ptr<ptrb?-1.:(double)(ptr - ptrb);
  19613. }
  19614. static double mp_find_seq(_cimg_math_parser& mp) {
  19615. const bool is_forward = (bool)_mp_arg(6);
  19616. const ulongT
  19617. siz1 = (ulongT)mp.opcode[3],
  19618. siz2 = (ulongT)mp.opcode[5];
  19619. longT ind = (longT)(mp.opcode[7]!=_cimg_mp_slot_nan?_mp_arg(7):is_forward?0:siz1 - 1);
  19620. if (ind<0 || ind>=(longT)siz1) return -1.;
  19621. const double
  19622. *const ptr1b = &_mp_arg(2) + 1,
  19623. *const ptr1e = ptr1b + siz1,
  19624. *const ptr2b = &_mp_arg(4) + 1,
  19625. *const ptr2e = ptr2b + siz2,
  19626. *ptr1 = ptr1b + ind,
  19627. *p1 = 0,
  19628. *p2 = 0;
  19629. // Forward search.
  19630. if (is_forward) {
  19631. do {
  19632. while (ptr1<ptr1e && *ptr1!=*ptr2b) ++ptr1;
  19633. p1 = ptr1 + 1;
  19634. p2 = ptr2b + 1;
  19635. while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; }
  19636. } while (p2<ptr2e && ++ptr1<ptr1e);
  19637. return p2<ptr2e?-1.0:(double)(ptr1 - ptr1b);
  19638. }
  19639. // Backward search.
  19640. do {
  19641. while (ptr1>=ptr1b && *ptr1!=*ptr2b) --ptr1;
  19642. p1 = ptr1 + 1;
  19643. p2 = ptr2b + 1;
  19644. while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; }
  19645. } while (p2<ptr2e && --ptr1>=ptr1b);
  19646. return p2<ptr2e?-1.0:(double)(ptr1 - ptr1b);
  19647. }
  19648. static double mp_floor(_cimg_math_parser& mp) {
  19649. return std::floor(_mp_arg(2));
  19650. }
  19651. static double mp_for(_cimg_math_parser& mp) {
  19652. const ulongT
  19653. mem_body = mp.opcode[1],
  19654. mem_cond = mp.opcode[3];
  19655. const CImg<ulongT>
  19656. *const p_init = ++mp.p_code,
  19657. *const p_cond = p_init + mp.opcode[4],
  19658. *const p_body = p_cond + mp.opcode[5],
  19659. *const p_post = p_body + mp.opcode[6],
  19660. *const p_end = p_post + mp.opcode[7];
  19661. const unsigned int vsiz = (unsigned int)mp.opcode[2];
  19662. bool is_cond = false;
  19663. if (mp.opcode[8]) { // Set default value for result and condition if necessary
  19664. if (vsiz) CImg<doubleT>(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type<double>::nan());
  19665. else mp.mem[mem_body] = cimg::type<double>::nan();
  19666. }
  19667. if (mp.opcode[9]) mp.mem[mem_cond] = 0;
  19668. const unsigned int _break_type = mp.break_type;
  19669. mp.break_type = 0;
  19670. for (mp.p_code = p_init; mp.p_code<p_cond; ++mp.p_code) { // Evaluate init
  19671. mp.opcode._data = mp.p_code->_data;
  19672. const ulongT target = mp.opcode[1];
  19673. mp.mem[target] = _cimg_mp_defunc(mp);
  19674. }
  19675. if (!mp.break_type) do {
  19676. for (mp.p_code = p_cond; mp.p_code<p_body; ++mp.p_code) { // Evaluate condition
  19677. mp.opcode._data = mp.p_code->_data;
  19678. const ulongT target = mp.opcode[1];
  19679. mp.mem[target] = _cimg_mp_defunc(mp);
  19680. }
  19681. if (mp.break_type==1) break;
  19682. is_cond = (bool)mp.mem[mem_cond];
  19683. if (is_cond && !mp.break_type) {
  19684. for (mp.p_code = p_body; mp.p_code<p_post; ++mp.p_code) { // Evaluate body
  19685. mp.opcode._data = mp.p_code->_data;
  19686. const ulongT target = mp.opcode[1];
  19687. mp.mem[target] = _cimg_mp_defunc(mp);
  19688. }
  19689. if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
  19690. for (mp.p_code = p_post; mp.p_code<p_end; ++mp.p_code) { // Evaluate post-code
  19691. mp.opcode._data = mp.p_code->_data;
  19692. const ulongT target = mp.opcode[1];
  19693. mp.mem[target] = _cimg_mp_defunc(mp);
  19694. }
  19695. if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
  19696. }
  19697. } while (is_cond);
  19698. mp.break_type = _break_type;
  19699. mp.p_code = p_end - 1;
  19700. return mp.mem[mem_body];
  19701. }
  19702. static double mp_fsize(_cimg_math_parser& mp) {
  19703. const CImg<charT> filename(mp.opcode._data + 3,mp.opcode[2] - 3);
  19704. return (double)cimg::fsize(filename);
  19705. }
  19706. static double mp_g(_cimg_math_parser& mp) {
  19707. cimg::unused(mp);
  19708. return cimg::grand();
  19709. }
  19710. static double mp_gauss(_cimg_math_parser& mp) {
  19711. const double x = _mp_arg(2), s = _mp_arg(3);
  19712. return std::exp(-x*x/(2*s*s))/(_mp_arg(4)?std::sqrt(2*s*s*cimg::PI):1);
  19713. }
  19714. static double mp_gcd(_cimg_math_parser& mp) {
  19715. return cimg::gcd((long)_mp_arg(2),(long)_mp_arg(3));
  19716. }
  19717. static double mp_gt(_cimg_math_parser& mp) {
  19718. return (double)(_mp_arg(2)>_mp_arg(3));
  19719. }
  19720. static double mp_gte(_cimg_math_parser& mp) {
  19721. return (double)(_mp_arg(2)>=_mp_arg(3));
  19722. }
  19723. static double mp_i(_cimg_math_parser& mp) {
  19724. return (double)mp.imgin.atXYZC((int)mp.mem[_cimg_mp_slot_x],(int)mp.mem[_cimg_mp_slot_y],
  19725. (int)mp.mem[_cimg_mp_slot_z],(int)mp.mem[_cimg_mp_slot_c],(T)0);
  19726. }
  19727. static double mp_if(_cimg_math_parser& mp) {
  19728. const bool is_cond = (bool)_mp_arg(2);
  19729. const ulongT
  19730. mem_left = mp.opcode[3],
  19731. mem_right = mp.opcode[4];
  19732. const CImg<ulongT>
  19733. *const p_right = ++mp.p_code + mp.opcode[5],
  19734. *const p_end = p_right + mp.opcode[6];
  19735. const unsigned int vtarget = (unsigned int)mp.opcode[1], vsiz = (unsigned int)mp.opcode[7];
  19736. if (is_cond) for ( ; mp.p_code<p_right; ++mp.p_code) {
  19737. mp.opcode._data = mp.p_code->_data;
  19738. const ulongT target = mp.opcode[1];
  19739. mp.mem[target] = _cimg_mp_defunc(mp);
  19740. }
  19741. else for (mp.p_code = p_right; mp.p_code<p_end; ++mp.p_code) {
  19742. mp.opcode._data = mp.p_code->_data;
  19743. const ulongT target = mp.opcode[1];
  19744. mp.mem[target] = _cimg_mp_defunc(mp);
  19745. }
  19746. if (mp.p_code==mp.p_break) --mp.p_code;
  19747. else mp.p_code = p_end - 1;
  19748. if (vsiz) std::memcpy(&mp.mem[vtarget] + 1,&mp.mem[is_cond?mem_left:mem_right] + 1,sizeof(double)*vsiz);
  19749. return mp.mem[is_cond?mem_left:mem_right];
  19750. }
  19751. static double mp_image_d(_cimg_math_parser& mp) {
  19752. unsigned int ind = (unsigned int)mp.opcode[2];
  19753. if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
  19754. const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
  19755. return (double)img.depth();
  19756. }
  19757. static double mp_image_display(_cimg_math_parser& mp) {
  19758. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width());
  19759. cimg::mutex(6);
  19760. CImg<T> &img = mp.listout[ind];
  19761. CImg<charT> title(256);
  19762. std::fputc('\n',cimg::output());
  19763. cimg_snprintf(title,title._width,"[ Image #%u ]",ind);
  19764. img.display(title);
  19765. cimg::mutex(6,0);
  19766. return cimg::type<double>::nan();
  19767. }
  19768. static double mp_image_h(_cimg_math_parser& mp) {
  19769. unsigned int ind = (unsigned int)mp.opcode[2];
  19770. if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
  19771. const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
  19772. return (double)img.height();
  19773. }
  19774. static double mp_image_median(_cimg_math_parser& mp) {
  19775. unsigned int ind = (unsigned int)mp.opcode[2];
  19776. if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
  19777. const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
  19778. return (double)img.median();
  19779. }
  19780. static double mp_image_print(_cimg_math_parser& mp) {
  19781. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width());
  19782. cimg::mutex(6);
  19783. CImg<T> &img = mp.listout[ind];
  19784. CImg<charT> title(256);
  19785. std::fputc('\n',cimg::output());
  19786. cimg_snprintf(title,title._width,"[ Image #%u ]",ind);
  19787. img.print(title);
  19788. cimg::mutex(6,0);
  19789. return cimg::type<double>::nan();
  19790. }
  19791. static double mp_image_resize(_cimg_math_parser& mp) {
  19792. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width());
  19793. cimg::mutex(6);
  19794. CImg<T> &img = mp.listout[ind];
  19795. const double
  19796. _w = mp.opcode[3]==~0U?-100:_mp_arg(3),
  19797. _h = mp.opcode[4]==~0U?-100:_mp_arg(4),
  19798. _d = mp.opcode[5]==~0U?-100:_mp_arg(5),
  19799. _s = mp.opcode[6]==~0U?-100:_mp_arg(6);
  19800. const unsigned int
  19801. w = (unsigned int)(_w>=0?_w:-_w*img.width()/100),
  19802. h = (unsigned int)(_h>=0?_h:-_h*img.height()/100),
  19803. d = (unsigned int)(_d>=0?_d:-_d*img.depth()/100),
  19804. s = (unsigned int)(_s>=0?_s:-_s*img.spectrum()/100),
  19805. interp = (int)_mp_arg(7);
  19806. if (mp.is_fill && img._data==mp.imgout._data) {
  19807. cimg::mutex(6,0);
  19808. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'resize()': "
  19809. "Cannot both fill and resize image (%u,%u,%u,%u) "
  19810. "to new dimensions (%u,%u,%u,%u).",
  19811. img.pixel_type(),img._width,img._height,img._depth,img._spectrum,w,h,d,s);
  19812. }
  19813. const unsigned int
  19814. boundary = (int)_mp_arg(8);
  19815. const float
  19816. cx = (float)_mp_arg(9),
  19817. cy = (float)_mp_arg(10),
  19818. cz = (float)_mp_arg(11),
  19819. cc = (float)_mp_arg(12);
  19820. img.resize(w,h,d,s,interp,boundary,cx,cy,cz,cc);
  19821. cimg::mutex(6,0);
  19822. return cimg::type<double>::nan();
  19823. }
  19824. static double mp_image_s(_cimg_math_parser& mp) {
  19825. unsigned int ind = (unsigned int)mp.opcode[2];
  19826. if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
  19827. const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
  19828. return (double)img.spectrum();
  19829. }
  19830. static double mp_image_sort(_cimg_math_parser& mp) {
  19831. const bool is_increasing = (bool)_mp_arg(3);
  19832. const unsigned int
  19833. ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width()),
  19834. axis = (unsigned int)_mp_arg(4);
  19835. cimg::mutex(6);
  19836. CImg<T> &img = mp.listout[ind];
  19837. img.sort(is_increasing,
  19838. axis==0 || axis=='x'?'x':
  19839. axis==1 || axis=='y'?'y':
  19840. axis==2 || axis=='z'?'z':
  19841. axis==3 || axis=='c'?'c':0);
  19842. cimg::mutex(6,0);
  19843. return cimg::type<double>::nan();
  19844. }
  19845. static double mp_image_stats(_cimg_math_parser& mp) {
  19846. double *ptrd = &_mp_arg(1) + 1;
  19847. unsigned int ind = (unsigned int)mp.opcode[2];
  19848. if (ind==~0U) CImg<doubleT>(ptrd,14,1,1,1,true) = mp.imgout.get_stats();
  19849. else {
  19850. ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
  19851. CImg<doubleT>(ptrd,14,1,1,1,true) = mp.listout[ind].get_stats();
  19852. }
  19853. return cimg::type<double>::nan();
  19854. }
  19855. static double mp_image_w(_cimg_math_parser& mp) {
  19856. unsigned int ind = (unsigned int)mp.opcode[2];
  19857. if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
  19858. const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
  19859. return (double)img.width();
  19860. }
  19861. static double mp_image_wh(_cimg_math_parser& mp) {
  19862. unsigned int ind = (unsigned int)mp.opcode[2];
  19863. if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
  19864. const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
  19865. return (double)img.width()*img.height();
  19866. }
  19867. static double mp_image_whd(_cimg_math_parser& mp) {
  19868. unsigned int ind = (unsigned int)mp.opcode[2];
  19869. if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
  19870. const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
  19871. return (double)img.width()*img.height()*img.depth();
  19872. }
  19873. static double mp_image_whds(_cimg_math_parser& mp) {
  19874. unsigned int ind = (unsigned int)mp.opcode[2];
  19875. if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
  19876. const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
  19877. return (double)img.width()*img.height()*img.depth()*img.spectrum();
  19878. }
  19879. static double mp_increment(_cimg_math_parser& mp) {
  19880. return _mp_arg(2) + 1;
  19881. }
  19882. static double mp_int(_cimg_math_parser& mp) {
  19883. return (double)(longT)_mp_arg(2);
  19884. }
  19885. static double mp_ioff(_cimg_math_parser& mp) {
  19886. const unsigned int
  19887. boundary_conditions = (unsigned int)_mp_arg(3);
  19888. const CImg<T> &img = mp.imgin;
  19889. const longT
  19890. off = (longT)_mp_arg(2),
  19891. whds = (longT)img.size();
  19892. if (off>=0 && off<whds) return (double)img[off];
  19893. if (img._data) switch (boundary_conditions) {
  19894. case 3 : { // Mirror
  19895. const longT whds2 = 2*whds, moff = cimg::mod(off,whds2);
  19896. return (double)img[moff<whds?moff:whds2 - moff - 1];
  19897. }
  19898. case 2 : // Periodic
  19899. return (double)img[cimg::mod(off,whds)];
  19900. case 1 : // Neumann
  19901. return (double)img[off<0?0:whds - 1];
  19902. default : // Dirichlet
  19903. return 0;
  19904. }
  19905. return 0;
  19906. }
  19907. static double mp_isbool(_cimg_math_parser& mp) {
  19908. const double val = _mp_arg(2);
  19909. return (double)(val==0.0 || val==1.0);
  19910. }
  19911. static double mp_isin(_cimg_math_parser& mp) {
  19912. const unsigned int i_end = (unsigned int)mp.opcode[2];
  19913. const double val = _mp_arg(3);
  19914. for (unsigned int i = 4; i<i_end; ++i)
  19915. if (val==_mp_arg(i)) return 1.0;
  19916. return 0.0;
  19917. }
  19918. static double mp_isinf(_cimg_math_parser& mp) {
  19919. return (double)cimg::type<double>::is_inf(_mp_arg(2));
  19920. }
  19921. static double mp_isint(_cimg_math_parser& mp) {
  19922. return (double)(cimg::mod(_mp_arg(2),1.0)==0);
  19923. }
  19924. static double mp_isnan(_cimg_math_parser& mp) {
  19925. return (double)cimg::type<double>::is_nan(_mp_arg(2));
  19926. }
  19927. static double mp_ixyzc(_cimg_math_parser& mp) {
  19928. const unsigned int
  19929. interpolation = (unsigned int)_mp_arg(6),
  19930. boundary_conditions = (unsigned int)_mp_arg(7);
  19931. const CImg<T> &img = mp.imgin;
  19932. const double
  19933. x = _mp_arg(2), y = _mp_arg(3),
  19934. z = _mp_arg(4), c = _mp_arg(5);
  19935. if (interpolation==0) switch (boundary_conditions) { // Nearest neighbor interpolation
  19936. case 3 : { // Mirror
  19937. const int
  19938. w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(),
  19939. mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2),
  19940. mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2);
  19941. return (double)img(mx<img.width()?mx:w2 - mx - 1,
  19942. my<img.height()?my:h2 - my - 1,
  19943. mz<img.depth()?mz:d2 - mz - 1,
  19944. mc<img.spectrum()?mc:s2 - mc - 1);
  19945. }
  19946. case 2 : // Periodic
  19947. return (double)img(cimg::mod((int)x,img.width()),
  19948. cimg::mod((int)y,img.height()),
  19949. cimg::mod((int)z,img.depth()),
  19950. cimg::mod((int)c,img.spectrum()));
  19951. case 1 : // Neumann
  19952. return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c);
  19953. default : // Dirichlet
  19954. return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0);
  19955. } else switch (boundary_conditions) { // Linear interpolation
  19956. case 3 : { // Mirror
  19957. const float
  19958. w2 = 2.0f*img.width(), h2 = 2.0f*img.height(), d2 = 2.0f*img.depth(), s2 = 2.0f*img.spectrum(),
  19959. mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
  19960. mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
  19961. return (double)img._linear_atXYZC(mx<img.width()?mx:w2 - mx - 1,
  19962. my<img.height()?my:h2 - my - 1,
  19963. mz<img.depth()?mz:d2 - mz - 1,
  19964. mc<img.spectrum()?mc:s2 - mc - 1);
  19965. }
  19966. case 2 : // Periodic
  19967. return (double)img._linear_atXYZC(cimg::mod((float)x,(float)img.width()),
  19968. cimg::mod((float)y,(float)img.height()),
  19969. cimg::mod((float)z,(float)img.depth()),
  19970. cimg::mod((float)c,(float)img.spectrum()));
  19971. case 1 : // Neumann
  19972. return (double)img._linear_atXYZC((float)x,(float)y,(float)z,(float)c);
  19973. default : // Dirichlet
  19974. return (double)img.linear_atXYZC((float)x,(float)y,(float)z,(float)c,(T)0);
  19975. }
  19976. }
  19977. static double mp_joff(_cimg_math_parser& mp) {
  19978. const unsigned int
  19979. boundary_conditions = (unsigned int)_mp_arg(3);
  19980. const int
  19981. ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
  19982. oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
  19983. const CImg<T> &img = mp.imgin;
  19984. const longT
  19985. off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2),
  19986. whds = (longT)img.size();
  19987. if (off>=0 && off<whds) return (double)img[off];
  19988. if (img._data) switch (boundary_conditions) {
  19989. case 3 : { // Mirror
  19990. const longT whds2 = 2*whds, moff = cimg::mod(off,whds2);
  19991. return (double)img[moff<whds?moff:whds2 - moff - 1];
  19992. }
  19993. case 2 : // Periodic
  19994. return (double)img[cimg::mod(off,whds)];
  19995. case 1 : // Neumann
  19996. return (double)img[off<0?0:whds - 1];
  19997. default : // Dirichlet
  19998. return 0;
  19999. }
  20000. return 0;
  20001. }
  20002. static double mp_jxyzc(_cimg_math_parser& mp) {
  20003. const unsigned int
  20004. interpolation = (unsigned int)_mp_arg(6),
  20005. boundary_conditions = (unsigned int)_mp_arg(7);
  20006. const CImg<T> &img = mp.imgin;
  20007. const double
  20008. ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y],
  20009. oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c],
  20010. x = ox + _mp_arg(2), y = oy + _mp_arg(3),
  20011. z = oz + _mp_arg(4), c = oc + _mp_arg(5);
  20012. if (interpolation==0) switch (boundary_conditions) { // Nearest neighbor interpolation
  20013. case 3 : { // Mirror
  20014. const int
  20015. w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(),
  20016. mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2),
  20017. mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2);
  20018. return (double)img(mx<img.width()?mx:w2 - mx - 1,
  20019. my<img.height()?my:h2 - my - 1,
  20020. mz<img.depth()?mz:d2 - mz - 1,
  20021. mc<img.spectrum()?mc:s2 - mc - 1);
  20022. }
  20023. case 2 : // Periodic
  20024. return (double)img(cimg::mod((int)x,img.width()),
  20025. cimg::mod((int)y,img.height()),
  20026. cimg::mod((int)z,img.depth()),
  20027. cimg::mod((int)c,img.spectrum()));
  20028. case 1 : // Neumann
  20029. return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c);
  20030. default : // Dirichlet
  20031. return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0);
  20032. } else switch (boundary_conditions) { // Linear interpolation
  20033. case 3 : { // Mirror
  20034. const float
  20035. w2 = 2.0f*img.width(), h2 = 2.0f*img.height(), d2 = 2.0f*img.depth(), s2 = 2.0f*img.spectrum(),
  20036. mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
  20037. mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
  20038. return (double)img._linear_atXYZC(mx<img.width()?mx:w2 - mx - 1,
  20039. my<img.height()?my:h2 - my - 1,
  20040. mz<img.depth()?mz:d2 - mz - 1,
  20041. mc<img.spectrum()?mc:s2 - mc - 1);
  20042. }
  20043. case 2 : // Periodic
  20044. return (double)img._linear_atXYZC(cimg::mod((float)x,(float)img.width()),
  20045. cimg::mod((float)y,(float)img.height()),
  20046. cimg::mod((float)z,(float)img.depth()),
  20047. cimg::mod((float)c,(float)img.spectrum()));
  20048. case 1 : // Neumann
  20049. return (double)img._linear_atXYZC((float)x,(float)y,(float)z,(float)c);
  20050. default : // Dirichlet
  20051. return (double)img.linear_atXYZC((float)x,(float)y,(float)z,(float)c,(T)0);
  20052. }
  20053. }
  20054. static double mp_kth(_cimg_math_parser& mp) {
  20055. const unsigned int i_end = (unsigned int)mp.opcode[2];
  20056. CImg<doubleT> vals(i_end - 4);
  20057. double *p = vals.data();
  20058. for (unsigned int i = 4; i<i_end; ++i) *(p++) = _mp_arg(i);
  20059. int ind = (int)cimg::round(_mp_arg(3));
  20060. if (ind<0) ind+=vals.width() + 1;
  20061. ind = std::max(1,std::min(vals.width(),ind));
  20062. return vals.kth_smallest(ind - 1);
  20063. }
  20064. static double mp_linear_add(_cimg_math_parser& mp) {
  20065. return _mp_arg(2)*_mp_arg(3) + _mp_arg(4);
  20066. }
  20067. static double mp_linear_sub_left(_cimg_math_parser& mp) {
  20068. return _mp_arg(2)*_mp_arg(3) - _mp_arg(4);
  20069. }
  20070. static double mp_linear_sub_right(_cimg_math_parser& mp) {
  20071. return _mp_arg(4) - _mp_arg(2)*_mp_arg(3);
  20072. }
  20073. static double mp_list_depth(_cimg_math_parser& mp) {
  20074. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
  20075. return (double)mp.listin[ind]._depth;
  20076. }
  20077. static double mp_list_find(_cimg_math_parser& mp) {
  20078. const unsigned int
  20079. indi = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
  20080. const CImg<T> &img = mp.listin[indi];
  20081. const bool is_forward = (bool)_mp_arg(4);
  20082. const ulongT siz = (ulongT)img.size();
  20083. longT ind = (longT)(mp.opcode[5]!=_cimg_mp_slot_nan?_mp_arg(5):is_forward?0:siz - 1);
  20084. if (ind<0 || ind>=(longT)siz) return -1.;
  20085. const T
  20086. *const ptrb = img.data(),
  20087. *const ptre = img.end(),
  20088. *ptr = ptrb + ind;
  20089. const double val = _mp_arg(3);
  20090. // Forward search
  20091. if (is_forward) {
  20092. while (ptr<ptre && (double)*ptr!=val) ++ptr;
  20093. return ptr==ptre?-1.:(double)(ptr - ptrb);
  20094. }
  20095. // Backward search.
  20096. while (ptr>=ptrb && (double)*ptr!=val) --ptr;
  20097. return ptr<ptrb?-1.:(double)(ptr - ptrb);
  20098. }
  20099. static double mp_list_find_seq(_cimg_math_parser& mp) {
  20100. const unsigned int
  20101. indi = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
  20102. const CImg<T> &img = mp.listin[indi];
  20103. const bool is_forward = (bool)_mp_arg(5);
  20104. const ulongT
  20105. siz1 = (ulongT)img.size(),
  20106. siz2 = (ulongT)mp.opcode[4];
  20107. longT ind = (longT)(mp.opcode[6]!=_cimg_mp_slot_nan?_mp_arg(6):is_forward?0:siz1 - 1);
  20108. if (ind<0 || ind>=(longT)siz1) return -1.;
  20109. const T
  20110. *const ptr1b = img.data(),
  20111. *const ptr1e = ptr1b + siz1,
  20112. *ptr1 = ptr1b + ind,
  20113. *p1 = 0;
  20114. const double
  20115. *const ptr2b = &_mp_arg(3) + 1,
  20116. *const ptr2e = ptr2b + siz2,
  20117. *p2 = 0;
  20118. // Forward search.
  20119. if (is_forward) {
  20120. do {
  20121. while (ptr1<ptr1e && *ptr1!=*ptr2b) ++ptr1;
  20122. p1 = ptr1 + 1;
  20123. p2 = ptr2b + 1;
  20124. while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; }
  20125. } while (p2<ptr2e && ++ptr1<ptr1e);
  20126. return p2<ptr2e?-1.0:(double)(ptr1 - ptr1b);
  20127. }
  20128. // Backward search.
  20129. do {
  20130. while (ptr1>=ptr1b && *ptr1!=*ptr2b) --ptr1;
  20131. p1 = ptr1 + 1;
  20132. p2 = ptr2b + 1;
  20133. while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; }
  20134. } while (p2<ptr2e && --ptr1>=ptr1b);
  20135. return p2<ptr2e?-1.0:(double)(ptr1 - ptr1b);
  20136. }
  20137. static double mp_list_height(_cimg_math_parser& mp) {
  20138. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
  20139. return (double)mp.listin[ind]._height;
  20140. }
  20141. static double mp_list_ioff(_cimg_math_parser& mp) {
  20142. const unsigned int
  20143. ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
  20144. boundary_conditions = (unsigned int)_mp_arg(4);
  20145. const CImg<T> &img = mp.listin[ind];
  20146. const longT
  20147. off = (longT)_mp_arg(3),
  20148. whds = (longT)img.size();
  20149. if (off>=0 && off<whds) return (double)img[off];
  20150. if (img._data) switch (boundary_conditions) {
  20151. case 3 : { // Mirror
  20152. const longT whds2 = 2*whds, moff = cimg::mod(off,whds2);
  20153. return (double)img[moff<whds?moff:whds2 - moff - 1];
  20154. }
  20155. case 2 : // Periodic
  20156. return (double)img[cimg::mod(off,whds)];
  20157. case 1 : // Neumann
  20158. return (double)img[off<0?0:whds - 1];
  20159. default : // Dirichlet
  20160. return 0;
  20161. }
  20162. return 0;
  20163. }
  20164. static double mp_list_is_shared(_cimg_math_parser& mp) {
  20165. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
  20166. return (double)mp.listin[ind]._is_shared;
  20167. }
  20168. static double mp_list_ixyzc(_cimg_math_parser& mp) {
  20169. const unsigned int
  20170. ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
  20171. interpolation = (unsigned int)_mp_arg(7),
  20172. boundary_conditions = (unsigned int)_mp_arg(8);
  20173. const CImg<T> &img = mp.listin[ind];
  20174. const double
  20175. x = _mp_arg(3), y = _mp_arg(4),
  20176. z = _mp_arg(5), c = _mp_arg(6);
  20177. if (interpolation==0) switch (boundary_conditions) { // Nearest neighbor interpolation
  20178. case 3 : { // Mirror
  20179. const int
  20180. w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(),
  20181. mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2),
  20182. mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2);
  20183. return (double)img(mx<img.width()?mx:w2 - mx - 1,
  20184. my<img.height()?my:h2 - my - 1,
  20185. mz<img.depth()?mz:d2 - mz - 1,
  20186. mc<img.spectrum()?mc:s2 - mc - 1);
  20187. }
  20188. case 2 : // Periodic
  20189. return (double)img(cimg::mod((int)x,img.width()),
  20190. cimg::mod((int)y,img.height()),
  20191. cimg::mod((int)z,img.depth()),
  20192. cimg::mod((int)c,img.spectrum()));
  20193. case 1 : // Neumann
  20194. return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c);
  20195. default : // Dirichlet
  20196. return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0);
  20197. } else switch (boundary_conditions) { // Linear interpolation
  20198. case 3 : { // Mirror
  20199. const float
  20200. w2 = 2.0f*img.width(), h2 = 2.0f*img.height(), d2 = 2.0f*img.depth(), s2 = 2.0f*img.spectrum(),
  20201. mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
  20202. mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
  20203. return (double)img._linear_atXYZC(mx<img.width()?mx:w2 - mx - 1,
  20204. my<img.height()?my:h2 - my - 1,
  20205. mz<img.depth()?mz:d2 - mz - 1,
  20206. mc<img.spectrum()?mc:s2 - mc - 1);
  20207. }
  20208. case 2 : // Periodic
  20209. return (double)img._linear_atXYZC(cimg::mod((float)x,(float)img.width()),
  20210. cimg::mod((float)y,(float)img.height()),
  20211. cimg::mod((float)z,(float)img.depth()),
  20212. cimg::mod((float)c,(float)img.spectrum()));
  20213. case 1 : // Neumann
  20214. return (double)img._linear_atXYZC((float)x,(float)y,(float)z,(float)c);
  20215. default : // Dirichlet
  20216. return (double)img.linear_atXYZC((float)x,(float)y,(float)z,(float)c,(T)0);
  20217. }
  20218. }
  20219. static double mp_list_joff(_cimg_math_parser& mp) {
  20220. const unsigned int
  20221. ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
  20222. boundary_conditions = (unsigned int)_mp_arg(4);
  20223. const int
  20224. ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
  20225. oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
  20226. const CImg<T> &img = mp.listin[ind];
  20227. const longT
  20228. off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3),
  20229. whds = (longT)img.size();
  20230. if (off>=0 && off<whds) return (double)img[off];
  20231. if (img._data) switch (boundary_conditions) {
  20232. case 3 : { // Mirror
  20233. const longT whds2 = 2*whds, moff = cimg::mod(off,whds2);
  20234. return (double)img[moff<whds?moff:whds2 - moff - 1];
  20235. }
  20236. case 2 : // Periodic
  20237. return (double)img[cimg::mod(off,whds)];
  20238. case 1 : // Neumann
  20239. return (double)img[off<0?0:whds - 1];
  20240. default : // Dirichlet
  20241. return 0;
  20242. }
  20243. return 0;
  20244. }
  20245. static double mp_list_jxyzc(_cimg_math_parser& mp) {
  20246. const unsigned int
  20247. ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
  20248. interpolation = (unsigned int)_mp_arg(7),
  20249. boundary_conditions = (unsigned int)_mp_arg(8);
  20250. const CImg<T> &img = mp.listin[ind];
  20251. const double
  20252. ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y],
  20253. oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c],
  20254. x = ox + _mp_arg(3), y = oy + _mp_arg(4),
  20255. z = oz + _mp_arg(5), c = oc + _mp_arg(6);
  20256. if (interpolation==0) switch (boundary_conditions) { // Nearest neighbor interpolation
  20257. case 3 : { // Mirror
  20258. const int
  20259. w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(),
  20260. mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2),
  20261. mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2);
  20262. return (double)img(mx<img.width()?mx:w2 - mx - 1,
  20263. my<img.height()?my:h2 - my - 1,
  20264. mz<img.depth()?mz:d2 - mz - 1,
  20265. mc<img.spectrum()?mc:s2 - mc - 1);
  20266. }
  20267. case 2 : // Periodic
  20268. return (double)img(cimg::mod((int)x,img.width()),
  20269. cimg::mod((int)y,img.height()),
  20270. cimg::mod((int)z,img.depth()),
  20271. cimg::mod((int)c,img.spectrum()));
  20272. case 1 : // Neumann
  20273. return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c);
  20274. default : // Dirichlet
  20275. return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0);
  20276. } else switch (boundary_conditions) { // Linear interpolation
  20277. case 3 : { // Mirror
  20278. const float
  20279. w2 = 2.0f*img.width(), h2 = 2.0f*img.height(), d2 = 2.0f*img.depth(), s2 = 2.0f*img.spectrum(),
  20280. mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
  20281. mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
  20282. return (double)img._linear_atXYZC(mx<img.width()?mx:w2 - mx - 1,
  20283. my<img.height()?my:h2 - my - 1,
  20284. mz<img.depth()?mz:d2 - mz - 1,
  20285. mc<img.spectrum()?mc:s2 - mc - 1);
  20286. }
  20287. case 2 : // Periodic
  20288. return (double)img._linear_atXYZC(cimg::mod((float)x,(float)img.width()),
  20289. cimg::mod((float)y,(float)img.height()),
  20290. cimg::mod((float)z,(float)img.depth()),
  20291. cimg::mod((float)c,(float)img.spectrum()));
  20292. case 1 : // Neumann
  20293. return (double)img._linear_atXYZC((float)x,(float)y,(float)z,(float)c);
  20294. default : // Dirichlet
  20295. return (double)img.linear_atXYZC((float)x,(float)y,(float)z,(float)c,(T)0);
  20296. }
  20297. }
  20298. static double mp_list_l(_cimg_math_parser& mp) {
  20299. return (double)mp.listout.width();
  20300. }
  20301. static double mp_list_median(_cimg_math_parser& mp) {
  20302. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
  20303. if (!mp.list_median) mp.list_median.assign(mp.listin._width);
  20304. if (!mp.list_median[ind]) CImg<doubleT>::vector(mp.listin[ind].median()).move_to(mp.list_median[ind]);
  20305. return *mp.list_median[ind];
  20306. }
  20307. static double mp_list_set_ioff(_cimg_math_parser& mp) {
  20308. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
  20309. CImg<T> &img = mp.listout[ind];
  20310. const longT
  20311. off = (longT)_mp_arg(3),
  20312. whds = (longT)img.size();
  20313. const double val = _mp_arg(1);
  20314. if (off>=0 && off<whds) img[off] = (T)val;
  20315. return val;
  20316. }
  20317. static double mp_list_set_ixyzc(_cimg_math_parser& mp) {
  20318. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
  20319. CImg<T> &img = mp.listout[ind];
  20320. const int
  20321. x = (int)_mp_arg(3), y = (int)_mp_arg(4),
  20322. z = (int)_mp_arg(5), c = (int)_mp_arg(6);
  20323. const double val = _mp_arg(1);
  20324. if (x>=0 && x<img.width() && y>=0 && y<img.height() &&
  20325. z>=0 && z<img.depth() && c>=0 && c<img.spectrum())
  20326. img(x,y,z,c) = (T)val;
  20327. return val;
  20328. }
  20329. static double mp_list_set_joff(_cimg_math_parser& mp) {
  20330. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
  20331. CImg<T> &img = mp.listout[ind];
  20332. const int
  20333. ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
  20334. oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
  20335. const longT
  20336. off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3),
  20337. whds = (longT)img.size();
  20338. const double val = _mp_arg(1);
  20339. if (off>=0 && off<whds) img[off] = (T)val;
  20340. return val;
  20341. }
  20342. static double mp_list_set_jxyzc(_cimg_math_parser& mp) {
  20343. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
  20344. CImg<T> &img = mp.listout[ind];
  20345. const double
  20346. ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y],
  20347. oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c];
  20348. const int
  20349. x = (int)(ox + _mp_arg(3)), y = (int)(oy + _mp_arg(4)),
  20350. z = (int)(oz + _mp_arg(5)), c = (int)(oc + _mp_arg(6));
  20351. const double val = _mp_arg(1);
  20352. if (x>=0 && x<img.width() && y>=0 && y<img.height() &&
  20353. z>=0 && z<img.depth() && c>=0 && c<img.spectrum())
  20354. img(x,y,z,c) = (T)val;
  20355. return val;
  20356. }
  20357. static double mp_list_set_Ioff_s(_cimg_math_parser& mp) {
  20358. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
  20359. CImg<T> &img = mp.listout[ind];
  20360. const longT
  20361. off = (longT)_mp_arg(3),
  20362. whd = (longT)img.width()*img.height()*img.depth();
  20363. const T val = (T)_mp_arg(1);
  20364. if (off>=0 && off<whd) {
  20365. T *ptrd = &img[off];
  20366. cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
  20367. }
  20368. return _mp_arg(1);
  20369. }
  20370. static double mp_list_set_Ioff_v(_cimg_math_parser& mp) {
  20371. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
  20372. CImg<T> &img = mp.listout[ind];
  20373. const longT
  20374. off = (longT)_mp_arg(3),
  20375. whd = (longT)img.width()*img.height()*img.depth();
  20376. const double *ptrs = &_mp_arg(1) + 1;
  20377. if (off>=0 && off<whd) {
  20378. const unsigned int vsiz = (unsigned int)mp.opcode[4];
  20379. T *ptrd = &img[off];
  20380. cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
  20381. }
  20382. return cimg::type<double>::nan();
  20383. }
  20384. static double mp_list_set_Ixyz_s(_cimg_math_parser& mp) {
  20385. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
  20386. CImg<T> &img = mp.listout[ind];
  20387. const int
  20388. x = (int)_mp_arg(3),
  20389. y = (int)_mp_arg(4),
  20390. z = (int)_mp_arg(5);
  20391. const T val = (T)_mp_arg(1);
  20392. if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
  20393. T *ptrd = &img(x,y,z);
  20394. const ulongT whd = (ulongT)img._width*img._height*img._depth;
  20395. cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
  20396. }
  20397. return _mp_arg(1);
  20398. }
  20399. static double mp_list_set_Ixyz_v(_cimg_math_parser& mp) {
  20400. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
  20401. CImg<T> &img = mp.listout[ind];
  20402. const int
  20403. x = (int)_mp_arg(3),
  20404. y = (int)_mp_arg(4),
  20405. z = (int)_mp_arg(5);
  20406. const double *ptrs = &_mp_arg(1) + 1;
  20407. if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
  20408. const unsigned int vsiz = (unsigned int)mp.opcode[6];
  20409. T *ptrd = &img(x,y,z);
  20410. const ulongT whd = (ulongT)img._width*img._height*img._depth;
  20411. cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
  20412. }
  20413. return cimg::type<double>::nan();
  20414. }
  20415. static double mp_list_set_Joff_s(_cimg_math_parser& mp) {
  20416. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
  20417. CImg<T> &img = mp.listout[ind];
  20418. const int
  20419. ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
  20420. oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
  20421. const longT
  20422. off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3),
  20423. whd = (longT)img.width()*img.height()*img.depth();
  20424. const T val = (T)_mp_arg(1);
  20425. if (off>=0 && off<whd) {
  20426. T *ptrd = &img[off];
  20427. cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
  20428. }
  20429. return _mp_arg(1);
  20430. }
  20431. static double mp_list_set_Joff_v(_cimg_math_parser& mp) {
  20432. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
  20433. CImg<T> &img = mp.listout[ind];
  20434. const int
  20435. ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
  20436. oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
  20437. const longT
  20438. off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3),
  20439. whd = (longT)img.width()*img.height()*img.depth();
  20440. const double *ptrs = &_mp_arg(1) + 1;
  20441. if (off>=0 && off<whd) {
  20442. const unsigned int vsiz = (unsigned int)mp.opcode[4];
  20443. T *ptrd = &img[off];
  20444. cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
  20445. }
  20446. return cimg::type<double>::nan();
  20447. }
  20448. static double mp_list_set_Jxyz_s(_cimg_math_parser& mp) {
  20449. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
  20450. CImg<T> &img = mp.listout[ind];
  20451. const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z];
  20452. const int
  20453. x = (int)(ox + _mp_arg(3)),
  20454. y = (int)(oy + _mp_arg(4)),
  20455. z = (int)(oz + _mp_arg(5));
  20456. const T val = (T)_mp_arg(1);
  20457. if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
  20458. T *ptrd = &img(x,y,z);
  20459. const ulongT whd = (ulongT)img._width*img._height*img._depth;
  20460. cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
  20461. }
  20462. return _mp_arg(1);
  20463. }
  20464. static double mp_list_set_Jxyz_v(_cimg_math_parser& mp) {
  20465. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
  20466. CImg<T> &img = mp.listout[ind];
  20467. const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z];
  20468. const int
  20469. x = (int)(ox + _mp_arg(3)),
  20470. y = (int)(oy + _mp_arg(4)),
  20471. z = (int)(oz + _mp_arg(5));
  20472. const double *ptrs = &_mp_arg(1) + 1;
  20473. if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
  20474. const unsigned int vsiz = (unsigned int)mp.opcode[6];
  20475. T *ptrd = &img(x,y,z);
  20476. const ulongT whd = (ulongT)img._width*img._height*img._depth;
  20477. cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
  20478. }
  20479. return cimg::type<double>::nan();
  20480. }
  20481. static double mp_list_spectrum(_cimg_math_parser& mp) {
  20482. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
  20483. return (double)mp.listin[ind]._spectrum;
  20484. }
  20485. static double mp_list_stats(_cimg_math_parser& mp) {
  20486. const unsigned int
  20487. ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
  20488. k = (unsigned int)mp.opcode[3];
  20489. if (!mp.list_stats) mp.list_stats.assign(mp.listin._width);
  20490. if (!mp.list_stats[ind]) mp.list_stats[ind].assign(1,14,1,1,0).fill(mp.listin[ind].get_stats(),false);
  20491. return mp.list_stats(ind,k);
  20492. }
  20493. static double mp_list_wh(_cimg_math_parser& mp) {
  20494. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
  20495. return (double)mp.listin[ind]._width*mp.listin[ind]._height;
  20496. }
  20497. static double mp_list_whd(_cimg_math_parser& mp) {
  20498. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
  20499. return (double)mp.listin[ind]._width*mp.listin[ind]._height*mp.listin[ind]._depth;
  20500. }
  20501. static double mp_list_whds(_cimg_math_parser& mp) {
  20502. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
  20503. return (double)mp.listin[ind]._width*mp.listin[ind]._height*mp.listin[ind]._depth*mp.listin[ind]._spectrum;
  20504. }
  20505. static double mp_list_width(_cimg_math_parser& mp) {
  20506. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
  20507. return (double)mp.listin[ind]._width;
  20508. }
  20509. static double mp_list_Ioff(_cimg_math_parser& mp) {
  20510. double *ptrd = &_mp_arg(1) + 1;
  20511. const unsigned int
  20512. ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
  20513. boundary_conditions = (unsigned int)_mp_arg(4),
  20514. vsiz = (unsigned int)mp.opcode[5];
  20515. const CImg<T> &img = mp.listin[ind];
  20516. const longT
  20517. off = (longT)_mp_arg(3),
  20518. whd = (longT)img.width()*img.height()*img.depth();
  20519. const T *ptrs;
  20520. if (off>=0 && off<whd) {
  20521. ptrs = &img[off];
  20522. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  20523. return cimg::type<double>::nan();
  20524. }
  20525. if (img._data) switch (boundary_conditions) {
  20526. case 3 : { // Mirror
  20527. const longT whd2 = 2*whd, moff = cimg::mod(off,whd2);
  20528. ptrs = &img[moff<whd?moff:whd2 - moff - 1];
  20529. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  20530. return cimg::type<double>::nan();
  20531. }
  20532. case 2 : // Periodic
  20533. ptrs = &img[cimg::mod(off,whd)];
  20534. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  20535. return cimg::type<double>::nan();
  20536. case 1 : // Neumann
  20537. ptrs = off<0?&img[0]:&img[whd - 1];
  20538. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  20539. return cimg::type<double>::nan();
  20540. default : // Dirichlet
  20541. std::memset(ptrd,0,vsiz*sizeof(double));
  20542. return cimg::type<double>::nan();
  20543. }
  20544. std::memset(ptrd,0,vsiz*sizeof(double));
  20545. return cimg::type<double>::nan();
  20546. }
  20547. static double mp_list_Ixyz(_cimg_math_parser& mp) {
  20548. double *ptrd = &_mp_arg(1) + 1;
  20549. const unsigned int
  20550. ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
  20551. interpolation = (unsigned int)_mp_arg(6),
  20552. boundary_conditions = (unsigned int)_mp_arg(7),
  20553. vsiz = (unsigned int)mp.opcode[8];
  20554. const CImg<T> &img = mp.listin[ind];
  20555. const double x = _mp_arg(3), y = _mp_arg(4), z = _mp_arg(5);
  20556. const ulongT whd = (ulongT)img._width*img._height*img._depth;
  20557. const T *ptrs;
  20558. if (interpolation==0) switch (boundary_conditions) { // Nearest neighbor interpolation
  20559. case 3 : { // Mirror
  20560. const int
  20561. w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(),
  20562. mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2),
  20563. cx = mx<img.width()?mx:w2 - mx - 1,
  20564. cy = my<img.height()?my:h2 - my - 1,
  20565. cz = mz<img.depth()?mz:d2 - mz - 1;
  20566. ptrs = &img(cx,cy,cz);
  20567. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
  20568. } break;
  20569. case 2 : { // Periodic
  20570. const int
  20571. cx = cimg::mod((int)x,img.width()),
  20572. cy = cimg::mod((int)y,img.height()),
  20573. cz = cimg::mod((int)z,img.depth());
  20574. ptrs = &img(cx,cy,cz);
  20575. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
  20576. } break;
  20577. case 1 : { // Neumann
  20578. ptrs = &img._atXYZ((int)x,(int)y,(int)z);
  20579. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
  20580. } break;
  20581. default : // Dirichlet
  20582. if (img.containsXYZC((int)x,(int)y,(int)z)) {
  20583. ptrs = &img((int)x,(int)y,(int)z);
  20584. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
  20585. } else std::memset(ptrd,0,vsiz*sizeof(double));
  20586. } else switch (boundary_conditions) { // Linear interpolation
  20587. case 3 : { // Mirror
  20588. const float
  20589. w2 = 2.0f*img.width(), h2 = 2.0f*img.height(), d2 = 2.0f*img.depth(),
  20590. mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
  20591. cx = mx<img.width()?mx:w2 - mx - 1,
  20592. cy = my<img.height()?my:h2 - my - 1,
  20593. cz = mz<img.depth()?mz:d2 - mz - 1;
  20594. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
  20595. } break;
  20596. case 2 : { // Periodic
  20597. const float
  20598. cx = cimg::mod((float)x,(float)img.width()),
  20599. cy = cimg::mod((float)y,(float)img.height()),
  20600. cz = cimg::mod((float)z,(float)img.depth());
  20601. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
  20602. } break;
  20603. case 1 : // Neumann
  20604. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c);
  20605. break;
  20606. case 0 : // Dirichlet
  20607. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0);
  20608. }
  20609. return cimg::type<double>::nan();
  20610. }
  20611. static double mp_list_Joff(_cimg_math_parser& mp) {
  20612. double *ptrd = &_mp_arg(1) + 1;
  20613. const unsigned int
  20614. ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
  20615. boundary_conditions = (unsigned int)_mp_arg(4),
  20616. vsiz = (unsigned int)mp.opcode[5];
  20617. const int
  20618. ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], oz = (int)mp.mem[_cimg_mp_slot_z];
  20619. const CImg<T> &img = mp.listin[ind];
  20620. const longT
  20621. off = img.offset(ox,oy,oz) + (longT)_mp_arg(3),
  20622. whd = (longT)img.width()*img.height()*img.depth();
  20623. const T *ptrs;
  20624. if (off>=0 && off<whd) {
  20625. ptrs = &img[off];
  20626. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  20627. return cimg::type<double>::nan();
  20628. }
  20629. if (img._data) switch (boundary_conditions) {
  20630. case 3 : { // Mirror
  20631. const longT whd2 = 2*whd, moff = cimg::mod(off,whd2);
  20632. ptrs = &img[moff<whd?moff:whd2 - moff - 1];
  20633. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  20634. return cimg::type<double>::nan();
  20635. }
  20636. case 2 : // Periodic
  20637. ptrs = &img[cimg::mod(off,whd)];
  20638. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  20639. return cimg::type<double>::nan();
  20640. case 1 : // Neumann
  20641. ptrs = off<0?&img[0]:&img[whd - 1];
  20642. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  20643. return cimg::type<double>::nan();
  20644. default : // Dirichlet
  20645. std::memset(ptrd,0,vsiz*sizeof(double));
  20646. return cimg::type<double>::nan();
  20647. }
  20648. std::memset(ptrd,0,vsiz*sizeof(double));
  20649. return cimg::type<double>::nan();
  20650. }
  20651. static double mp_list_Jxyz(_cimg_math_parser& mp) {
  20652. double *ptrd = &_mp_arg(1) + 1;
  20653. const unsigned int
  20654. ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
  20655. interpolation = (unsigned int)_mp_arg(6),
  20656. boundary_conditions = (unsigned int)_mp_arg(7),
  20657. vsiz = (unsigned int)mp.opcode[8];
  20658. const CImg<T> &img = mp.listin[ind];
  20659. const double
  20660. ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z],
  20661. x = ox + _mp_arg(3), y = oy + _mp_arg(4), z = oz + _mp_arg(5);
  20662. const ulongT whd = (ulongT)img._width*img._height*img._depth;
  20663. const T *ptrs;
  20664. if (interpolation==0) switch (boundary_conditions) { // Nearest neighbor interpolation
  20665. case 3 : { // Mirror
  20666. const int
  20667. w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(),
  20668. mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2),
  20669. cx = mx<img.width()?mx:w2 - mx - 1,
  20670. cy = my<img.height()?my:h2 - my - 1,
  20671. cz = mz<img.depth()?mz:d2 - mz - 1;
  20672. ptrs = &img(cx,cy,cz);
  20673. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
  20674. } break;
  20675. case 2 : { // Periodic
  20676. const int
  20677. cx = cimg::mod((int)x,img.width()),
  20678. cy = cimg::mod((int)y,img.height()),
  20679. cz = cimg::mod((int)z,img.depth());
  20680. ptrs = &img(cx,cy,cz);
  20681. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
  20682. } break;
  20683. case 1 : { // Neumann
  20684. ptrs = &img._atXYZ((int)x,(int)y,(int)z);
  20685. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
  20686. } break;
  20687. default : // Dirichlet
  20688. if (img.containsXYZC((int)x,(int)y,(int)z)) {
  20689. ptrs = &img((int)x,(int)y,(int)z);
  20690. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
  20691. } else std::memset(ptrd,0,vsiz*sizeof(double));
  20692. } else switch (boundary_conditions) { // Linear interpolation
  20693. case 3 : { // Mirror
  20694. const float
  20695. w2 = 2.0f*img.width(), h2 = 2.0f*img.height(), d2 = 2.0f*img.depth(),
  20696. mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
  20697. cx = mx<img.width()?mx:w2 - mx - 1,
  20698. cy = my<img.height()?my:h2 - my - 1,
  20699. cz = mz<img.depth()?mz:d2 - mz - 1;
  20700. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
  20701. } break;
  20702. case 2 : { // Periodic
  20703. const float
  20704. cx = cimg::mod((float)x,(float)img.width()),
  20705. cy = cimg::mod((float)y,(float)img.height()),
  20706. cz = cimg::mod((float)z,(float)img.depth());
  20707. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
  20708. } break;
  20709. case 1 : // Neumann
  20710. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c);
  20711. break;
  20712. default : // Dirichlet
  20713. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0);
  20714. }
  20715. return cimg::type<double>::nan();
  20716. }
  20717. static double mp_log(_cimg_math_parser& mp) {
  20718. return std::log(_mp_arg(2));
  20719. }
  20720. static double mp_log10(_cimg_math_parser& mp) {
  20721. return std::log10(_mp_arg(2));
  20722. }
  20723. static double mp_log2(_cimg_math_parser& mp) {
  20724. return cimg::log2(_mp_arg(2));
  20725. }
  20726. static double mp_logical_and(_cimg_math_parser& mp) {
  20727. const bool val_left = (bool)_mp_arg(2);
  20728. const CImg<ulongT> *const p_end = ++mp.p_code + mp.opcode[4];
  20729. if (!val_left) { mp.p_code = p_end - 1; return 0; }
  20730. const ulongT mem_right = mp.opcode[3];
  20731. for ( ; mp.p_code<p_end; ++mp.p_code) {
  20732. mp.opcode._data = mp.p_code->_data;
  20733. const ulongT target = mp.opcode[1];
  20734. mp.mem[target] = _cimg_mp_defunc(mp);
  20735. }
  20736. --mp.p_code;
  20737. return (double)(bool)mp.mem[mem_right];
  20738. }
  20739. static double mp_logical_not(_cimg_math_parser& mp) {
  20740. return (double)!_mp_arg(2);
  20741. }
  20742. static double mp_logical_or(_cimg_math_parser& mp) {
  20743. const bool val_left = (bool)_mp_arg(2);
  20744. const CImg<ulongT> *const p_end = ++mp.p_code + mp.opcode[4];
  20745. if (val_left) { mp.p_code = p_end - 1; return 1; }
  20746. const ulongT mem_right = mp.opcode[3];
  20747. for ( ; mp.p_code<p_end; ++mp.p_code) {
  20748. mp.opcode._data = mp.p_code->_data;
  20749. const ulongT target = mp.opcode[1];
  20750. mp.mem[target] = _cimg_mp_defunc(mp);
  20751. }
  20752. --mp.p_code;
  20753. return (double)(bool)mp.mem[mem_right];
  20754. }
  20755. static double mp_lowercase(_cimg_math_parser& mp) {
  20756. return cimg::lowercase(_mp_arg(2));
  20757. }
  20758. static double mp_lt(_cimg_math_parser& mp) {
  20759. return (double)(_mp_arg(2)<_mp_arg(3));
  20760. }
  20761. static double mp_lte(_cimg_math_parser& mp) {
  20762. return (double)(_mp_arg(2)<=_mp_arg(3));
  20763. }
  20764. static double mp_matrix_eig(_cimg_math_parser& mp) {
  20765. double *ptrd = &_mp_arg(1) + 1;
  20766. const double *ptr1 = &_mp_arg(2) + 1;
  20767. const unsigned int k = (unsigned int)mp.opcode[3];
  20768. CImg<doubleT> val, vec;
  20769. CImg<doubleT>(ptr1,k,k,1,1,true).symmetric_eigen(val,vec);
  20770. CImg<doubleT>(ptrd,1,k,1,1,true) = val;
  20771. CImg<doubleT>(ptrd + k,k,k,1,1,true) = vec.get_transpose();
  20772. return cimg::type<double>::nan();
  20773. }
  20774. static double mp_matrix_inv(_cimg_math_parser& mp) {
  20775. double *ptrd = &_mp_arg(1) + 1;
  20776. const double *ptr1 = &_mp_arg(2) + 1;
  20777. const unsigned int k = (unsigned int)mp.opcode[3];
  20778. CImg<doubleT>(ptrd,k,k,1,1,true) = CImg<doubleT>(ptr1,k,k,1,1,true).get_invert();
  20779. return cimg::type<double>::nan();
  20780. }
  20781. static double mp_matrix_mul(_cimg_math_parser& mp) {
  20782. double *ptrd = &_mp_arg(1) + 1;
  20783. const double
  20784. *ptr1 = &_mp_arg(2) + 1,
  20785. *ptr2 = &_mp_arg(3) + 1;
  20786. const unsigned int
  20787. k = (unsigned int)mp.opcode[4],
  20788. l = (unsigned int)mp.opcode[5],
  20789. m = (unsigned int)mp.opcode[6];
  20790. CImg<doubleT>(ptrd,m,k,1,1,true) = CImg<doubleT>(ptr1,l,k,1,1,true)*CImg<doubleT>(ptr2,m,l,1,1,true);
  20791. return cimg::type<double>::nan();
  20792. }
  20793. static double mp_matrix_pseudoinv(_cimg_math_parser& mp) {
  20794. double *ptrd = &_mp_arg(1) + 1;
  20795. const double *ptr1 = &_mp_arg(2) + 1;
  20796. const unsigned int
  20797. k = (unsigned int)mp.opcode[3],
  20798. l = (unsigned int)mp.opcode[4];
  20799. CImg<doubleT>(ptrd,l,k,1,1,true) = CImg<doubleT>(ptr1,k,l,1,1,true).get_pseudoinvert();
  20800. return cimg::type<double>::nan();
  20801. }
  20802. static double mp_matrix_svd(_cimg_math_parser& mp) {
  20803. double *ptrd = &_mp_arg(1) + 1;
  20804. const double *ptr1 = &_mp_arg(2) + 1;
  20805. const unsigned int
  20806. k = (unsigned int)mp.opcode[3],
  20807. l = (unsigned int)mp.opcode[4];
  20808. CImg<doubleT> U, S, V;
  20809. CImg<doubleT>(ptr1,k,l,1,1,true).SVD(U,S,V);
  20810. CImg<doubleT>(ptrd,k,l,1,1,true) = U;
  20811. CImg<doubleT>(ptrd + k*l,1,k,1,1,true) = S;
  20812. CImg<doubleT>(ptrd + k*l + k,k,k,1,1,true) = V;
  20813. return cimg::type<double>::nan();
  20814. }
  20815. static double mp_max(_cimg_math_parser& mp) {
  20816. const unsigned int i_end = (unsigned int)mp.opcode[2];
  20817. double val = _mp_arg(3);
  20818. for (unsigned int i = 4; i<i_end; ++i) val = std::max(val,_mp_arg(i));
  20819. return val;
  20820. }
  20821. static double* _mp_memcopy_double(_cimg_math_parser& mp, const unsigned int ind, const ulongT *const p_ref,
  20822. const longT siz, const long inc) {
  20823. const longT
  20824. off = *p_ref?p_ref[1] + (longT)mp.mem[(longT)p_ref[2]] + 1:ind,
  20825. eoff = off + (siz - 1)*inc;
  20826. if (off<0 || eoff>=mp.mem.width())
  20827. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'copy()': "
  20828. "Out-of-bounds variable pointer "
  20829. "(length: %ld, increment: %ld, offset start: %ld, "
  20830. "offset end: %ld, offset max: %u).",
  20831. mp.imgin.pixel_type(),siz,inc,off,eoff,mp.mem._width - 1);
  20832. return &mp.mem[off];
  20833. }
  20834. static float* _mp_memcopy_float(_cimg_math_parser& mp, const ulongT *const p_ref,
  20835. const longT siz, const long inc) {
  20836. const unsigned ind = (unsigned int)p_ref[1];
  20837. const CImg<T> &img = ind==~0U?mp.imgin:mp.listin[cimg::mod((int)mp.mem[ind],mp.listin.width())];
  20838. const bool is_relative = (bool)p_ref[2];
  20839. int ox, oy, oz, oc;
  20840. longT off = 0;
  20841. if (is_relative) {
  20842. ox = (int)mp.mem[_cimg_mp_slot_x];
  20843. oy = (int)mp.mem[_cimg_mp_slot_y];
  20844. oz = (int)mp.mem[_cimg_mp_slot_z];
  20845. oc = (int)mp.mem[_cimg_mp_slot_c];
  20846. off = img.offset(ox,oy,oz,oc);
  20847. }
  20848. if ((*p_ref)%2) {
  20849. const int
  20850. x = (int)mp.mem[p_ref[3]],
  20851. y = (int)mp.mem[p_ref[4]],
  20852. z = (int)mp.mem[p_ref[5]],
  20853. c = *p_ref==5?0:(int)mp.mem[p_ref[6]];
  20854. off+=img.offset(x,y,z,c);
  20855. } else off+=(longT)mp.mem[p_ref[3]];
  20856. const longT eoff = off + (siz - 1)*inc;
  20857. if (off<0 || eoff>=(longT)img.size())
  20858. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'copy()': "
  20859. "Out-of-bounds image pointer "
  20860. "(length: %ld, increment: %ld, offset start: %ld, "
  20861. "offset end: %ld, offset max: %lu).",
  20862. mp.imgin.pixel_type(),siz,inc,off,eoff,img.size() - 1);
  20863. return (float*)&img[off];
  20864. }
  20865. static double mp_memcopy(_cimg_math_parser& mp) {
  20866. longT siz = (longT)_mp_arg(4);
  20867. const longT inc_d = (longT)_mp_arg(5), inc_s = (longT)_mp_arg(6);
  20868. const float
  20869. _opacity = (float)_mp_arg(7),
  20870. opacity = (float)cimg::abs(_opacity),
  20871. omopacity = 1 - std::max(_opacity,0.0f);
  20872. if (siz>0) {
  20873. const bool
  20874. is_doubled = mp.opcode[8]<=1,
  20875. is_doubles = mp.opcode[15]<=1;
  20876. if (is_doubled && is_doubles) { // (double*) <- (double*)
  20877. double *ptrd = _mp_memcopy_double(mp,(unsigned int)mp.opcode[2],&mp.opcode[8],siz,inc_d);
  20878. const double *ptrs = _mp_memcopy_double(mp,(unsigned int)mp.opcode[3],&mp.opcode[15],siz,inc_s);
  20879. if (inc_d==1 && inc_s==1 && _opacity>=1) {
  20880. if (ptrs + siz - 1<ptrd || ptrs>ptrd + siz - 1) std::memcpy(ptrd,ptrs,siz*sizeof(double));
  20881. else std::memmove(ptrd,ptrs,siz*sizeof(double));
  20882. } else {
  20883. if (ptrs + (siz - 1)*inc_s<ptrd || ptrs>ptrd + (siz - 1)*inc_d) {
  20884. if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; }
  20885. else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; }
  20886. } else { // Overlapping buffers
  20887. CImg<doubleT> buf((unsigned int)siz);
  20888. cimg_for(buf,ptr,double) { *ptr = *ptrs; ptrs+=inc_s; }
  20889. ptrs = buf;
  20890. if (_opacity>=1) while (siz-->0) { *ptrd = *(ptrs++); ptrd+=inc_d; }
  20891. else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**(ptrs++); ptrd+=inc_d; }
  20892. }
  20893. }
  20894. } else if (is_doubled && !is_doubles) { // (double*) <- (float*)
  20895. double *ptrd = _mp_memcopy_double(mp,(unsigned int)mp.opcode[2],&mp.opcode[8],siz,inc_d);
  20896. const float *ptrs = _mp_memcopy_float(mp,&mp.opcode[15],siz,inc_s);
  20897. if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; }
  20898. else while (siz-->0) { *ptrd = omopacity**ptrd + _opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; }
  20899. } else if (!is_doubled && is_doubles) { // (float*) <- (double*)
  20900. float *ptrd = _mp_memcopy_float(mp,&mp.opcode[8],siz,inc_d);
  20901. const double *ptrs = _mp_memcopy_double(mp,(unsigned int)mp.opcode[3],&mp.opcode[15],siz,inc_s);
  20902. if (_opacity>=1) while (siz-->0) { *ptrd = (float)*ptrs; ptrd+=inc_d; ptrs+=inc_s; }
  20903. else while (siz-->0) { *ptrd = (float)(omopacity**ptrd + opacity**ptrs); ptrd+=inc_d; ptrs+=inc_s; }
  20904. } else { // (float*) <- (float*)
  20905. float *ptrd = _mp_memcopy_float(mp,&mp.opcode[8],siz,inc_d);
  20906. const float *ptrs = _mp_memcopy_float(mp,&mp.opcode[15],siz,inc_s);
  20907. if (inc_d==1 && inc_s==1 && _opacity>=1) {
  20908. if (ptrs + siz - 1<ptrd || ptrs>ptrd + siz - 1) std::memcpy(ptrd,ptrs,siz*sizeof(float));
  20909. else std::memmove(ptrd,ptrs,siz*sizeof(float));
  20910. } else {
  20911. if (ptrs + (siz - 1)*inc_s<ptrd || ptrs>ptrd + (siz - 1)*inc_d) {
  20912. if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; }
  20913. else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; }
  20914. } else { // Overlapping buffers
  20915. CImg<floatT> buf((unsigned int)siz);
  20916. cimg_for(buf,ptr,float) { *ptr = *ptrs; ptrs+=inc_s; }
  20917. ptrs = buf;
  20918. if (_opacity>=1) while (siz-->0) { *ptrd = *(ptrs++); ptrd+=inc_d; }
  20919. else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**(ptrs++); ptrd+=inc_d; }
  20920. }
  20921. }
  20922. }
  20923. }
  20924. return _mp_arg(1);
  20925. }
  20926. static double mp_min(_cimg_math_parser& mp) {
  20927. const unsigned int i_end = (unsigned int)mp.opcode[2];
  20928. double val = _mp_arg(3);
  20929. for (unsigned int i = 4; i<i_end; ++i) val = std::min(val,_mp_arg(i));
  20930. return val;
  20931. }
  20932. static double mp_minus(_cimg_math_parser& mp) {
  20933. return -_mp_arg(2);
  20934. }
  20935. static double mp_median(_cimg_math_parser& mp) {
  20936. const unsigned int i_end = (unsigned int)mp.opcode[2];
  20937. switch (i_end - 3) {
  20938. case 1 : return _mp_arg(3);
  20939. case 2 : return cimg::median(_mp_arg(3),_mp_arg(4));
  20940. case 3 : return cimg::median(_mp_arg(3),_mp_arg(4),_mp_arg(5));
  20941. case 5 : return cimg::median(_mp_arg(3),_mp_arg(4),_mp_arg(5),_mp_arg(6),_mp_arg(7));
  20942. case 7 : return cimg::median(_mp_arg(3),_mp_arg(4),_mp_arg(5),_mp_arg(6),_mp_arg(7),_mp_arg(8),_mp_arg(9));
  20943. case 9 : return cimg::median(_mp_arg(3),_mp_arg(4),_mp_arg(5),_mp_arg(6),_mp_arg(7),_mp_arg(8),_mp_arg(9),
  20944. _mp_arg(10),_mp_arg(11));
  20945. case 13 : return cimg::median(_mp_arg(3),_mp_arg(4),_mp_arg(5),_mp_arg(6),_mp_arg(7),_mp_arg(8),_mp_arg(9),
  20946. _mp_arg(10),_mp_arg(11),_mp_arg(12),_mp_arg(13),_mp_arg(14),_mp_arg(15));
  20947. }
  20948. CImg<doubleT> vals(i_end - 3);
  20949. double *p = vals.data();
  20950. for (unsigned int i = 3; i<i_end; ++i) *(p++) = _mp_arg(i);
  20951. return vals.median();
  20952. }
  20953. static double mp_modulo(_cimg_math_parser& mp) {
  20954. return cimg::mod(_mp_arg(2),_mp_arg(3));
  20955. }
  20956. static double mp_mul(_cimg_math_parser& mp) {
  20957. return _mp_arg(2)*_mp_arg(3);
  20958. }
  20959. static double mp_mul2(_cimg_math_parser& mp) {
  20960. return _mp_arg(2)*_mp_arg(3)*_mp_arg(4);
  20961. }
  20962. static double mp_neq(_cimg_math_parser& mp) {
  20963. return (double)(_mp_arg(2)!=_mp_arg(3));
  20964. }
  20965. static double mp_norm0(_cimg_math_parser& mp) {
  20966. const unsigned int i_end = (unsigned int)mp.opcode[2];
  20967. switch (i_end - 3) {
  20968. case 1 : return _mp_arg(3)!=0;
  20969. case 2 : return (_mp_arg(3)!=0) + (_mp_arg(4)!=0);
  20970. }
  20971. double res = 0;
  20972. for (unsigned int i = 3; i<i_end; ++i)
  20973. res+=_mp_arg(i)==0?0:1;
  20974. return res;
  20975. }
  20976. static double mp_norm1(_cimg_math_parser& mp) {
  20977. const unsigned int i_end = (unsigned int)mp.opcode[2];
  20978. switch (i_end - 3) {
  20979. case 1 : return cimg::abs(_mp_arg(3));
  20980. case 2 : return cimg::abs(_mp_arg(3)) + cimg::abs(_mp_arg(4));
  20981. }
  20982. double res = 0;
  20983. for (unsigned int i = 3; i<i_end; ++i)
  20984. res+=cimg::abs(_mp_arg(i));
  20985. return res;
  20986. }
  20987. static double mp_norm2(_cimg_math_parser& mp) {
  20988. const unsigned int i_end = (unsigned int)mp.opcode[2];
  20989. switch (i_end - 3) {
  20990. case 1 : return cimg::abs(_mp_arg(3));
  20991. case 2 : return cimg::_hypot(_mp_arg(3),_mp_arg(4));
  20992. }
  20993. double res = 0;
  20994. for (unsigned int i = 3; i<i_end; ++i)
  20995. res+=cimg::sqr(_mp_arg(i));
  20996. return std::sqrt(res);
  20997. }
  20998. static double mp_norminf(_cimg_math_parser& mp) {
  20999. const unsigned int i_end = (unsigned int)mp.opcode[2];
  21000. switch (i_end - 3) {
  21001. case 1 : return cimg::abs(_mp_arg(3));
  21002. case 2 : return std::max(cimg::abs(_mp_arg(3)),cimg::abs(_mp_arg(4)));
  21003. }
  21004. double res = 0;
  21005. for (unsigned int i = 3; i<i_end; ++i) {
  21006. const double val = cimg::abs(_mp_arg(i));
  21007. if (val>res) res = val;
  21008. }
  21009. return res;
  21010. }
  21011. static double mp_normp(_cimg_math_parser& mp) {
  21012. const unsigned int i_end = (unsigned int)mp.opcode[2];
  21013. if (i_end==4) return cimg::abs(_mp_arg(3));
  21014. const double p = (double)mp.opcode[3];
  21015. double res = 0;
  21016. for (unsigned int i = 4; i<i_end; ++i)
  21017. res+=std::pow(cimg::abs(_mp_arg(i)),p);
  21018. res = std::pow(res,1/p);
  21019. return res>0?res:0.0;
  21020. }
  21021. static double mp_permutations(_cimg_math_parser& mp) {
  21022. return cimg::permutations((int)_mp_arg(2),(int)_mp_arg(3),(bool)_mp_arg(4));
  21023. }
  21024. static double mp_polygon(_cimg_math_parser& mp) {
  21025. const unsigned int i_end = (unsigned int)mp.opcode[2];
  21026. unsigned int ind = (unsigned int)mp.opcode[3];
  21027. if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.listin.width());
  21028. CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
  21029. bool is_invalid_arguments = i_end<=4;
  21030. if (!is_invalid_arguments) {
  21031. const int nbv = (int)_mp_arg(4);
  21032. if (nbv<=0) is_invalid_arguments = true;
  21033. else {
  21034. CImg<intT> points(nbv,2,1,1,0);
  21035. CImg<T> color(img._spectrum,1,1,1,0);
  21036. float opacity = 1;
  21037. unsigned int i = 5;
  21038. cimg_foroff(points,k) if (i<i_end) points(k/2,k%2) = (int)cimg::round(_mp_arg(i++));
  21039. else { is_invalid_arguments = true; break; }
  21040. if (!is_invalid_arguments) {
  21041. if (i<i_end) opacity = (float)_mp_arg(i++);
  21042. cimg_forX(color,k) if (i<i_end) color[k] = (T)_mp_arg(i++);
  21043. else { color.resize(k,1,1,1,-1); break; }
  21044. color.resize(img._spectrum,1,1,1,0,2);
  21045. img.draw_polygon(points,color._data,opacity);
  21046. }
  21047. }
  21048. }
  21049. if (is_invalid_arguments) {
  21050. CImg<doubleT> args(i_end - 4);
  21051. cimg_forX(args,k) args[k] = _mp_arg(4 + k);
  21052. if (ind==~0U)
  21053. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'polygon()': "
  21054. "Invalid arguments '%s'. ",
  21055. mp.imgin.pixel_type(),args.value_string()._data);
  21056. else
  21057. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'polygon()': "
  21058. "Invalid arguments '#%u%s%s'. ",
  21059. mp.imgin.pixel_type(),ind,args._width?",":"",args.value_string()._data);
  21060. }
  21061. return cimg::type<double>::nan();
  21062. }
  21063. static double mp_pow(_cimg_math_parser& mp) {
  21064. const double v = _mp_arg(2), p = _mp_arg(3);
  21065. return std::pow(v,p);
  21066. }
  21067. static double mp_pow0_25(_cimg_math_parser& mp) {
  21068. const double val = _mp_arg(2);
  21069. return std::sqrt(std::sqrt(val));
  21070. }
  21071. static double mp_pow3(_cimg_math_parser& mp) {
  21072. const double val = _mp_arg(2);
  21073. return val*val*val;
  21074. }
  21075. static double mp_pow4(_cimg_math_parser& mp) {
  21076. const double val = _mp_arg(2);
  21077. return val*val*val*val;
  21078. }
  21079. static double mp_print(_cimg_math_parser& mp) {
  21080. const double val = _mp_arg(1);
  21081. const bool print_char = (bool)mp.opcode[3];
  21082. cimg_pragma_openmp(critical(mp_print))
  21083. {
  21084. CImg<charT> expr(mp.opcode[2] - 4);
  21085. const ulongT *ptrs = mp.opcode._data + 4;
  21086. cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++);
  21087. cimg::strellipsize(expr);
  21088. cimg::mutex(6);
  21089. if (print_char)
  21090. std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = %g = '%c'",expr._data,val,(int)val);
  21091. else
  21092. std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = %g",expr._data,val);
  21093. std::fflush(cimg::output());
  21094. cimg::mutex(6,0);
  21095. }
  21096. return val;
  21097. }
  21098. static double mp_prod(_cimg_math_parser& mp) {
  21099. const unsigned int i_end = (unsigned int)mp.opcode[2];
  21100. double val = _mp_arg(3);
  21101. for (unsigned int i = 4; i<i_end; ++i) val*=_mp_arg(i);
  21102. return val;
  21103. }
  21104. static double mp_copy(_cimg_math_parser& mp) {
  21105. return _mp_arg(2);
  21106. }
  21107. static double mp_rol(_cimg_math_parser& mp) {
  21108. return cimg::rol(_mp_arg(2),(unsigned int)_mp_arg(3));
  21109. }
  21110. static double mp_ror(_cimg_math_parser& mp) {
  21111. return cimg::ror(_mp_arg(2),(unsigned int)_mp_arg(3));
  21112. }
  21113. static double mp_rot2d(_cimg_math_parser& mp) {
  21114. double *ptrd = &_mp_arg(1) + 1;
  21115. const float
  21116. theta = (float)_mp_arg(2)*cimg::PI/180,
  21117. ca = std::cos(theta),
  21118. sa = std::sin(theta);
  21119. *(ptrd++) = ca;
  21120. *(ptrd++) = -sa;
  21121. *(ptrd++) = sa;
  21122. *ptrd = ca;
  21123. return cimg::type<double>::nan();
  21124. }
  21125. static double mp_rot3d(_cimg_math_parser& mp) {
  21126. double *ptrd = &_mp_arg(1) + 1;
  21127. const float x = (float)_mp_arg(2), y = (float)_mp_arg(3), z = (float)_mp_arg(4), theta = (float)_mp_arg(5);
  21128. CImg<doubleT>(ptrd,3,3,1,1,true) = CImg<doubleT>::rotation_matrix(x,y,z,theta);
  21129. return cimg::type<double>::nan();
  21130. }
  21131. static double mp_round(_cimg_math_parser& mp) {
  21132. return cimg::round(_mp_arg(2),_mp_arg(3),(int)_mp_arg(4));
  21133. }
  21134. static double mp_self_add(_cimg_math_parser& mp) {
  21135. return _mp_arg(1)+=_mp_arg(2);
  21136. }
  21137. static double mp_self_bitwise_and(_cimg_math_parser& mp) {
  21138. double &val = _mp_arg(1);
  21139. return val = (double)((longT)val & (longT)_mp_arg(2));
  21140. }
  21141. static double mp_self_bitwise_left_shift(_cimg_math_parser& mp) {
  21142. double &val = _mp_arg(1);
  21143. return val = (double)((longT)val<<(unsigned int)_mp_arg(2));
  21144. }
  21145. static double mp_self_bitwise_or(_cimg_math_parser& mp) {
  21146. double &val = _mp_arg(1);
  21147. return val = (double)((longT)val | (longT)_mp_arg(2));
  21148. }
  21149. static double mp_self_bitwise_right_shift(_cimg_math_parser& mp) {
  21150. double &val = _mp_arg(1);
  21151. return val = (double)((longT)val>>(unsigned int)_mp_arg(2));
  21152. }
  21153. static double mp_self_decrement(_cimg_math_parser& mp) {
  21154. return --_mp_arg(1);
  21155. }
  21156. static double mp_self_increment(_cimg_math_parser& mp) {
  21157. return ++_mp_arg(1);
  21158. }
  21159. static double mp_self_map_vector_s(_cimg_math_parser& mp) { // Vector += scalar
  21160. unsigned int
  21161. ptrd = (unsigned int)mp.opcode[1] + 1,
  21162. siz = (unsigned int)mp.opcode[2];
  21163. mp_func op = (mp_func)mp.opcode[3];
  21164. CImg<ulongT> l_opcode(1,3);
  21165. l_opcode[2] = mp.opcode[4]; // Scalar argument.
  21166. l_opcode.swap(mp.opcode);
  21167. ulongT &target = mp.opcode[1];
  21168. while (siz-->0) { target = ptrd++; (*op)(mp); }
  21169. l_opcode.swap(mp.opcode);
  21170. return cimg::type<double>::nan();
  21171. }
  21172. static double mp_self_map_vector_v(_cimg_math_parser& mp) { // Vector += vector
  21173. unsigned int
  21174. ptrd = (unsigned int)mp.opcode[1] + 1,
  21175. siz = (unsigned int)mp.opcode[2],
  21176. ptrs = (unsigned int)mp.opcode[4] + 1;
  21177. mp_func op = (mp_func)mp.opcode[3];
  21178. CImg<ulongT> l_opcode(1,4);
  21179. l_opcode.swap(mp.opcode);
  21180. ulongT &target = mp.opcode[1], &argument = mp.opcode[2];
  21181. while (siz-->0) { target = ptrd++; argument = ptrs++; (*op)(mp); }
  21182. l_opcode.swap(mp.opcode);
  21183. return cimg::type<double>::nan();
  21184. }
  21185. static double mp_self_mul(_cimg_math_parser& mp) {
  21186. return _mp_arg(1)*=_mp_arg(2);
  21187. }
  21188. static double mp_self_div(_cimg_math_parser& mp) {
  21189. return _mp_arg(1)/=_mp_arg(2);
  21190. }
  21191. static double mp_self_modulo(_cimg_math_parser& mp) {
  21192. double &val = _mp_arg(1);
  21193. return val = cimg::mod(val,_mp_arg(2));
  21194. }
  21195. static double mp_self_pow(_cimg_math_parser& mp) {
  21196. double &val = _mp_arg(1);
  21197. return val = std::pow(val,_mp_arg(2));
  21198. }
  21199. static double mp_self_sub(_cimg_math_parser& mp) {
  21200. return _mp_arg(1)-=_mp_arg(2);
  21201. }
  21202. static double mp_set_ioff(_cimg_math_parser& mp) {
  21203. CImg<T> &img = mp.imgout;
  21204. const longT
  21205. off = (longT)_mp_arg(2),
  21206. whds = (longT)img.size();
  21207. const double val = _mp_arg(1);
  21208. if (off>=0 && off<whds) img[off] = (T)val;
  21209. return val;
  21210. }
  21211. static double mp_set_ixyzc(_cimg_math_parser& mp) {
  21212. CImg<T> &img = mp.imgout;
  21213. const int
  21214. x = (int)_mp_arg(2), y = (int)_mp_arg(3),
  21215. z = (int)_mp_arg(4), c = (int)_mp_arg(5);
  21216. const double val = _mp_arg(1);
  21217. if (x>=0 && x<img.width() && y>=0 && y<img.height() &&
  21218. z>=0 && z<img.depth() && c>=0 && c<img.spectrum())
  21219. img(x,y,z,c) = (T)val;
  21220. return val;
  21221. }
  21222. static double mp_set_joff(_cimg_math_parser& mp) {
  21223. CImg<T> &img = mp.imgout;
  21224. const int
  21225. ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
  21226. oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
  21227. const longT
  21228. off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2),
  21229. whds = (longT)img.size();
  21230. const double val = _mp_arg(1);
  21231. if (off>=0 && off<whds) img[off] = (T)val;
  21232. return val;
  21233. }
  21234. static double mp_set_jxyzc(_cimg_math_parser& mp) {
  21235. CImg<T> &img = mp.imgout;
  21236. const double
  21237. ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y],
  21238. oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c];
  21239. const int
  21240. x = (int)(ox + _mp_arg(2)), y = (int)(oy + _mp_arg(3)),
  21241. z = (int)(oz + _mp_arg(4)), c = (int)(oc + _mp_arg(5));
  21242. const double val = _mp_arg(1);
  21243. if (x>=0 && x<img.width() && y>=0 && y<img.height() &&
  21244. z>=0 && z<img.depth() && c>=0 && c<img.spectrum())
  21245. img(x,y,z,c) = (T)val;
  21246. return val;
  21247. }
  21248. static double mp_set_Ioff_s(_cimg_math_parser& mp) {
  21249. CImg<T> &img = mp.imgout;
  21250. const longT
  21251. off = (longT)_mp_arg(2),
  21252. whd = (longT)img.width()*img.height()*img.depth();
  21253. const T val = (T)_mp_arg(1);
  21254. if (off>=0 && off<whd) {
  21255. T *ptrd = &img[off];
  21256. cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
  21257. }
  21258. return _mp_arg(1);
  21259. }
  21260. static double mp_set_Ioff_v(_cimg_math_parser& mp) {
  21261. CImg<T> &img = mp.imgout;
  21262. const longT
  21263. off = (longT)_mp_arg(2),
  21264. whd = (longT)img.width()*img.height()*img.depth();
  21265. const double *ptrs = &_mp_arg(1) + 1;
  21266. if (off>=0 && off<whd) {
  21267. const unsigned int vsiz = (unsigned int)mp.opcode[3];
  21268. T *ptrd = &img[off];
  21269. cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
  21270. }
  21271. return cimg::type<double>::nan();
  21272. }
  21273. static double mp_set_Ixyz_s(_cimg_math_parser& mp) {
  21274. CImg<T> &img = mp.imgout;
  21275. const int
  21276. x = (int)_mp_arg(2),
  21277. y = (int)_mp_arg(3),
  21278. z = (int)_mp_arg(4);
  21279. const T val = (T)_mp_arg(1);
  21280. if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
  21281. T *ptrd = &img(x,y,z);
  21282. const ulongT whd = (ulongT)img._width*img._height*img._depth;
  21283. cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
  21284. }
  21285. return _mp_arg(1);
  21286. }
  21287. static double mp_set_Ixyz_v(_cimg_math_parser& mp) {
  21288. CImg<T> &img = mp.imgout;
  21289. const int
  21290. x = (int)_mp_arg(2),
  21291. y = (int)_mp_arg(3),
  21292. z = (int)_mp_arg(4);
  21293. const double *ptrs = &_mp_arg(1) + 1;
  21294. if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
  21295. const unsigned int vsiz = (unsigned int)mp.opcode[5];
  21296. T *ptrd = &img(x,y,z);
  21297. const ulongT whd = (ulongT)img._width*img._height*img._depth;
  21298. cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
  21299. }
  21300. return cimg::type<double>::nan();
  21301. }
  21302. static double mp_set_Joff_s(_cimg_math_parser& mp) {
  21303. CImg<T> &img = mp.imgout;
  21304. const int
  21305. ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
  21306. oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
  21307. const longT
  21308. off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2),
  21309. whd = (longT)img.width()*img.height()*img.depth();
  21310. const T val = (T)_mp_arg(1);
  21311. if (off>=0 && off<whd) {
  21312. T *ptrd = &img[off];
  21313. cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
  21314. }
  21315. return _mp_arg(1);
  21316. }
  21317. static double mp_set_Joff_v(_cimg_math_parser& mp) {
  21318. CImg<T> &img = mp.imgout;
  21319. const int
  21320. ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
  21321. oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
  21322. const longT
  21323. off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2),
  21324. whd = (longT)img.width()*img.height()*img.depth();
  21325. const double *ptrs = &_mp_arg(1) + 1;
  21326. if (off>=0 && off<whd) {
  21327. const unsigned int vsiz = (unsigned int)mp.opcode[3];
  21328. T *ptrd = &img[off];
  21329. cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
  21330. }
  21331. return cimg::type<double>::nan();
  21332. }
  21333. static double mp_set_Jxyz_s(_cimg_math_parser& mp) {
  21334. CImg<T> &img = mp.imgout;
  21335. const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z];
  21336. const int
  21337. x = (int)(ox + _mp_arg(2)),
  21338. y = (int)(oy + _mp_arg(3)),
  21339. z = (int)(oz + _mp_arg(4));
  21340. const T val = (T)_mp_arg(1);
  21341. if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
  21342. T *ptrd = &img(x,y,z);
  21343. const ulongT whd = (ulongT)img._width*img._height*img._depth;
  21344. cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
  21345. }
  21346. return _mp_arg(1);
  21347. }
  21348. static double mp_set_Jxyz_v(_cimg_math_parser& mp) {
  21349. CImg<T> &img = mp.imgout;
  21350. const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z];
  21351. const int
  21352. x = (int)(ox + _mp_arg(2)),
  21353. y = (int)(oy + _mp_arg(3)),
  21354. z = (int)(oz + _mp_arg(4));
  21355. const double *ptrs = &_mp_arg(1) + 1;
  21356. if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
  21357. const unsigned int vsiz = (unsigned int)mp.opcode[5];
  21358. T *ptrd = &img(x,y,z);
  21359. const ulongT whd = (ulongT)img._width*img._height*img._depth;
  21360. cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
  21361. }
  21362. return cimg::type<double>::nan();
  21363. }
  21364. static double mp_shift(_cimg_math_parser& mp) {
  21365. double *const ptrd = &_mp_arg(1) + 1;
  21366. const double *const ptrs = &_mp_arg(2) + 1;
  21367. const unsigned int siz = (unsigned int)mp.opcode[3];
  21368. const int
  21369. shift = (int)_mp_arg(4),
  21370. boundary_conditions = (int)_mp_arg(5);
  21371. CImg<doubleT>(ptrd,siz,1,1,1,true) = CImg<doubleT>(ptrs,siz,1,1,1,true).shift(shift,0,0,0,boundary_conditions);
  21372. return cimg::type<double>::nan();
  21373. }
  21374. static double mp_sign(_cimg_math_parser& mp) {
  21375. return cimg::sign(_mp_arg(2));
  21376. }
  21377. static double mp_sin(_cimg_math_parser& mp) {
  21378. return std::sin(_mp_arg(2));
  21379. }
  21380. static double mp_sinc(_cimg_math_parser& mp) {
  21381. return cimg::sinc(_mp_arg(2));
  21382. }
  21383. static double mp_sinh(_cimg_math_parser& mp) {
  21384. return std::sinh(_mp_arg(2));
  21385. }
  21386. static double mp_solve(_cimg_math_parser& mp) {
  21387. double *ptrd = &_mp_arg(1) + 1;
  21388. const double
  21389. *ptr1 = &_mp_arg(2) + 1,
  21390. *ptr2 = &_mp_arg(3) + 1;
  21391. const unsigned int
  21392. k = (unsigned int)mp.opcode[4],
  21393. l = (unsigned int)mp.opcode[5],
  21394. m = (unsigned int)mp.opcode[6];
  21395. CImg<doubleT>(ptrd,m,k,1,1,true) = CImg<doubleT>(ptr2,m,l,1,1,true).get_solve(CImg<doubleT>(ptr1,k,l,1,1,true));
  21396. return cimg::type<double>::nan();
  21397. }
  21398. static double mp_sort(_cimg_math_parser& mp) {
  21399. double *const ptrd = &_mp_arg(1) + 1;
  21400. const double *const ptrs = &_mp_arg(2) + 1;
  21401. const unsigned int
  21402. siz = (unsigned int)mp.opcode[3],
  21403. chunk_siz = (unsigned int)mp.opcode[5];
  21404. const bool is_increasing = (bool)_mp_arg(4);
  21405. CImg<doubleT>(ptrd,chunk_siz,siz/chunk_siz,1,1,true) = CImg<doubleT>(ptrs,chunk_siz,siz/chunk_siz,1,1,true).
  21406. get_sort(is_increasing,chunk_siz>1?'y':0);
  21407. return cimg::type<double>::nan();
  21408. }
  21409. static double mp_sqr(_cimg_math_parser& mp) {
  21410. return cimg::sqr(_mp_arg(2));
  21411. }
  21412. static double mp_sqrt(_cimg_math_parser& mp) {
  21413. return std::sqrt(_mp_arg(2));
  21414. }
  21415. static double mp_srand(_cimg_math_parser& mp) {
  21416. return cimg::srand((unsigned int)_mp_arg(2));
  21417. }
  21418. static double mp_srand0(_cimg_math_parser& mp) {
  21419. cimg::unused(mp);
  21420. return cimg::srand();
  21421. }
  21422. static double mp_std(_cimg_math_parser& mp) {
  21423. const unsigned int i_end = (unsigned int)mp.opcode[2];
  21424. CImg<doubleT> vals(i_end - 3);
  21425. double *p = vals.data();
  21426. for (unsigned int i = 3; i<i_end; ++i) *(p++) = _mp_arg(i);
  21427. return std::sqrt(vals.variance());
  21428. }
  21429. static double mp_string_init(_cimg_math_parser& mp) {
  21430. const char *ptrs = (char*)&mp.opcode[3];
  21431. unsigned int
  21432. ptrd = (unsigned int)mp.opcode[1] + 1,
  21433. siz = (unsigned int)mp.opcode[2];
  21434. while (siz-->0) mp.mem[ptrd++] = (double)*(ptrs++);
  21435. return cimg::type<double>::nan();
  21436. }
  21437. static double mp_stov(_cimg_math_parser& mp) {
  21438. const double *ptrs = &_mp_arg(2);
  21439. const ulongT siz = (ulongT)mp.opcode[3];
  21440. longT ind = (longT)_mp_arg(4);
  21441. const bool is_strict = (bool)_mp_arg(5);
  21442. double val = cimg::type<double>::nan();
  21443. if (ind<0 || ind>=(longT)siz) return val;
  21444. if (!siz) return *ptrs>='0' && *ptrs<='9'?*ptrs - '0':val;
  21445. CImg<charT> ss(siz + 1 - ind);
  21446. char sep;
  21447. ptrs+=1 + ind; cimg_forX(ss,i) ss[i] = (char)*(ptrs++); ss.back() = 0;
  21448. int err = std::sscanf(ss,"%lf%c",&val,&sep);
  21449. #if cimg_OS==2
  21450. // Check for +/-NaN and +/-inf as Microsoft's sscanf() version is not able
  21451. // to read those particular values.
  21452. if (!err && (*ss=='+' || *ss=='-' || *ss=='i' || *ss=='I' || *ss=='n' || *ss=='N')) {
  21453. bool is_positive = true;
  21454. const char *s = ss;
  21455. if (*s=='+') ++s; else if (*s=='-') { ++s; is_positive = false; }
  21456. if (!cimg::strcasecmp(s,"inf")) { val = cimg::type<double>::inf(); err = 1; }
  21457. else if (!cimg::strcasecmp(s,"nan")) { val = cimg::type<double>::nan(); err = 1; }
  21458. if (err==1 && !is_positive) val = -val;
  21459. }
  21460. #endif
  21461. if (is_strict && err!=1) return cimg::type<double>::nan();
  21462. return val;
  21463. }
  21464. static double mp_sub(_cimg_math_parser& mp) {
  21465. return _mp_arg(2) - _mp_arg(3);
  21466. }
  21467. static double mp_sum(_cimg_math_parser& mp) {
  21468. const unsigned int i_end = (unsigned int)mp.opcode[2];
  21469. double val = _mp_arg(3);
  21470. for (unsigned int i = 4; i<i_end; ++i) val+=_mp_arg(i);
  21471. return val;
  21472. }
  21473. static double mp_tan(_cimg_math_parser& mp) {
  21474. return std::tan(_mp_arg(2));
  21475. }
  21476. static double mp_tanh(_cimg_math_parser& mp) {
  21477. return std::tanh(_mp_arg(2));
  21478. }
  21479. static double mp_trace(_cimg_math_parser& mp) {
  21480. const double *ptrs = &_mp_arg(2) + 1;
  21481. const unsigned int k = (unsigned int)mp.opcode[3];
  21482. return CImg<doubleT>(ptrs,k,k,1,1,true).trace();
  21483. }
  21484. static double mp_transp(_cimg_math_parser& mp) {
  21485. double *ptrd = &_mp_arg(1) + 1;
  21486. const double *ptrs = &_mp_arg(2) + 1;
  21487. const unsigned int
  21488. k = (unsigned int)mp.opcode[3],
  21489. l = (unsigned int)mp.opcode[4];
  21490. CImg<doubleT>(ptrd,l,k,1,1,true) = CImg<doubleT>(ptrs,k,l,1,1,true).get_transpose();
  21491. return cimg::type<double>::nan();
  21492. }
  21493. static double mp_u(_cimg_math_parser& mp) {
  21494. return cimg::rand(_mp_arg(2),_mp_arg(3));
  21495. }
  21496. static double mp_uppercase(_cimg_math_parser& mp) {
  21497. return cimg::uppercase(_mp_arg(2));
  21498. }
  21499. static double mp_var(_cimg_math_parser& mp) {
  21500. const unsigned int i_end = (unsigned int)mp.opcode[2];
  21501. CImg<doubleT> vals(i_end - 3);
  21502. double *p = vals.data();
  21503. for (unsigned int i = 3; i<i_end; ++i) *(p++) = _mp_arg(i);
  21504. return vals.variance();
  21505. }
  21506. static double mp_vector_copy(_cimg_math_parser& mp) {
  21507. std::memcpy(&_mp_arg(1) + 1,&_mp_arg(2) + 1,sizeof(double)*mp.opcode[3]);
  21508. return cimg::type<double>::nan();
  21509. }
  21510. static double mp_vector_crop(_cimg_math_parser& mp) {
  21511. double *const ptrd = &_mp_arg(1) + 1;
  21512. const double *const ptrs = &_mp_arg(2) + 1;
  21513. const longT
  21514. length = (longT)mp.opcode[3],
  21515. start = (longT)_mp_arg(4),
  21516. sublength = (longT)mp.opcode[5];
  21517. if (start<0 || start + sublength>length)
  21518. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Value accessor '[]': "
  21519. "Out-of-bounds sub-vector request "
  21520. "(length: %ld, start: %ld, sub-length: %ld).",
  21521. mp.imgin.pixel_type(),length,start,sublength);
  21522. std::memcpy(ptrd,ptrs + start,sublength*sizeof(double));
  21523. return cimg::type<double>::nan();
  21524. }
  21525. static double mp_vector_init(_cimg_math_parser& mp) {
  21526. unsigned int
  21527. ptrs = 4U,
  21528. ptrd = (unsigned int)mp.opcode[1] + 1,
  21529. siz = (unsigned int)mp.opcode[3];
  21530. switch (mp.opcode[2] - 4) {
  21531. case 0 : std::memset(mp.mem._data + ptrd,0,siz*sizeof(double)); break; // 0 values given
  21532. case 1 : { const double val = _mp_arg(ptrs); while (siz-->0) mp.mem[ptrd++] = val; } break;
  21533. default : while (siz-->0) { mp.mem[ptrd++] = _mp_arg(ptrs++); if (ptrs>=mp.opcode[2]) ptrs = 4U; }
  21534. }
  21535. return cimg::type<double>::nan();
  21536. }
  21537. static double mp_vector_eq(_cimg_math_parser& mp) {
  21538. const double
  21539. *ptr1 = &_mp_arg(2) + 1,
  21540. *ptr2 = &_mp_arg(4) + 1;
  21541. unsigned int p1 = (unsigned int)mp.opcode[3], p2 = (unsigned int)mp.opcode[5], n;
  21542. const int N = (int)_mp_arg(6);
  21543. const bool case_sensitive = (bool)_mp_arg(7);
  21544. bool still_equal = true;
  21545. double value;
  21546. if (!N) return true;
  21547. // Compare all values.
  21548. if (N<0) {
  21549. if (p1>0 && p2>0) { // Vector == vector
  21550. if (p1!=p2) return false;
  21551. if (case_sensitive)
  21552. while (still_equal && p1--) still_equal = *(ptr1++)==*(ptr2++);
  21553. else
  21554. while (still_equal && p1--)
  21555. still_equal = cimg::lowercase(*(ptr1++))==cimg::lowercase(*(ptr2++));
  21556. return still_equal;
  21557. } else if (p1>0 && !p2) { // Vector == scalar
  21558. value = _mp_arg(4);
  21559. if (!case_sensitive) value = cimg::lowercase(value);
  21560. while (still_equal && p1--) still_equal = *(ptr1++)==value;
  21561. return still_equal;
  21562. } else if (!p1 && p2>0) { // Scalar == vector
  21563. value = _mp_arg(2);
  21564. if (!case_sensitive) value = cimg::lowercase(value);
  21565. while (still_equal && p2--) still_equal = *(ptr2++)==value;
  21566. return still_equal;
  21567. } else { // Scalar == scalar
  21568. if (case_sensitive) return _mp_arg(2)==_mp_arg(4);
  21569. else return cimg::lowercase(_mp_arg(2))==cimg::lowercase(_mp_arg(4));
  21570. }
  21571. }
  21572. // Compare only first N values.
  21573. if (p1>0 && p2>0) { // Vector == vector
  21574. n = cimg::min((unsigned int)N,p1,p2);
  21575. if (case_sensitive)
  21576. while (still_equal && n--) still_equal = *(ptr1++)==(*ptr2++);
  21577. else
  21578. while (still_equal && n--) still_equal = cimg::lowercase(*(ptr1++))==cimg::lowercase(*(ptr2++));
  21579. return still_equal;
  21580. } else if (p1>0 && !p2) { // Vector == scalar
  21581. n = std::min((unsigned int)N,p1);
  21582. value = _mp_arg(4);
  21583. if (!case_sensitive) value = cimg::lowercase(value);
  21584. while (still_equal && n--) still_equal = *(ptr1++)==value;
  21585. return still_equal;
  21586. } else if (!p1 && p2>0) { // Scalar == vector
  21587. n = std::min((unsigned int)N,p2);
  21588. value = _mp_arg(2);
  21589. if (!case_sensitive) value = cimg::lowercase(value);
  21590. while (still_equal && n--) still_equal = *(ptr2++)==value;
  21591. return still_equal;
  21592. } // Scalar == scalar
  21593. if (case_sensitive) return _mp_arg(2)==_mp_arg(4);
  21594. return cimg::lowercase(_mp_arg(2))==cimg::lowercase(_mp_arg(4));
  21595. }
  21596. static double mp_vector_off(_cimg_math_parser& mp) {
  21597. const unsigned int
  21598. ptr = (unsigned int)mp.opcode[2] + 1,
  21599. siz = (unsigned int)mp.opcode[3];
  21600. const int off = (int)_mp_arg(4);
  21601. return off>=0 && off<(int)siz?mp.mem[ptr + off]:cimg::type<double>::nan();
  21602. }
  21603. static double mp_vector_map_sv(_cimg_math_parser& mp) { // Operator(scalar,vector)
  21604. unsigned int
  21605. siz = (unsigned int)mp.opcode[2],
  21606. ptrs = (unsigned int)mp.opcode[5] + 1;
  21607. double *ptrd = &_mp_arg(1) + 1;
  21608. mp_func op = (mp_func)mp.opcode[3];
  21609. CImg<ulongT> l_opcode(4);
  21610. l_opcode[2] = mp.opcode[4]; // Scalar argument1
  21611. l_opcode.swap(mp.opcode);
  21612. ulongT &argument2 = mp.opcode[3];
  21613. while (siz-->0) { argument2 = ptrs++; *(ptrd++) = (*op)(mp); }
  21614. l_opcode.swap(mp.opcode);
  21615. return cimg::type<double>::nan();
  21616. }
  21617. static double mp_vector_map_v(_cimg_math_parser& mp) { // Operator(vector)
  21618. unsigned int
  21619. siz = (unsigned int)mp.opcode[2],
  21620. ptrs = (unsigned int)mp.opcode[4] + 1;
  21621. double *ptrd = &_mp_arg(1) + 1;
  21622. mp_func op = (mp_func)mp.opcode[3];
  21623. CImg<ulongT> l_opcode(1,3);
  21624. l_opcode.swap(mp.opcode);
  21625. ulongT &argument = mp.opcode[2];
  21626. while (siz-->0) { argument = ptrs++; *(ptrd++) = (*op)(mp); }
  21627. l_opcode.swap(mp.opcode);
  21628. return cimg::type<double>::nan();
  21629. }
  21630. static double mp_vector_map_vs(_cimg_math_parser& mp) { // Operator(vector,scalar)
  21631. unsigned int
  21632. siz = (unsigned int)mp.opcode[2],
  21633. ptrs = (unsigned int)mp.opcode[4] + 1;
  21634. double *ptrd = &_mp_arg(1) + 1;
  21635. mp_func op = (mp_func)mp.opcode[3];
  21636. CImg<ulongT> l_opcode(1,4);
  21637. l_opcode[3] = mp.opcode[5]; // Scalar argument2
  21638. l_opcode.swap(mp.opcode);
  21639. ulongT &argument1 = mp.opcode[2];
  21640. while (siz-->0) { argument1 = ptrs++; *(ptrd++) = (*op)(mp); }
  21641. l_opcode.swap(mp.opcode);
  21642. return cimg::type<double>::nan();
  21643. }
  21644. static double mp_vector_map_vss(_cimg_math_parser& mp) { // Operator(vector,scalar,scalar)
  21645. unsigned int
  21646. siz = (unsigned int)mp.opcode[2],
  21647. ptrs = (unsigned int)mp.opcode[4] + 1;
  21648. double *ptrd = &_mp_arg(1) + 1;
  21649. mp_func op = (mp_func)mp.opcode[3];
  21650. CImg<ulongT> l_opcode(1,5);
  21651. l_opcode[3] = mp.opcode[5]; // Scalar argument2
  21652. l_opcode[4] = mp.opcode[6]; // Scalar argument3
  21653. l_opcode.swap(mp.opcode);
  21654. ulongT &argument1 = mp.opcode[2];
  21655. while (siz-->0) { argument1 = ptrs++; *(ptrd++) = (*op)(mp); }
  21656. l_opcode.swap(mp.opcode);
  21657. return cimg::type<double>::nan();
  21658. }
  21659. static double mp_vector_map_vv(_cimg_math_parser& mp) { // Operator(vector,vector)
  21660. unsigned int
  21661. siz = (unsigned int)mp.opcode[2],
  21662. ptrs1 = (unsigned int)mp.opcode[4] + 1,
  21663. ptrs2 = (unsigned int)mp.opcode[5] + 1;
  21664. double *ptrd = &_mp_arg(1) + 1;
  21665. mp_func op = (mp_func)mp.opcode[3];
  21666. CImg<ulongT> l_opcode(1,4);
  21667. l_opcode.swap(mp.opcode);
  21668. ulongT &argument1 = mp.opcode[2], &argument2 = mp.opcode[3];
  21669. while (siz-->0) { argument1 = ptrs1++; argument2 = ptrs2++; *(ptrd++) = (*op)(mp); }
  21670. l_opcode.swap(mp.opcode);
  21671. return cimg::type<double>::nan();
  21672. }
  21673. static double mp_vector_neq(_cimg_math_parser& mp) {
  21674. return !mp_vector_eq(mp);
  21675. }
  21676. static double mp_vector_print(_cimg_math_parser& mp) {
  21677. const bool print_string = (bool)mp.opcode[4];
  21678. cimg_pragma_openmp(critical(mp_vector_print))
  21679. {
  21680. CImg<charT> expr(mp.opcode[2] - 5);
  21681. const ulongT *ptrs = mp.opcode._data + 5;
  21682. cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++);
  21683. cimg::strellipsize(expr);
  21684. unsigned int
  21685. ptr = (unsigned int)mp.opcode[1] + 1,
  21686. siz0 = (unsigned int)mp.opcode[3],
  21687. siz = siz0;
  21688. cimg::mutex(6);
  21689. std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = [ ",expr._data);
  21690. unsigned int count = 0;
  21691. while (siz-->0) {
  21692. if (count>=64 && siz>=64) {
  21693. std::fprintf(cimg::output(),"...,");
  21694. ptr = (unsigned int)mp.opcode[1] + 1 + siz0 - 64;
  21695. siz = 64;
  21696. } else std::fprintf(cimg::output(),"%g%s",mp.mem[ptr++],siz?",":"");
  21697. ++count;
  21698. }
  21699. if (print_string) {
  21700. CImg<charT> str(siz0 + 1);
  21701. ptr = (unsigned int)mp.opcode[1] + 1;
  21702. for (unsigned int k = 0; k<siz0; ++k) str[k] = (char)mp.mem[ptr++];
  21703. str[siz0] = 0;
  21704. cimg::strellipsize(str,1024,false);
  21705. std::fprintf(cimg::output()," ] = '%s' (size: %u)",str._data,siz0);
  21706. } else std::fprintf(cimg::output()," ] (size: %u)",siz0);
  21707. std::fflush(cimg::output());
  21708. cimg::mutex(6,0);
  21709. }
  21710. return cimg::type<double>::nan();
  21711. }
  21712. static double mp_vector_resize(_cimg_math_parser& mp) {
  21713. double *const ptrd = &_mp_arg(1) + 1;
  21714. const unsigned int p1 = (unsigned int)mp.opcode[2], p2 = (unsigned int)mp.opcode[4];
  21715. const int
  21716. interpolation = (int)_mp_arg(5),
  21717. boundary_conditions = (int)_mp_arg(6);
  21718. if (p2) { // Resize vector
  21719. const double *const ptrs = &_mp_arg(3) + 1;
  21720. CImg<doubleT>(ptrd,p1,1,1,1,true) = CImg<doubleT>(ptrs,p2,1,1,1,true).
  21721. get_resize(p1,1,1,1,interpolation,boundary_conditions);
  21722. } else { // Resize scalar
  21723. const double value = _mp_arg(3);
  21724. CImg<doubleT>(ptrd,p1,1,1,1,true) = CImg<doubleT>(1,1,1,1,value).resize(p1,1,1,1,interpolation,
  21725. boundary_conditions);
  21726. }
  21727. return cimg::type<double>::nan();
  21728. }
  21729. static double mp_vector_reverse(_cimg_math_parser& mp) {
  21730. double *const ptrd = &_mp_arg(1) + 1;
  21731. const double *const ptrs = &_mp_arg(2) + 1;
  21732. const unsigned int p1 = (unsigned int)mp.opcode[3];
  21733. CImg<doubleT>(ptrd,p1,1,1,1,true) = CImg<doubleT>(ptrs,p1,1,1,1,true).get_mirror('x');
  21734. return cimg::type<double>::nan();
  21735. }
  21736. static double mp_vector_set_off(_cimg_math_parser& mp) {
  21737. const unsigned int
  21738. ptr = (unsigned int)mp.opcode[2] + 1,
  21739. siz = (unsigned int)mp.opcode[3];
  21740. const int off = (int)_mp_arg(4);
  21741. if (off>=0 && off<(int)siz) mp.mem[ptr + off] = _mp_arg(5);
  21742. return _mp_arg(5);
  21743. }
  21744. static double mp_vtos(_cimg_math_parser& mp) {
  21745. double *ptrd = &_mp_arg(1) + 1;
  21746. const unsigned int
  21747. sizd = (unsigned int)mp.opcode[2],
  21748. sizs = (unsigned int)mp.opcode[4];
  21749. const int nb_digits = (int)_mp_arg(5);
  21750. CImg<charT> format(8);
  21751. switch (nb_digits) {
  21752. case -1 : std::strcpy(format,"%g"); break;
  21753. case 0 : std::strcpy(format,"%.17g"); break;
  21754. default : cimg_snprintf(format,format._width,"%%.%dg",nb_digits);
  21755. }
  21756. CImg<charT> str;
  21757. if (sizs) { // Vector expression
  21758. const double *ptrs = &_mp_arg(3) + 1;
  21759. CImg<doubleT>(ptrs,sizs,1,1,1,true).value_string(',',sizd + 1,format).move_to(str);
  21760. } else { // Scalar expression
  21761. str.assign(sizd + 1);
  21762. cimg_snprintf(str,sizd + 1,format,_mp_arg(3));
  21763. }
  21764. const unsigned int l = std::min(sizd,(unsigned int)std::strlen(str) + 1);
  21765. CImg<doubleT>(ptrd,l,1,1,1,true) = str.get_shared_points(0,l - 1);
  21766. return cimg::type<double>::nan();
  21767. }
  21768. static double mp_whiledo(_cimg_math_parser& mp) {
  21769. const ulongT
  21770. mem_body = mp.opcode[1],
  21771. mem_cond = mp.opcode[2];
  21772. const CImg<ulongT>
  21773. *const p_cond = ++mp.p_code,
  21774. *const p_body = p_cond + mp.opcode[3],
  21775. *const p_end = p_body + mp.opcode[4];
  21776. const unsigned int vsiz = (unsigned int)mp.opcode[5];
  21777. bool is_cond = false;
  21778. if (mp.opcode[6]) { // Set default value for result and condition if necessary
  21779. if (vsiz) CImg<doubleT>(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type<double>::nan());
  21780. else mp.mem[mem_body] = cimg::type<double>::nan();
  21781. }
  21782. if (mp.opcode[7]) mp.mem[mem_cond] = 0;
  21783. const unsigned int _break_type = mp.break_type;
  21784. mp.break_type = 0;
  21785. do {
  21786. for (mp.p_code = p_cond; mp.p_code<p_body; ++mp.p_code) { // Evaluate condition
  21787. mp.opcode._data = mp.p_code->_data;
  21788. const ulongT target = mp.opcode[1];
  21789. mp.mem[target] = _cimg_mp_defunc(mp);
  21790. }
  21791. if (mp.break_type==1) break;
  21792. is_cond = (bool)mp.mem[mem_cond];
  21793. if (is_cond && !mp.break_type) // Evaluate body
  21794. for (mp.p_code = p_body; mp.p_code<p_end; ++mp.p_code) {
  21795. mp.opcode._data = mp.p_code->_data;
  21796. const ulongT target = mp.opcode[1];
  21797. mp.mem[target] = _cimg_mp_defunc(mp);
  21798. }
  21799. if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
  21800. } while (is_cond);
  21801. mp.break_type = _break_type;
  21802. mp.p_code = p_end - 1;
  21803. return mp.mem[mem_body];
  21804. }
  21805. static double mp_Ioff(_cimg_math_parser& mp) {
  21806. double *ptrd = &_mp_arg(1) + 1;
  21807. const unsigned int
  21808. boundary_conditions = (unsigned int)_mp_arg(3),
  21809. vsiz = (unsigned int)mp.opcode[4];
  21810. const CImg<T> &img = mp.imgin;
  21811. const longT
  21812. off = (longT)_mp_arg(2),
  21813. whd = (longT)img.width()*img.height()*img.depth();
  21814. const T *ptrs;
  21815. if (off>=0 && off<whd) {
  21816. ptrs = &img[off];
  21817. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  21818. return cimg::type<double>::nan();
  21819. }
  21820. if (img._data) switch (boundary_conditions) {
  21821. case 3 : { // Mirror
  21822. const longT whd2 = 2*whd, moff = cimg::mod(off,whd2);
  21823. ptrs = &img[moff<whd?moff:whd2 - moff - 1];
  21824. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  21825. return cimg::type<double>::nan();
  21826. }
  21827. case 2 : // Periodic
  21828. ptrs = &img[cimg::mod(off,whd)];
  21829. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  21830. return cimg::type<double>::nan();
  21831. case 1 : // Neumann
  21832. ptrs = off<0?&img[0]:&img[whd - 1];
  21833. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  21834. return cimg::type<double>::nan();
  21835. default : // Dirichlet
  21836. std::memset(ptrd,0,vsiz*sizeof(double));
  21837. return cimg::type<double>::nan();
  21838. }
  21839. std::memset(ptrd,0,vsiz*sizeof(double));
  21840. return cimg::type<double>::nan();
  21841. }
  21842. static double mp_Ixyz(_cimg_math_parser& mp) {
  21843. double *ptrd = &_mp_arg(1) + 1;
  21844. const unsigned int
  21845. interpolation = (unsigned int)_mp_arg(5),
  21846. boundary_conditions = (unsigned int)_mp_arg(6),
  21847. vsiz = (unsigned int)mp.opcode[7];
  21848. const CImg<T> &img = mp.imgin;
  21849. const double x = _mp_arg(2), y = _mp_arg(3), z = _mp_arg(4);
  21850. const ulongT whd = (ulongT)img._width*img._height*img._depth;
  21851. const T *ptrs;
  21852. if (interpolation==0) switch (boundary_conditions) { // Nearest neighbor interpolation
  21853. case 3 : { // Mirror
  21854. const int
  21855. w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(),
  21856. mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2),
  21857. cx = mx<img.width()?mx:w2 - mx - 1,
  21858. cy = my<img.height()?my:h2 - my - 1,
  21859. cz = mz<img.depth()?mz:d2 - mz - 1;
  21860. ptrs = &img(cx,cy,cz);
  21861. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
  21862. } break;
  21863. case 2 : { // Periodic
  21864. const int
  21865. cx = cimg::mod((int)x,img.width()),
  21866. cy = cimg::mod((int)y,img.height()),
  21867. cz = cimg::mod((int)z,img.depth());
  21868. ptrs = &img(cx,cy,cz);
  21869. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
  21870. } break;
  21871. case 1 : { // Neumann
  21872. ptrs = &img._atXYZ((int)x,(int)y,(int)z);
  21873. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
  21874. } break;
  21875. default : // Dirichlet
  21876. if (img.containsXYZC((int)x,(int)y,(int)z)) {
  21877. ptrs = &img((int)x,(int)y,(int)z);
  21878. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
  21879. } else std::memset(ptrd,0,vsiz*sizeof(double));
  21880. } else switch (boundary_conditions) { // Linear interpolation
  21881. case 3 : { // Mirror
  21882. const float
  21883. w2 = 2.0f*img.width(), h2 = 2.0f*img.height(), d2 = 2.0f*img.depth(),
  21884. mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
  21885. cx = mx<img.width()?mx:w2 - mx - 1,
  21886. cy = my<img.height()?my:h2 - my - 1,
  21887. cz = mz<img.depth()?mz:d2 - mz - 1;
  21888. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
  21889. } break;
  21890. case 2 : { // Periodic
  21891. const float
  21892. cx = cimg::mod((float)x,(float)img.width()),
  21893. cy = cimg::mod((float)y,(float)img.height()),
  21894. cz = cimg::mod((float)z,(float)img.depth());
  21895. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
  21896. } break;
  21897. case 1 : // Neumann
  21898. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c);
  21899. break;
  21900. default : // Dirichlet
  21901. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0);
  21902. }
  21903. return cimg::type<double>::nan();
  21904. }
  21905. static double mp_Joff(_cimg_math_parser& mp) {
  21906. double *ptrd = &_mp_arg(1) + 1;
  21907. const unsigned int
  21908. boundary_conditions = (unsigned int)_mp_arg(3),
  21909. vsiz = (unsigned int)mp.opcode[4];
  21910. const CImg<T> &img = mp.imgin;
  21911. const int
  21912. ox = (int)mp.mem[_cimg_mp_slot_x],
  21913. oy = (int)mp.mem[_cimg_mp_slot_y],
  21914. oz = (int)mp.mem[_cimg_mp_slot_z];
  21915. const longT
  21916. off = img.offset(ox,oy,oz) + (longT)_mp_arg(2),
  21917. whd = (longT)img.width()*img.height()*img.depth();
  21918. const T *ptrs;
  21919. if (off>=0 && off<whd) {
  21920. ptrs = &img[off];
  21921. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  21922. return cimg::type<double>::nan();
  21923. }
  21924. if (img._data) switch (boundary_conditions) {
  21925. case 3 : { // Mirror
  21926. const longT whd2 = 2*whd, moff = cimg::mod(off,whd2);
  21927. ptrs = &img[moff<whd?moff:whd2 - moff - 1];
  21928. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  21929. return cimg::type<double>::nan();
  21930. }
  21931. case 2 : // Periodic
  21932. ptrs = &img[cimg::mod(off,whd)];
  21933. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  21934. return cimg::type<double>::nan();
  21935. case 1 : // Neumann
  21936. ptrs = off<0?&img[0]:&img[whd - 1];
  21937. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  21938. return cimg::type<double>::nan();
  21939. default : // Dirichlet
  21940. std::memset(ptrd,0,vsiz*sizeof(double));
  21941. return cimg::type<double>::nan();
  21942. }
  21943. std::memset(ptrd,0,vsiz*sizeof(double));
  21944. return cimg::type<double>::nan();
  21945. }
  21946. static double mp_Jxyz(_cimg_math_parser& mp) {
  21947. double *ptrd = &_mp_arg(1) + 1;
  21948. const unsigned int
  21949. interpolation = (unsigned int)_mp_arg(5),
  21950. boundary_conditions = (unsigned int)_mp_arg(6),
  21951. vsiz = (unsigned int)mp.opcode[7];
  21952. const CImg<T> &img = mp.imgin;
  21953. const double
  21954. ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z],
  21955. x = ox + _mp_arg(2), y = oy + _mp_arg(3), z = oz + _mp_arg(4);
  21956. const ulongT whd = (ulongT)img._width*img._height*img._depth;
  21957. const T *ptrs;
  21958. if (interpolation==0) switch (boundary_conditions) { // Nearest neighbor interpolation
  21959. case 3 : { // Mirror
  21960. const int
  21961. w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(),
  21962. mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2),
  21963. cx = mx<img.width()?mx:w2 - mx - 1,
  21964. cy = my<img.height()?my:h2 - my - 1,
  21965. cz = mz<img.depth()?mz:d2 - mz - 1;
  21966. ptrs = &img(cx,cy,cz);
  21967. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
  21968. } break;
  21969. case 2 : { // Periodic
  21970. const int
  21971. cx = cimg::mod((int)x,img.width()),
  21972. cy = cimg::mod((int)y,img.height()),
  21973. cz = cimg::mod((int)z,img.depth());
  21974. ptrs = &img(cx,cy,cz);
  21975. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
  21976. } break;
  21977. case 1 : { // Neumann
  21978. ptrs = &img._atXYZ((int)x,(int)y,(int)z);
  21979. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
  21980. } break;
  21981. default : // Dirichlet
  21982. if (img.containsXYZC((int)x,(int)y,(int)z)) {
  21983. ptrs = &img((int)x,(int)y,(int)z);
  21984. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
  21985. } else std::memset(ptrd,0,vsiz*sizeof(double));
  21986. } else switch (boundary_conditions) { // Linear interpolation
  21987. case 3 : { // Mirror
  21988. const float
  21989. w2 = 2.0f*img.width(), h2 = 2.0f*img.height(), d2 = 2.0f*img.depth(),
  21990. mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
  21991. cx = mx<img.width()?mx:w2 - mx - 1,
  21992. cy = my<img.height()?my:h2 - my - 1,
  21993. cz = mz<img.depth()?mz:d2 - mz - 1;
  21994. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
  21995. } break;
  21996. case 2 : { // Periodic
  21997. const float
  21998. cx = cimg::mod((float)x,(float)img.width()),
  21999. cy = cimg::mod((float)y,(float)img.height()),
  22000. cz = cimg::mod((float)z,(float)img.depth());
  22001. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
  22002. } break;
  22003. case 1 : // Neumann
  22004. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c);
  22005. break;
  22006. default : // Dirichlet
  22007. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0);
  22008. }
  22009. return cimg::type<double>::nan();
  22010. }
  22011. #undef _mp_arg
  22012. }; // struct _cimg_math_parser {}
  22013. //! Compute the square value of each pixel value.
  22014. /**
  22015. 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$.
  22016. \note
  22017. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  22018. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  22019. \par Example
  22020. \code
  22021. const CImg<float> img("reference.jpg");
  22022. (img,img.get_sqr().normalize(0,255)).display();
  22023. \endcode
  22024. \image html ref_sqr.jpg
  22025. **/
  22026. CImg<T>& sqr() {
  22027. if (is_empty()) return *this;
  22028. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=524288))
  22029. cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(val*val); };
  22030. return *this;
  22031. }
  22032. //! Compute the square value of each pixel value \newinstance.
  22033. CImg<Tfloat> get_sqr() const {
  22034. return CImg<Tfloat>(*this,false).sqr();
  22035. }
  22036. //! Compute the square root of each pixel value.
  22037. /**
  22038. 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$.
  22039. \note
  22040. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  22041. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  22042. \par Example
  22043. \code
  22044. const CImg<float> img("reference.jpg");
  22045. (img,img.get_sqrt().normalize(0,255)).display();
  22046. \endcode
  22047. \image html ref_sqrt.jpg
  22048. **/
  22049. CImg<T>& sqrt() {
  22050. if (is_empty()) return *this;
  22051. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=8192))
  22052. cimg_rof(*this,ptrd,T) *ptrd = (T)std::sqrt((double)*ptrd);
  22053. return *this;
  22054. }
  22055. //! Compute the square root of each pixel value \newinstance.
  22056. CImg<Tfloat> get_sqrt() const {
  22057. return CImg<Tfloat>(*this,false).sqrt();
  22058. }
  22059. //! Compute the exponential of each pixel value.
  22060. /**
  22061. 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$.
  22062. \note
  22063. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  22064. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  22065. **/
  22066. CImg<T>& exp() {
  22067. if (is_empty()) return *this;
  22068. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=4096))
  22069. cimg_rof(*this,ptrd,T) *ptrd = (T)std::exp((double)*ptrd);
  22070. return *this;
  22071. }
  22072. //! Compute the exponential of each pixel value \newinstance.
  22073. CImg<Tfloat> get_exp() const {
  22074. return CImg<Tfloat>(*this,false).exp();
  22075. }
  22076. //! Compute the logarithm of each pixel value.
  22077. /**
  22078. Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its logarithm
  22079. \f$\mathrm{log}_{e}(I_{(x,y,z,c)})\f$.
  22080. \note
  22081. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  22082. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  22083. **/
  22084. CImg<T>& log() {
  22085. if (is_empty()) return *this;
  22086. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=262144))
  22087. cimg_rof(*this,ptrd,T) *ptrd = (T)std::log((double)*ptrd);
  22088. return *this;
  22089. }
  22090. //! Compute the logarithm of each pixel value \newinstance.
  22091. CImg<Tfloat> get_log() const {
  22092. return CImg<Tfloat>(*this,false).log();
  22093. }
  22094. //! Compute the base-2 logarithm of each pixel value.
  22095. /**
  22096. Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its base-2 logarithm
  22097. \f$\mathrm{log}_{2}(I_{(x,y,z,c)})\f$.
  22098. \note
  22099. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  22100. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  22101. **/
  22102. CImg<T>& log2() {
  22103. if (is_empty()) return *this;
  22104. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=4096))
  22105. cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::log2((double)*ptrd);
  22106. return *this;
  22107. }
  22108. //! Compute the base-10 logarithm of each pixel value \newinstance.
  22109. CImg<Tfloat> get_log2() const {
  22110. return CImg<Tfloat>(*this,false).log2();
  22111. }
  22112. //! Compute the base-10 logarithm of each pixel value.
  22113. /**
  22114. Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its base-10 logarithm
  22115. \f$\mathrm{log}_{10}(I_{(x,y,z,c)})\f$.
  22116. \note
  22117. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  22118. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  22119. **/
  22120. CImg<T>& log10() {
  22121. if (is_empty()) return *this;
  22122. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=4096))
  22123. cimg_rof(*this,ptrd,T) *ptrd = (T)std::log10((double)*ptrd);
  22124. return *this;
  22125. }
  22126. //! Compute the base-10 logarithm of each pixel value \newinstance.
  22127. CImg<Tfloat> get_log10() const {
  22128. return CImg<Tfloat>(*this,false).log10();
  22129. }
  22130. //! Compute the absolute value of each pixel value.
  22131. /**
  22132. 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$.
  22133. \note
  22134. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  22135. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  22136. **/
  22137. CImg<T>& abs() {
  22138. if (is_empty()) return *this;
  22139. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=524288))
  22140. cimg_rof(*this,ptrd,T) *ptrd = cimg::abs(*ptrd);
  22141. return *this;
  22142. }
  22143. //! Compute the absolute value of each pixel value \newinstance.
  22144. CImg<Tfloat> get_abs() const {
  22145. return CImg<Tfloat>(*this,false).abs();
  22146. }
  22147. //! Compute the sign of each pixel value.
  22148. /**
  22149. Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sign
  22150. \f$\mathrm{sign}(I_{(x,y,z,c)})\f$.
  22151. \note
  22152. - The sign is set to:
  22153. - \c 1 if pixel value is strictly positive.
  22154. - \c -1 if pixel value is strictly negative.
  22155. - \c 0 if pixel value is equal to \c 0.
  22156. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  22157. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  22158. **/
  22159. CImg<T>& sign() {
  22160. if (is_empty()) return *this;
  22161. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
  22162. cimg_rof(*this,ptrd,T) *ptrd = cimg::sign(*ptrd);
  22163. return *this;
  22164. }
  22165. //! Compute the sign of each pixel value \newinstance.
  22166. CImg<Tfloat> get_sign() const {
  22167. return CImg<Tfloat>(*this,false).sign();
  22168. }
  22169. //! Compute the cosine of each pixel value.
  22170. /**
  22171. 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$.
  22172. \note
  22173. - Pixel values are regarded as being in \e radian.
  22174. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  22175. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  22176. **/
  22177. CImg<T>& cos() {
  22178. if (is_empty()) return *this;
  22179. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=8192))
  22180. cimg_rof(*this,ptrd,T) *ptrd = (T)std::cos((double)*ptrd);
  22181. return *this;
  22182. }
  22183. //! Compute the cosine of each pixel value \newinstance.
  22184. CImg<Tfloat> get_cos() const {
  22185. return CImg<Tfloat>(*this,false).cos();
  22186. }
  22187. //! Compute the sine of each pixel value.
  22188. /**
  22189. 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$.
  22190. \note
  22191. - Pixel values are regarded as being in \e radian.
  22192. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  22193. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  22194. **/
  22195. CImg<T>& sin() {
  22196. if (is_empty()) return *this;
  22197. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=8192))
  22198. cimg_rof(*this,ptrd,T) *ptrd = (T)std::sin((double)*ptrd);
  22199. return *this;
  22200. }
  22201. //! Compute the sine of each pixel value \newinstance.
  22202. CImg<Tfloat> get_sin() const {
  22203. return CImg<Tfloat>(*this,false).sin();
  22204. }
  22205. //! Compute the sinc of each pixel value.
  22206. /**
  22207. Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sinc
  22208. \f$\mathrm{sinc}(I_{(x,y,z,c)})\f$.
  22209. \note
  22210. - Pixel values are regarded as being exin \e radian.
  22211. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  22212. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  22213. **/
  22214. CImg<T>& sinc() {
  22215. if (is_empty()) return *this;
  22216. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=2048))
  22217. cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::sinc((double)*ptrd);
  22218. return *this;
  22219. }
  22220. //! Compute the sinc of each pixel value \newinstance.
  22221. CImg<Tfloat> get_sinc() const {
  22222. return CImg<Tfloat>(*this,false).sinc();
  22223. }
  22224. //! Compute the tangent of each pixel value.
  22225. /**
  22226. 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$.
  22227. \note
  22228. - Pixel values are regarded as being exin \e radian.
  22229. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  22230. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  22231. **/
  22232. CImg<T>& tan() {
  22233. if (is_empty()) return *this;
  22234. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=2048))
  22235. cimg_rof(*this,ptrd,T) *ptrd = (T)std::tan((double)*ptrd);
  22236. return *this;
  22237. }
  22238. //! Compute the tangent of each pixel value \newinstance.
  22239. CImg<Tfloat> get_tan() const {
  22240. return CImg<Tfloat>(*this,false).tan();
  22241. }
  22242. //! Compute the hyperbolic cosine of each pixel value.
  22243. /**
  22244. Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic cosine
  22245. \f$\mathrm{cosh}(I_{(x,y,z,c)})\f$.
  22246. \note
  22247. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  22248. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  22249. **/
  22250. CImg<T>& cosh() {
  22251. if (is_empty()) return *this;
  22252. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=2048))
  22253. cimg_rof(*this,ptrd,T) *ptrd = (T)std::cosh((double)*ptrd);
  22254. return *this;
  22255. }
  22256. //! Compute the hyperbolic cosine of each pixel value \newinstance.
  22257. CImg<Tfloat> get_cosh() const {
  22258. return CImg<Tfloat>(*this,false).cosh();
  22259. }
  22260. //! Compute the hyperbolic sine of each pixel value.
  22261. /**
  22262. Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic sine
  22263. \f$\mathrm{sinh}(I_{(x,y,z,c)})\f$.
  22264. \note
  22265. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  22266. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  22267. **/
  22268. CImg<T>& sinh() {
  22269. if (is_empty()) return *this;
  22270. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=2048))
  22271. cimg_rof(*this,ptrd,T) *ptrd = (T)std::sinh((double)*ptrd);
  22272. return *this;
  22273. }
  22274. //! Compute the hyperbolic sine of each pixel value \newinstance.
  22275. CImg<Tfloat> get_sinh() const {
  22276. return CImg<Tfloat>(*this,false).sinh();
  22277. }
  22278. //! Compute the hyperbolic tangent of each pixel value.
  22279. /**
  22280. Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic tangent
  22281. \f$\mathrm{tanh}(I_{(x,y,z,c)})\f$.
  22282. \note
  22283. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  22284. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  22285. **/
  22286. CImg<T>& tanh() {
  22287. if (is_empty()) return *this;
  22288. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=2048))
  22289. cimg_rof(*this,ptrd,T) *ptrd = (T)std::tanh((double)*ptrd);
  22290. return *this;
  22291. }
  22292. //! Compute the hyperbolic tangent of each pixel value \newinstance.
  22293. CImg<Tfloat> get_tanh() const {
  22294. return CImg<Tfloat>(*this,false).tanh();
  22295. }
  22296. //! Compute the arccosine of each pixel value.
  22297. /**
  22298. Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arccosine
  22299. \f$\mathrm{acos}(I_{(x,y,z,c)})\f$.
  22300. \note
  22301. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  22302. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  22303. **/
  22304. CImg<T>& acos() {
  22305. if (is_empty()) return *this;
  22306. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=8192))
  22307. cimg_rof(*this,ptrd,T) *ptrd = (T)std::acos((double)*ptrd);
  22308. return *this;
  22309. }
  22310. //! Compute the arccosine of each pixel value \newinstance.
  22311. CImg<Tfloat> get_acos() const {
  22312. return CImg<Tfloat>(*this,false).acos();
  22313. }
  22314. //! Compute the arcsine of each pixel value.
  22315. /**
  22316. Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arcsine
  22317. \f$\mathrm{asin}(I_{(x,y,z,c)})\f$.
  22318. \note
  22319. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  22320. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  22321. **/
  22322. CImg<T>& asin() {
  22323. if (is_empty()) return *this;
  22324. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=8192))
  22325. cimg_rof(*this,ptrd,T) *ptrd = (T)std::asin((double)*ptrd);
  22326. return *this;
  22327. }
  22328. //! Compute the arcsine of each pixel value \newinstance.
  22329. CImg<Tfloat> get_asin() const {
  22330. return CImg<Tfloat>(*this,false).asin();
  22331. }
  22332. //! Compute the arctangent of each pixel value.
  22333. /**
  22334. Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arctangent
  22335. \f$\mathrm{atan}(I_{(x,y,z,c)})\f$.
  22336. \note
  22337. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  22338. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  22339. **/
  22340. CImg<T>& atan() {
  22341. if (is_empty()) return *this;
  22342. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=8192))
  22343. cimg_rof(*this,ptrd,T) *ptrd = (T)std::atan((double)*ptrd);
  22344. return *this;
  22345. }
  22346. //! Compute the arctangent of each pixel value \newinstance.
  22347. CImg<Tfloat> get_atan() const {
  22348. return CImg<Tfloat>(*this,false).atan();
  22349. }
  22350. //! Compute the arctangent2 of each pixel value.
  22351. /**
  22352. Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arctangent2
  22353. \f$\mathrm{atan2}(I_{(x,y,z,c)})\f$.
  22354. \param img Image whose pixel values specify the second argument of the \c atan2() function.
  22355. \note
  22356. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  22357. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  22358. \par Example
  22359. \code
  22360. const CImg<float>
  22361. img_x(100,100,1,1,"x-w/2",false), // Define an horizontal centered gradient, from '-width/2' to 'width/2'.
  22362. img_y(100,100,1,1,"y-h/2",false), // Define a vertical centered gradient, from '-height/2' to 'height/2'.
  22363. img_atan2 = img_y.get_atan2(img_x); // Compute atan2(y,x) for each pixel value.
  22364. (img_x,img_y,img_atan2).display();
  22365. \endcode
  22366. **/
  22367. template<typename t>
  22368. CImg<T>& atan2(const CImg<t>& img) {
  22369. const ulongT siz = size(), isiz = img.size();
  22370. if (siz && isiz) {
  22371. if (is_overlapped(img)) return atan2(+img);
  22372. T *ptrd = _data, *const ptre = _data + siz;
  22373. if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
  22374. for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
  22375. *ptrd = (T)std::atan2((double)*ptrd,(double)*(ptrs++));
  22376. for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)std::atan2((double)*ptrd,(double)*(ptrs++));
  22377. }
  22378. return *this;
  22379. }
  22380. //! Compute the arctangent2 of each pixel value \newinstance.
  22381. template<typename t>
  22382. CImg<Tfloat> get_atan2(const CImg<t>& img) const {
  22383. return CImg<Tfloat>(*this,false).atan2(img);
  22384. }
  22385. //! In-place pointwise multiplication.
  22386. /**
  22387. Compute the pointwise multiplication between the image instance and the specified input image \c img.
  22388. \param img Input image, as the second operand of the multiplication.
  22389. \note
  22390. - Similar to operator+=(const CImg<t>&), except that it performs a pointwise multiplication
  22391. instead of an addition.
  22392. - It does \e not perform a \e matrix multiplication. For this purpose, use operator*=(const CImg<t>&) instead.
  22393. \par Example
  22394. \code
  22395. CImg<float>
  22396. img("reference.jpg"),
  22397. shade(img.width,img.height(),1,1,"-(x-w/2)^2-(y-h/2)^2",false);
  22398. shade.normalize(0,1);
  22399. (img,shade,img.get_mul(shade)).display();
  22400. \endcode
  22401. **/
  22402. template<typename t>
  22403. CImg<T>& mul(const CImg<t>& img) {
  22404. const ulongT siz = size(), isiz = img.size();
  22405. if (siz && isiz) {
  22406. if (is_overlapped(img)) return mul(+img);
  22407. T *ptrd = _data, *const ptre = _data + siz;
  22408. if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
  22409. for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
  22410. *ptrd = (T)(*ptrd * *(ptrs++));
  22411. for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd * *(ptrs++));
  22412. }
  22413. return *this;
  22414. }
  22415. //! In-place pointwise multiplication \newinstance.
  22416. template<typename t>
  22417. CImg<_cimg_Tt> get_mul(const CImg<t>& img) const {
  22418. return CImg<_cimg_Tt>(*this,false).mul(img);
  22419. }
  22420. //! In-place pointwise division.
  22421. /**
  22422. Similar to mul(const CImg<t>&), except that it performs a pointwise division instead of a multiplication.
  22423. **/
  22424. template<typename t>
  22425. CImg<T>& div(const CImg<t>& img) {
  22426. const ulongT siz = size(), isiz = img.size();
  22427. if (siz && isiz) {
  22428. if (is_overlapped(img)) return div(+img);
  22429. T *ptrd = _data, *const ptre = _data + siz;
  22430. if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
  22431. for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
  22432. *ptrd = (T)(*ptrd / *(ptrs++));
  22433. for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd / *(ptrs++));
  22434. }
  22435. return *this;
  22436. }
  22437. //! In-place pointwise division \newinstance.
  22438. template<typename t>
  22439. CImg<_cimg_Tt> get_div(const CImg<t>& img) const {
  22440. return CImg<_cimg_Tt>(*this,false).div(img);
  22441. }
  22442. //! Raise each pixel value to a specified power.
  22443. /**
  22444. 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$.
  22445. \param p Exponent value.
  22446. \note
  22447. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  22448. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  22449. \par Example
  22450. \code
  22451. const CImg<float>
  22452. img0("reference.jpg"), // Load reference color image.
  22453. img1 = (img0/255).pow(1.8)*=255, // Compute gamma correction, with gamma = 1.8.
  22454. img2 = (img0/255).pow(0.5)*=255; // Compute gamma correction, with gamma = 0.5.
  22455. (img0,img1,img2).display();
  22456. \endcode
  22457. **/
  22458. CImg<T>& pow(const double p) {
  22459. if (is_empty()) return *this;
  22460. if (p==-4) {
  22461. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
  22462. cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/(val*val*val*val)); }
  22463. return *this;
  22464. }
  22465. if (p==-3) {
  22466. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
  22467. cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/(val*val*val)); }
  22468. return *this;
  22469. }
  22470. if (p==-2) {
  22471. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
  22472. cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/(val*val)); }
  22473. return *this;
  22474. }
  22475. if (p==-1) {
  22476. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
  22477. cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/val); }
  22478. return *this;
  22479. }
  22480. if (p==-0.5) {
  22481. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=8192))
  22482. cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1/std::sqrt((double)val)); }
  22483. return *this;
  22484. }
  22485. if (p==0) return fill((T)1);
  22486. if (p==0.25) return sqrt().sqrt();
  22487. if (p==0.5) return sqrt();
  22488. if (p==1) return *this;
  22489. if (p==2) return sqr();
  22490. if (p==3) {
  22491. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=262144))
  22492. cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = val*val*val; }
  22493. return *this;
  22494. }
  22495. if (p==4) {
  22496. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=131072))
  22497. cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = val*val*val*val; }
  22498. return *this;
  22499. }
  22500. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=1024))
  22501. cimg_rof(*this,ptrd,T) *ptrd = (T)std::pow((double)*ptrd,p);
  22502. return *this;
  22503. }
  22504. //! Raise each pixel value to a specified power \newinstance.
  22505. CImg<Tfloat> get_pow(const double p) const {
  22506. return CImg<Tfloat>(*this,false).pow(p);
  22507. }
  22508. //! Raise each pixel value to a power, specified from an expression.
  22509. /**
  22510. Similar to operator+=(const char*), except it performs a pointwise exponentiation instead of an addition.
  22511. **/
  22512. CImg<T>& pow(const char *const expression) {
  22513. return pow((+*this)._fill(expression,true,true,0,0,"pow",this));
  22514. }
  22515. //! Raise each pixel value to a power, specified from an expression \newinstance.
  22516. CImg<Tfloat> get_pow(const char *const expression) const {
  22517. return CImg<Tfloat>(*this,false).pow(expression);
  22518. }
  22519. //! Raise each pixel value to a power, pointwisely specified from another image.
  22520. /**
  22521. Similar to operator+=(const CImg<t>& img), except that it performs an exponentiation instead of an addition.
  22522. **/
  22523. template<typename t>
  22524. CImg<T>& pow(const CImg<t>& img) {
  22525. const ulongT siz = size(), isiz = img.size();
  22526. if (siz && isiz) {
  22527. if (is_overlapped(img)) return pow(+img);
  22528. T *ptrd = _data, *const ptre = _data + siz;
  22529. if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
  22530. for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
  22531. *ptrd = (T)std::pow((double)*ptrd,(double)(*(ptrs++)));
  22532. for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)std::pow((double)*ptrd,(double)(*(ptrs++)));
  22533. }
  22534. return *this;
  22535. }
  22536. //! Raise each pixel value to a power, pointwisely specified from another image \newinstance.
  22537. template<typename t>
  22538. CImg<Tfloat> get_pow(const CImg<t>& img) const {
  22539. return CImg<Tfloat>(*this,false).pow(img);
  22540. }
  22541. //! Compute the bitwise left rotation of each pixel value.
  22542. /**
  22543. Similar to operator<<=(unsigned int), except that it performs a left rotation instead of a left shift.
  22544. **/
  22545. CImg<T>& rol(const unsigned int n=1) {
  22546. if (is_empty()) return *this;
  22547. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
  22548. cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::rol(*ptrd,n);
  22549. return *this;
  22550. }
  22551. //! Compute the bitwise left rotation of each pixel value \newinstance.
  22552. CImg<T> get_rol(const unsigned int n=1) const {
  22553. return (+*this).rol(n);
  22554. }
  22555. //! Compute the bitwise left rotation of each pixel value.
  22556. /**
  22557. Similar to operator<<=(const char*), except that it performs a left rotation instead of a left shift.
  22558. **/
  22559. CImg<T>& rol(const char *const expression) {
  22560. return rol((+*this)._fill(expression,true,true,0,0,"rol",this));
  22561. }
  22562. //! Compute the bitwise left rotation of each pixel value \newinstance.
  22563. CImg<T> get_rol(const char *const expression) const {
  22564. return (+*this).rol(expression);
  22565. }
  22566. //! Compute the bitwise left rotation of each pixel value.
  22567. /**
  22568. Similar to operator<<=(const CImg<t>&), except that it performs a left rotation instead of a left shift.
  22569. **/
  22570. template<typename t>
  22571. CImg<T>& rol(const CImg<t>& img) {
  22572. const ulongT siz = size(), isiz = img.size();
  22573. if (siz && isiz) {
  22574. if (is_overlapped(img)) return rol(+img);
  22575. T *ptrd = _data, *const ptre = _data + siz;
  22576. if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
  22577. for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
  22578. *ptrd = (T)cimg::rol(*ptrd,(unsigned int)(*(ptrs++)));
  22579. for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)cimg::rol(*ptrd,(unsigned int)(*(ptrs++)));
  22580. }
  22581. return *this;
  22582. }
  22583. //! Compute the bitwise left rotation of each pixel value \newinstance.
  22584. template<typename t>
  22585. CImg<T> get_rol(const CImg<t>& img) const {
  22586. return (+*this).rol(img);
  22587. }
  22588. //! Compute the bitwise right rotation of each pixel value.
  22589. /**
  22590. Similar to operator>>=(unsigned int), except that it performs a right rotation instead of a right shift.
  22591. **/
  22592. CImg<T>& ror(const unsigned int n=1) {
  22593. if (is_empty()) return *this;
  22594. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
  22595. cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::ror(*ptrd,n);
  22596. return *this;
  22597. }
  22598. //! Compute the bitwise right rotation of each pixel value \newinstance.
  22599. CImg<T> get_ror(const unsigned int n=1) const {
  22600. return (+*this).ror(n);
  22601. }
  22602. //! Compute the bitwise right rotation of each pixel value.
  22603. /**
  22604. Similar to operator>>=(const char*), except that it performs a right rotation instead of a right shift.
  22605. **/
  22606. CImg<T>& ror(const char *const expression) {
  22607. return ror((+*this)._fill(expression,true,true,0,0,"ror",this));
  22608. }
  22609. //! Compute the bitwise right rotation of each pixel value \newinstance.
  22610. CImg<T> get_ror(const char *const expression) const {
  22611. return (+*this).ror(expression);
  22612. }
  22613. //! Compute the bitwise right rotation of each pixel value.
  22614. /**
  22615. Similar to operator>>=(const CImg<t>&), except that it performs a right rotation instead of a right shift.
  22616. **/
  22617. template<typename t>
  22618. CImg<T>& ror(const CImg<t>& img) {
  22619. const ulongT siz = size(), isiz = img.size();
  22620. if (siz && isiz) {
  22621. if (is_overlapped(img)) return ror(+img);
  22622. T *ptrd = _data, *const ptre = _data + siz;
  22623. if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
  22624. for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
  22625. *ptrd = (T)cimg::ror(*ptrd,(unsigned int)(*(ptrs++)));
  22626. for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)cimg::ror(*ptrd,(unsigned int)(*(ptrs++)));
  22627. }
  22628. return *this;
  22629. }
  22630. //! Compute the bitwise right rotation of each pixel value \newinstance.
  22631. template<typename t>
  22632. CImg<T> get_ror(const CImg<t>& img) const {
  22633. return (+*this).ror(img);
  22634. }
  22635. //! Pointwise min operator between instance image and a value.
  22636. /**
  22637. \param val Value used as the reference argument of the min operator.
  22638. \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
  22639. \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{val})\f$.
  22640. **/
  22641. CImg<T>& min(const T& val) {
  22642. if (is_empty()) return *this;
  22643. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=65536))
  22644. cimg_rof(*this,ptrd,T) *ptrd = std::min(*ptrd,val);
  22645. return *this;
  22646. }
  22647. //! Pointwise min operator between instance image and a value \newinstance.
  22648. CImg<T> get_min(const T& val) const {
  22649. return (+*this).min(val);
  22650. }
  22651. //! Pointwise min operator between two images.
  22652. /**
  22653. \param img Image used as the reference argument of the min operator.
  22654. \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
  22655. \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$.
  22656. **/
  22657. template<typename t>
  22658. CImg<T>& min(const CImg<t>& img) {
  22659. const ulongT siz = size(), isiz = img.size();
  22660. if (siz && isiz) {
  22661. if (is_overlapped(img)) return min(+img);
  22662. T *ptrd = _data, *const ptre = _data + siz;
  22663. if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
  22664. for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
  22665. *ptrd = std::min((T)*(ptrs++),*ptrd);
  22666. for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = std::min((T)*(ptrs++),*ptrd);
  22667. }
  22668. return *this;
  22669. }
  22670. //! Pointwise min operator between two images \newinstance.
  22671. template<typename t>
  22672. CImg<_cimg_Tt> get_min(const CImg<t>& img) const {
  22673. return CImg<_cimg_Tt>(*this,false).min(img);
  22674. }
  22675. //! Pointwise min operator between an image and an expression.
  22676. /**
  22677. \param expression Math formula as a C-string.
  22678. \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
  22679. \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$.
  22680. **/
  22681. CImg<T>& min(const char *const expression) {
  22682. return min((+*this)._fill(expression,true,true,0,0,"min",this));
  22683. }
  22684. //! Pointwise min operator between an image and an expression \newinstance.
  22685. CImg<Tfloat> get_min(const char *const expression) const {
  22686. return CImg<Tfloat>(*this,false).min(expression);
  22687. }
  22688. //! Pointwise max operator between instance image and a value.
  22689. /**
  22690. \param val Value used as the reference argument of the max operator.
  22691. \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
  22692. \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{val})\f$.
  22693. **/
  22694. CImg<T>& max(const T& val) {
  22695. if (is_empty()) return *this;
  22696. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=65536))
  22697. cimg_rof(*this,ptrd,T) *ptrd = std::max(*ptrd,val);
  22698. return *this;
  22699. }
  22700. //! Pointwise max operator between instance image and a value \newinstance.
  22701. CImg<T> get_max(const T& val) const {
  22702. return (+*this).max(val);
  22703. }
  22704. //! Pointwise max operator between two images.
  22705. /**
  22706. \param img Image used as the reference argument of the max operator.
  22707. \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
  22708. \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$.
  22709. **/
  22710. template<typename t>
  22711. CImg<T>& max(const CImg<t>& img) {
  22712. const ulongT siz = size(), isiz = img.size();
  22713. if (siz && isiz) {
  22714. if (is_overlapped(img)) return max(+img);
  22715. T *ptrd = _data, *const ptre = _data + siz;
  22716. if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
  22717. for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
  22718. *ptrd = std::max((T)*(ptrs++),*ptrd);
  22719. for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = std::max((T)*(ptrs++),*ptrd);
  22720. }
  22721. return *this;
  22722. }
  22723. //! Pointwise max operator between two images \newinstance.
  22724. template<typename t>
  22725. CImg<_cimg_Tt> get_max(const CImg<t>& img) const {
  22726. return CImg<_cimg_Tt>(*this,false).max(img);
  22727. }
  22728. //! Pointwise max operator between an image and an expression.
  22729. /**
  22730. \param expression Math formula as a C-string.
  22731. \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
  22732. \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$.
  22733. **/
  22734. CImg<T>& max(const char *const expression) {
  22735. return max((+*this)._fill(expression,true,true,0,0,"max",this));
  22736. }
  22737. //! Pointwise max operator between an image and an expression \newinstance.
  22738. CImg<Tfloat> get_max(const char *const expression) const {
  22739. return CImg<Tfloat>(*this,false).max(expression);
  22740. }
  22741. //! Return a reference to the minimum pixel value.
  22742. /**
  22743. **/
  22744. T& min() {
  22745. if (is_empty())
  22746. throw CImgInstanceException(_cimg_instance
  22747. "min(): Empty instance.",
  22748. cimg_instance);
  22749. T *ptr_min = _data;
  22750. T min_value = *ptr_min;
  22751. cimg_for(*this,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
  22752. return *ptr_min;
  22753. }
  22754. //! Return a reference to the minimum pixel value \const.
  22755. const T& min() const {
  22756. if (is_empty())
  22757. throw CImgInstanceException(_cimg_instance
  22758. "min(): Empty instance.",
  22759. cimg_instance);
  22760. const T *ptr_min = _data;
  22761. T min_value = *ptr_min;
  22762. cimg_for(*this,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
  22763. return *ptr_min;
  22764. }
  22765. //! Return a reference to the maximum pixel value.
  22766. /**
  22767. **/
  22768. T& max() {
  22769. if (is_empty())
  22770. throw CImgInstanceException(_cimg_instance
  22771. "max(): Empty instance.",
  22772. cimg_instance);
  22773. T *ptr_max = _data;
  22774. T max_value = *ptr_max;
  22775. cimg_for(*this,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
  22776. return *ptr_max;
  22777. }
  22778. //! Return a reference to the maximum pixel value \const.
  22779. const T& max() const {
  22780. if (is_empty())
  22781. throw CImgInstanceException(_cimg_instance
  22782. "max(): Empty instance.",
  22783. cimg_instance);
  22784. const T *ptr_max = _data;
  22785. T max_value = *ptr_max;
  22786. cimg_for(*this,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
  22787. return *ptr_max;
  22788. }
  22789. //! Return a reference to the minimum pixel value as well as the maximum pixel value.
  22790. /**
  22791. \param[out] max_val Maximum pixel value.
  22792. **/
  22793. template<typename t>
  22794. T& min_max(t& max_val) {
  22795. if (is_empty())
  22796. throw CImgInstanceException(_cimg_instance
  22797. "min_max(): Empty instance.",
  22798. cimg_instance);
  22799. T *ptr_min = _data;
  22800. T min_value = *ptr_min, max_value = min_value;
  22801. cimg_for(*this,ptrs,T) {
  22802. const T val = *ptrs;
  22803. if (val<min_value) { min_value = val; ptr_min = ptrs; }
  22804. if (val>max_value) max_value = val;
  22805. }
  22806. max_val = (t)max_value;
  22807. return *ptr_min;
  22808. }
  22809. //! Return a reference to the minimum pixel value as well as the maximum pixel value \const.
  22810. template<typename t>
  22811. const T& min_max(t& max_val) const {
  22812. if (is_empty())
  22813. throw CImgInstanceException(_cimg_instance
  22814. "min_max(): Empty instance.",
  22815. cimg_instance);
  22816. const T *ptr_min = _data;
  22817. T min_value = *ptr_min, max_value = min_value;
  22818. cimg_for(*this,ptrs,T) {
  22819. const T val = *ptrs;
  22820. if (val<min_value) { min_value = val; ptr_min = ptrs; }
  22821. if (val>max_value) max_value = val;
  22822. }
  22823. max_val = (t)max_value;
  22824. return *ptr_min;
  22825. }
  22826. //! Return a reference to the maximum pixel value as well as the minimum pixel value.
  22827. /**
  22828. \param[out] min_val Minimum pixel value.
  22829. **/
  22830. template<typename t>
  22831. T& max_min(t& min_val) {
  22832. if (is_empty())
  22833. throw CImgInstanceException(_cimg_instance
  22834. "max_min(): Empty instance.",
  22835. cimg_instance);
  22836. T *ptr_max = _data;
  22837. T max_value = *ptr_max, min_value = max_value;
  22838. cimg_for(*this,ptrs,T) {
  22839. const T val = *ptrs;
  22840. if (val>max_value) { max_value = val; ptr_max = ptrs; }
  22841. if (val<min_value) min_value = val;
  22842. }
  22843. min_val = (t)min_value;
  22844. return *ptr_max;
  22845. }
  22846. //! Return a reference to the maximum pixel value as well as the minimum pixel value \const.
  22847. template<typename t>
  22848. const T& max_min(t& min_val) const {
  22849. if (is_empty())
  22850. throw CImgInstanceException(_cimg_instance
  22851. "max_min(): Empty instance.",
  22852. cimg_instance);
  22853. const T *ptr_max = _data;
  22854. T max_value = *ptr_max, min_value = max_value;
  22855. cimg_for(*this,ptrs,T) {
  22856. const T val = *ptrs;
  22857. if (val>max_value) { max_value = val; ptr_max = ptrs; }
  22858. if (val<min_value) min_value = val;
  22859. }
  22860. min_val = (t)min_value;
  22861. return *ptr_max;
  22862. }
  22863. //! Return the kth smallest pixel value.
  22864. /**
  22865. \param k Rank of the search smallest element.
  22866. **/
  22867. T kth_smallest(const ulongT k) const {
  22868. if (is_empty())
  22869. throw CImgInstanceException(_cimg_instance
  22870. "kth_smallest(): Empty instance.",
  22871. cimg_instance);
  22872. CImg<T> arr(*this,false);
  22873. ulongT l = 0, ir = size() - 1;
  22874. for ( ; ; ) {
  22875. if (ir<=l + 1) {
  22876. if (ir==l + 1 && arr[ir]<arr[l]) cimg::swap(arr[l],arr[ir]);
  22877. return arr[k];
  22878. } else {
  22879. const ulongT mid = (l + ir)>>1;
  22880. cimg::swap(arr[mid],arr[l + 1]);
  22881. if (arr[l]>arr[ir]) cimg::swap(arr[l],arr[ir]);
  22882. if (arr[l + 1]>arr[ir]) cimg::swap(arr[l + 1],arr[ir]);
  22883. if (arr[l]>arr[l + 1]) cimg::swap(arr[l],arr[l + 1]);
  22884. ulongT i = l + 1, j = ir;
  22885. const T pivot = arr[l + 1];
  22886. for ( ; ; ) {
  22887. do ++i; while (arr[i]<pivot);
  22888. do --j; while (arr[j]>pivot);
  22889. if (j<i) break;
  22890. cimg::swap(arr[i],arr[j]);
  22891. }
  22892. arr[l + 1] = arr[j];
  22893. arr[j] = pivot;
  22894. if (j>=k) ir = j - 1;
  22895. if (j<=k) l = i;
  22896. }
  22897. }
  22898. }
  22899. //! Return the median pixel value.
  22900. /**
  22901. **/
  22902. T median() const {
  22903. if (is_empty())
  22904. throw CImgInstanceException(_cimg_instance
  22905. "median(): Empty instance.",
  22906. cimg_instance);
  22907. const ulongT s = size();
  22908. switch (s) {
  22909. case 1 : return _data[0];
  22910. case 2 : return cimg::median(_data[0],_data[1]);
  22911. case 3 : return cimg::median(_data[0],_data[1],_data[2]);
  22912. case 5 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4]);
  22913. case 7 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6]);
  22914. case 9 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6],_data[7],_data[8]);
  22915. case 13 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6],_data[7],_data[8],
  22916. _data[9],_data[10],_data[11],_data[12]);
  22917. }
  22918. const T res = kth_smallest(s>>1);
  22919. return (s%2)?res:(T)((res + kth_smallest((s>>1) - 1))/2);
  22920. }
  22921. //! Return the product of all the pixel values.
  22922. /**
  22923. **/
  22924. double product() const {
  22925. if (is_empty()) return 0;
  22926. double res = 1;
  22927. cimg_for(*this,ptrs,T) res*=(double)*ptrs;
  22928. return res;
  22929. }
  22930. //! Return the sum of all the pixel values.
  22931. /**
  22932. **/
  22933. double sum() const {
  22934. double res = 0;
  22935. cimg_for(*this,ptrs,T) res+=(double)*ptrs;
  22936. return res;
  22937. }
  22938. //! Return the average pixel value.
  22939. /**
  22940. **/
  22941. double mean() const {
  22942. double res = 0;
  22943. cimg_for(*this,ptrs,T) res+=(double)*ptrs;
  22944. return res/size();
  22945. }
  22946. //! Return the variance of the pixel values.
  22947. /**
  22948. \param variance_method Method used to estimate the variance. Can be:
  22949. - \c 0: Second moment, computed as
  22950. \f$1/N \sum\limits_{k=1}^{N} (x_k - \bar x)^2 =
  22951. 1/N \left( \sum\limits_{k=1}^N x_k^2 - \left( \sum\limits_{k=1}^N x_k \right)^2 / N \right)\f$
  22952. with \f$ \bar x = 1/N \sum\limits_{k=1}^N x_k \f$.
  22953. - \c 1: Best unbiased estimator, computed as \f$\frac{1}{N - 1} \sum\limits_{k=1}^{N} (x_k - \bar x)^2 \f$.
  22954. - \c 2: Least median of squares.
  22955. - \c 3: Least trimmed of squares.
  22956. **/
  22957. double variance(const unsigned int variance_method=1) const {
  22958. double foo;
  22959. return variance_mean(variance_method,foo);
  22960. }
  22961. //! Return the variance as well as the average of the pixel values.
  22962. /**
  22963. \param variance_method Method used to estimate the variance (see variance(const unsigned int) const).
  22964. \param[out] mean Average pixel value.
  22965. **/
  22966. template<typename t>
  22967. double variance_mean(const unsigned int variance_method, t& mean) const {
  22968. if (is_empty())
  22969. throw CImgInstanceException(_cimg_instance
  22970. "variance_mean(): Empty instance.",
  22971. cimg_instance);
  22972. double variance = 0, average = 0;
  22973. const ulongT siz = size();
  22974. switch (variance_method) {
  22975. case 0 : { // Least mean square (standard definition)
  22976. double S = 0, S2 = 0;
  22977. cimg_for(*this,ptrs,T) { const double val = (double)*ptrs; S+=val; S2+=val*val; }
  22978. variance = (S2 - S*S/siz)/siz;
  22979. average = S;
  22980. } break;
  22981. case 1 : { // Least mean square (robust definition)
  22982. double S = 0, S2 = 0;
  22983. cimg_for(*this,ptrs,T) { const double val = (double)*ptrs; S+=val; S2+=val*val; }
  22984. variance = siz>1?(S2 - S*S/siz)/(siz - 1):0;
  22985. average = S;
  22986. } break;
  22987. case 2 : { // Least Median of Squares (MAD)
  22988. CImg<Tfloat> buf(*this,false);
  22989. buf.sort();
  22990. const ulongT siz2 = siz>>1;
  22991. const double med_i = (double)buf[siz2];
  22992. cimg_for(buf,ptrs,Tfloat) {
  22993. const double val = (double)*ptrs; *ptrs = (Tfloat)cimg::abs(val - med_i); average+=val;
  22994. }
  22995. buf.sort();
  22996. const double sig = (double)(1.4828*buf[siz2]);
  22997. variance = sig*sig;
  22998. } break;
  22999. default : { // Least trimmed of Squares
  23000. CImg<Tfloat> buf(*this,false);
  23001. const ulongT siz2 = siz>>1;
  23002. cimg_for(buf,ptrs,Tfloat) {
  23003. const double val = (double)*ptrs; (*ptrs)=(Tfloat)((*ptrs)*val); average+=val;
  23004. }
  23005. buf.sort();
  23006. double a = 0;
  23007. const Tfloat *ptrs = buf._data;
  23008. for (ulongT j = 0; j<siz2; ++j) a+=(double)*(ptrs++);
  23009. const double sig = (double)(2.6477*std::sqrt(a/siz2));
  23010. variance = sig*sig;
  23011. }
  23012. }
  23013. mean = (t)(average/siz);
  23014. return variance>0?variance:0;
  23015. }
  23016. //! Return estimated variance of the noise.
  23017. /**
  23018. \param variance_method Method used to compute the variance (see variance(const unsigned int) const).
  23019. \note Because of structures such as edges in images it is
  23020. recommanded to use a robust variance estimation. The variance of the
  23021. noise is estimated by computing the variance of the Laplacian \f$(\Delta
  23022. I)^2 \f$ scaled by a factor \f$c\f$ insuring \f$ c E[(\Delta I)^2]=
  23023. \sigma^2\f$ where \f$\sigma\f$ is the noise variance.
  23024. **/
  23025. double variance_noise(const unsigned int variance_method=2) const {
  23026. if (is_empty())
  23027. throw CImgInstanceException(_cimg_instance
  23028. "variance_noise(): Empty instance.",
  23029. cimg_instance);
  23030. const ulongT siz = size();
  23031. if (!siz || !_data) return 0;
  23032. if (variance_method>1) { // Compute a scaled version of the Laplacian.
  23033. CImg<Tdouble> tmp(*this,false);
  23034. if (_depth==1) {
  23035. const double cste = 1.0/std::sqrt(20.0); // Depends on how the Laplacian is computed.
  23036. cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=262144 && _spectrum>=2))
  23037. cimg_forC(*this,c) {
  23038. CImg_3x3(I,T);
  23039. cimg_for3x3(*this,x,y,0,c,I,T) {
  23040. tmp(x,y,c) = cste*((double)Inc + (double)Ipc + (double)Icn +
  23041. (double)Icp - 4*(double)Icc);
  23042. }
  23043. }
  23044. } else {
  23045. const double cste = 1.0/std::sqrt(42.0); // Depends on how the Laplacian is computed.
  23046. cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=262144 && _spectrum>=2))
  23047. cimg_forC(*this,c) {
  23048. CImg_3x3x3(I,T);
  23049. cimg_for3x3x3(*this,x,y,z,c,I,T) {
  23050. tmp(x,y,z,c) = cste*(
  23051. (double)Incc + (double)Ipcc + (double)Icnc + (double)Icpc +
  23052. (double)Iccn + (double)Iccp - 6*(double)Iccc);
  23053. }
  23054. }
  23055. }
  23056. return tmp.variance(variance_method);
  23057. }
  23058. // Version that doesn't need intermediate images.
  23059. double variance = 0, S = 0, S2 = 0;
  23060. if (_depth==1) {
  23061. const double cste = 1.0/std::sqrt(20.0);
  23062. CImg_3x3(I,T);
  23063. cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T) {
  23064. const double val = cste*((double)Inc + (double)Ipc +
  23065. (double)Icn + (double)Icp - 4*(double)Icc);
  23066. S+=val; S2+=val*val;
  23067. }
  23068. } else {
  23069. const double cste = 1.0/std::sqrt(42.0);
  23070. CImg_3x3x3(I,T);
  23071. cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,T) {
  23072. const double val = cste *
  23073. ((double)Incc + (double)Ipcc + (double)Icnc +
  23074. (double)Icpc +
  23075. (double)Iccn + (double)Iccp - 6*(double)Iccc);
  23076. S+=val; S2+=val*val;
  23077. }
  23078. }
  23079. if (variance_method) variance = siz>1?(S2 - S*S/siz)/(siz - 1):0;
  23080. else variance = (S2 - S*S/siz)/siz;
  23081. return variance>0?variance:0;
  23082. }
  23083. //! Compute the MSE (Mean-Squared Error) between two images.
  23084. /**
  23085. \param img Image used as the second argument of the MSE operator.
  23086. **/
  23087. template<typename t>
  23088. double MSE(const CImg<t>& img) const {
  23089. if (img.size()!=size())
  23090. throw CImgArgumentException(_cimg_instance
  23091. "MSE(): Instance and specified image (%u,%u,%u,%u,%p) have different dimensions.",
  23092. cimg_instance,
  23093. img._width,img._height,img._depth,img._spectrum,img._data);
  23094. double vMSE = 0;
  23095. const t* ptr2 = img._data;
  23096. cimg_for(*this,ptr1,T) {
  23097. const double diff = (double)*ptr1 - (double)*(ptr2++);
  23098. vMSE+=diff*diff;
  23099. }
  23100. const ulongT siz = img.size();
  23101. if (siz) vMSE/=siz;
  23102. return vMSE;
  23103. }
  23104. //! Compute the PSNR (Peak Signal-to-Noise Ratio) between two images.
  23105. /**
  23106. \param img Image used as the second argument of the PSNR operator.
  23107. \param max_value Maximum theoretical value of the signal.
  23108. **/
  23109. template<typename t>
  23110. double PSNR(const CImg<t>& img, const double max_value=255) const {
  23111. const double vMSE = (double)std::sqrt(MSE(img));
  23112. return (vMSE!=0)?(double)(20*std::log10(max_value/vMSE)):(double)(cimg::type<double>::max());
  23113. }
  23114. //! Evaluate math formula.
  23115. /**
  23116. \param expression Math formula, as a C-string.
  23117. \param x Value of the pre-defined variable \c x.
  23118. \param y Value of the pre-defined variable \c y.
  23119. \param z Value of the pre-defined variable \c z.
  23120. \param c Value of the pre-defined variable \c c.
  23121. \param list_inputs A list of input images attached to the specified math formula.
  23122. \param[out] list_outputs A pointer to a list of output images attached to the specified math formula.
  23123. **/
  23124. double eval(const char *const expression,
  23125. const double x=0, const double y=0, const double z=0, const double c=0,
  23126. const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) {
  23127. return _eval(this,expression,x,y,z,c,list_inputs,list_outputs);
  23128. }
  23129. //! Evaluate math formula \const.
  23130. double eval(const char *const expression,
  23131. const double x=0, const double y=0, const double z=0, const double c=0,
  23132. const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const {
  23133. return _eval(0,expression,x,y,z,c,list_inputs,list_outputs);
  23134. }
  23135. double _eval(CImg<T> *const img_output, const char *const expression,
  23136. const double x, const double y, const double z, const double c,
  23137. const CImgList<T> *const list_inputs, CImgList<T> *const list_outputs) const {
  23138. if (!expression || !*expression) return 0;
  23139. if (!expression[1]) switch (*expression) { // Single-char optimization.
  23140. case 'w' : return (double)_width;
  23141. case 'h' : return (double)_height;
  23142. case 'd' : return (double)_depth;
  23143. case 's' : return (double)_spectrum;
  23144. case 'r' : return (double)_is_shared;
  23145. }
  23146. _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' ||
  23147. *expression=='*' || *expression==':'),"eval",
  23148. *this,img_output,list_inputs,list_outputs,false);
  23149. const double val = mp(x,y,z,c);
  23150. mp.end();
  23151. return val;
  23152. }
  23153. //! Evaluate math formula.
  23154. /**
  23155. \param[out] output Contains values of output vector returned by the evaluated expression
  23156. (or is empty if the returned type is scalar).
  23157. \param expression Math formula, as a C-string.
  23158. \param x Value of the pre-defined variable \c x.
  23159. \param y Value of the pre-defined variable \c y.
  23160. \param z Value of the pre-defined variable \c z.
  23161. \param c Value of the pre-defined variable \c c.
  23162. \param list_inputs A list of input images attached to the specified math formula.
  23163. \param[out] list_outputs A pointer to a list of output images attached to the specified math formula.
  23164. **/
  23165. template<typename t>
  23166. void eval(CImg<t> &output, const char *const expression,
  23167. const double x=0, const double y=0, const double z=0, const double c=0,
  23168. const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) {
  23169. _eval(output,this,expression,x,y,z,c,list_inputs,list_outputs);
  23170. }
  23171. //! Evaluate math formula \const.
  23172. template<typename t>
  23173. void eval(CImg<t>& output, const char *const expression,
  23174. const double x=0, const double y=0, const double z=0, const double c=0,
  23175. const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const {
  23176. _eval(output,0,expression,x,y,z,c,list_inputs,list_outputs);
  23177. }
  23178. template<typename t>
  23179. void _eval(CImg<t>& output, CImg<T> *const img_output, const char *const expression,
  23180. const double x, const double y, const double z, const double c,
  23181. const CImgList<T> *const list_inputs, CImgList<T> *const list_outputs) const {
  23182. if (!expression || !*expression) { output.assign(1); *output = 0; }
  23183. if (!expression[1]) switch (*expression) { // Single-char optimization.
  23184. case 'w' : output.assign(1); *output = (t)_width; break;
  23185. case 'h' : output.assign(1); *output = (t)_height; break;
  23186. case 'd' : output.assign(1); *output = (t)_depth; break;
  23187. case 's' : output.assign(1); *output = (t)_spectrum; break;
  23188. case 'r' : output.assign(1); *output = (t)_is_shared; break;
  23189. }
  23190. _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' ||
  23191. *expression=='*' || *expression==':'),"eval",
  23192. *this,img_output,list_inputs,list_outputs,false);
  23193. output.assign(1,std::max(1U,mp.result_dim));
  23194. mp(x,y,z,c,output._data);
  23195. mp.end();
  23196. }
  23197. //! Evaluate math formula on a set of variables.
  23198. /**
  23199. \param expression Math formula, as a C-string.
  23200. \param xyzc Set of values (x,y,z,c) used for the evaluation.
  23201. \param list_inputs A list of input images attached to the specified math formula.
  23202. \param[out] list_outputs A pointer to a list of output images attached to the specified math formula.
  23203. **/
  23204. template<typename t>
  23205. CImg<doubleT> eval(const char *const expression, const CImg<t>& xyzc,
  23206. const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) {
  23207. return _eval(this,expression,xyzc,list_inputs,list_outputs);
  23208. }
  23209. //! Evaluate math formula on a set of variables \const.
  23210. template<typename t>
  23211. CImg<doubleT> eval(const char *const expression, const CImg<t>& xyzc,
  23212. const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const {
  23213. return _eval(0,expression,xyzc,list_inputs,list_outputs);
  23214. }
  23215. template<typename t>
  23216. CImg<doubleT> _eval(CImg<T> *const output, const char *const expression, const CImg<t>& xyzc,
  23217. const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const {
  23218. CImg<doubleT> res(1,xyzc.size()/4);
  23219. if (!expression || !*expression) return res.fill(0);
  23220. _cimg_math_parser mp(expression,"eval",*this,output,list_inputs,list_outputs,false);
  23221. #ifdef cimg_use_openmp
  23222. cimg_pragma_openmp(parallel if (res._height>=512))
  23223. {
  23224. _cimg_math_parser
  23225. _mp = omp_get_thread_num()?mp:_cimg_math_parser(),
  23226. &lmp = omp_get_thread_num()?_mp:mp;
  23227. cimg_pragma_openmp(for)
  23228. for (unsigned int i = 0; i<res._height; ++i) {
  23229. const unsigned int i4 = 4*i;
  23230. const double
  23231. x = (double)xyzc[i4], y = (double)xyzc[i4 + 1],
  23232. z = (double)xyzc[i4 + 2], c = (double)xyzc[i4 + 3];
  23233. res[i] = lmp(x,y,z,c);
  23234. }
  23235. }
  23236. #else
  23237. const t *ps = xyzc._data;
  23238. cimg_for(res,pd,double) {
  23239. const double x = (double)*(ps++), y = (double)*(ps++), z = (double)*(ps++), c = (double)*(ps++);
  23240. *pd = mp(x,y,z,c);
  23241. }
  23242. #endif
  23243. mp.end();
  23244. return res;
  23245. }
  23246. //! Compute statistics vector from the pixel values.
  23247. /*
  23248. \param variance_method Method used to compute the variance (see variance(const unsigned int) const).
  23249. \return Statistics vector as
  23250. <tt>[min, max, mean, variance, xmin, ymin, zmin, cmin, xmax, ymax, zmax, cmax, sum, product]</tt>.
  23251. **/
  23252. CImg<Tdouble> get_stats(const unsigned int variance_method=1) const {
  23253. if (is_empty()) return CImg<doubleT>();
  23254. const T *const p_end = end(), *pm = _data, *pM = _data;
  23255. double S = 0, S2 = 0, P = 1;
  23256. const ulongT siz = size();
  23257. T m = *pm, M = *pM;
  23258. cimg_pragma_openmp(parallel reduction(+:S,S2) reduction(*:P) cimg_openmp_if(siz>=131072)) {
  23259. const T *lpm = _data, *lpM = _data;
  23260. T lm = *lpm, lM = *lpM;
  23261. cimg_pragma_openmp(for)
  23262. for (const T *ptrs = _data; ptrs<p_end; ++ptrs) {
  23263. const T val = *ptrs;
  23264. const double _val = (double)val;
  23265. if (val<lm) { lm = val; lpm = ptrs; }
  23266. if (val>lM) { lM = val; lpM = ptrs; }
  23267. S+=_val;
  23268. S2+=_val*_val;
  23269. P*=_val;
  23270. }
  23271. cimg_pragma_openmp(critical(get_stats)) {
  23272. if (lm<m || (lm==m && lpm<pm)) { m = lm; pm = lpm; }
  23273. if (lM>M || (lM==M && lpM<pM)) { M = lM; pM = lpM; }
  23274. }
  23275. }
  23276. const double
  23277. mean_value = S/siz,
  23278. _variance_value = variance_method==0?(S2 - S*S/siz)/siz:
  23279. (variance_method==1?(siz>1?(S2 - S*S/siz)/(siz - 1):0):
  23280. variance(variance_method)),
  23281. variance_value = _variance_value>0?_variance_value:0;
  23282. int
  23283. xm = 0, ym = 0, zm = 0, cm = 0,
  23284. xM = 0, yM = 0, zM = 0, cM = 0;
  23285. contains(*pm,xm,ym,zm,cm);
  23286. contains(*pM,xM,yM,zM,cM);
  23287. return CImg<Tdouble>(1,14).fill((double)m,(double)M,mean_value,variance_value,
  23288. (double)xm,(double)ym,(double)zm,(double)cm,
  23289. (double)xM,(double)yM,(double)zM,(double)cM,
  23290. S,P);
  23291. }
  23292. //! Compute statistics vector from the pixel values \inplace.
  23293. CImg<T>& stats(const unsigned int variance_method=1) {
  23294. return get_stats(variance_method).move_to(*this);
  23295. }
  23296. //@}
  23297. //-------------------------------------
  23298. //
  23299. //! \name Vector / Matrix Operations
  23300. //@{
  23301. //-------------------------------------
  23302. //! Compute norm of the image, viewed as a matrix.
  23303. /**
  23304. \param magnitude_type Norm type. Can be:
  23305. - \c -1: Linf-norm
  23306. - \c 0: L0-norm
  23307. - \c 1: L1-norm
  23308. - \c 2: L2-norm
  23309. **/
  23310. double magnitude(const int magnitude_type=2) const {
  23311. if (is_empty())
  23312. throw CImgInstanceException(_cimg_instance
  23313. "magnitude(): Empty instance.",
  23314. cimg_instance);
  23315. double res = 0;
  23316. switch (magnitude_type) {
  23317. case -1 : {
  23318. cimg_for(*this,ptrs,T) { const double val = (double)cimg::abs(*ptrs); if (val>res) res = val; }
  23319. } break;
  23320. case 1 : {
  23321. cimg_for(*this,ptrs,T) res+=(double)cimg::abs(*ptrs);
  23322. } break;
  23323. default : {
  23324. cimg_for(*this,ptrs,T) res+=(double)cimg::sqr(*ptrs);
  23325. res = (double)std::sqrt(res);
  23326. }
  23327. }
  23328. return res;
  23329. }
  23330. //! Compute the trace of the image, viewed as a matrix.
  23331. /**
  23332. **/
  23333. double trace() const {
  23334. if (is_empty())
  23335. throw CImgInstanceException(_cimg_instance
  23336. "trace(): Empty instance.",
  23337. cimg_instance);
  23338. double res = 0;
  23339. cimg_forX(*this,k) res+=(double)(*this)(k,k);
  23340. return res;
  23341. }
  23342. //! Compute the determinant of the image, viewed as a matrix.
  23343. /**
  23344. **/
  23345. double det() const {
  23346. if (is_empty() || _width!=_height || _depth!=1 || _spectrum!=1)
  23347. throw CImgInstanceException(_cimg_instance
  23348. "det(): Instance is not a square matrix.",
  23349. cimg_instance);
  23350. switch (_width) {
  23351. case 1 : return (double)((*this)(0,0));
  23352. case 2 : return (double)((*this)(0,0))*(double)((*this)(1,1)) - (double)((*this)(0,1))*(double)((*this)(1,0));
  23353. case 3 : {
  23354. const double
  23355. a = (double)_data[0], d = (double)_data[1], g = (double)_data[2],
  23356. b = (double)_data[3], e = (double)_data[4], h = (double)_data[5],
  23357. c = (double)_data[6], f = (double)_data[7], i = (double)_data[8];
  23358. return i*a*e - a*h*f - i*b*d + b*g*f + c*d*h - c*g*e;
  23359. }
  23360. default : {
  23361. CImg<Tfloat> lu(*this,false);
  23362. CImg<uintT> indx;
  23363. bool d;
  23364. lu._LU(indx,d);
  23365. double res = d?(double)1:(double)-1;
  23366. cimg_forX(lu,i) res*=lu(i,i);
  23367. return res;
  23368. }
  23369. }
  23370. }
  23371. //! Compute the dot product between instance and argument, viewed as matrices.
  23372. /**
  23373. \param img Image used as a second argument of the dot product.
  23374. **/
  23375. template<typename t>
  23376. double dot(const CImg<t>& img) const {
  23377. if (is_empty())
  23378. throw CImgInstanceException(_cimg_instance
  23379. "dot(): Empty instance.",
  23380. cimg_instance);
  23381. if (!img)
  23382. throw CImgArgumentException(_cimg_instance
  23383. "dot(): Empty specified image.",
  23384. cimg_instance);
  23385. const ulongT nb = std::min(size(),img.size());
  23386. double res = 0;
  23387. for (ulongT off = 0; off<nb; ++off) res+=(double)_data[off]*(double)img[off];
  23388. return res;
  23389. }
  23390. //! Get vector-valued pixel located at specified position.
  23391. /**
  23392. \param x X-coordinate of the pixel value.
  23393. \param y Y-coordinate of the pixel value.
  23394. \param z Z-coordinate of the pixel value.
  23395. **/
  23396. CImg<T> get_vector_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const {
  23397. CImg<T> res;
  23398. if (res._height!=_spectrum) res.assign(1,_spectrum);
  23399. const ulongT whd = (ulongT)_width*_height*_depth;
  23400. const T *ptrs = data(x,y,z);
  23401. T *ptrd = res._data;
  23402. cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  23403. return res;
  23404. }
  23405. //! Get (square) matrix-valued pixel located at specified position.
  23406. /**
  23407. \param x X-coordinate of the pixel value.
  23408. \param y Y-coordinate of the pixel value.
  23409. \param z Z-coordinate of the pixel value.
  23410. \note - The spectrum() of the image must be a square.
  23411. **/
  23412. CImg<T> get_matrix_at(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) const {
  23413. const int n = (int)cimg::round(std::sqrt((double)_spectrum));
  23414. const T *ptrs = data(x,y,z,0);
  23415. const ulongT whd = (ulongT)_width*_height*_depth;
  23416. CImg<T> res(n,n);
  23417. T *ptrd = res._data;
  23418. cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  23419. return res;
  23420. }
  23421. //! Get tensor-valued pixel located at specified position.
  23422. /**
  23423. \param x X-coordinate of the pixel value.
  23424. \param y Y-coordinate of the pixel value.
  23425. \param z Z-coordinate of the pixel value.
  23426. **/
  23427. CImg<T> get_tensor_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const {
  23428. const T *ptrs = data(x,y,z,0);
  23429. const ulongT whd = (ulongT)_width*_height*_depth;
  23430. if (_spectrum==6)
  23431. return tensor(*ptrs,*(ptrs + whd),*(ptrs + 2*whd),*(ptrs + 3*whd),*(ptrs + 4*whd),*(ptrs + 5*whd));
  23432. if (_spectrum==3)
  23433. return tensor(*ptrs,*(ptrs + whd),*(ptrs + 2*whd));
  23434. return tensor(*ptrs);
  23435. }
  23436. //! Set vector-valued pixel at specified position.
  23437. /**
  23438. \param vec Vector to put on the instance image.
  23439. \param x X-coordinate of the pixel value.
  23440. \param y Y-coordinate of the pixel value.
  23441. \param z Z-coordinate of the pixel value.
  23442. **/
  23443. template<typename t>
  23444. CImg<T>& set_vector_at(const CImg<t>& vec, const unsigned int x, const unsigned int y=0, const unsigned int z=0) {
  23445. if (x<_width && y<_height && z<_depth) {
  23446. const t *ptrs = vec._data;
  23447. const ulongT whd = (ulongT)_width*_height*_depth;
  23448. T *ptrd = data(x,y,z);
  23449. for (unsigned int k = std::min((unsigned int)vec.size(),_spectrum); k; --k) {
  23450. *ptrd = (T)*(ptrs++); ptrd+=whd;
  23451. }
  23452. }
  23453. return *this;
  23454. }
  23455. //! Set (square) matrix-valued pixel at specified position.
  23456. /**
  23457. \param mat Matrix to put on the instance image.
  23458. \param x X-coordinate of the pixel value.
  23459. \param y Y-coordinate of the pixel value.
  23460. \param z Z-coordinate of the pixel value.
  23461. **/
  23462. template<typename t>
  23463. CImg<T>& set_matrix_at(const CImg<t>& mat, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) {
  23464. return set_vector_at(mat,x,y,z);
  23465. }
  23466. //! Set tensor-valued pixel at specified position.
  23467. /**
  23468. \param ten Tensor to put on the instance image.
  23469. \param x X-coordinate of the pixel value.
  23470. \param y Y-coordinate of the pixel value.
  23471. \param z Z-coordinate of the pixel value.
  23472. **/
  23473. template<typename t>
  23474. CImg<T>& set_tensor_at(const CImg<t>& ten, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) {
  23475. T *ptrd = data(x,y,z,0);
  23476. const ulongT siz = (ulongT)_width*_height*_depth;
  23477. if (ten._height==2) {
  23478. *ptrd = (T)ten[0]; ptrd+=siz;
  23479. *ptrd = (T)ten[1]; ptrd+=siz;
  23480. *ptrd = (T)ten[3];
  23481. }
  23482. else {
  23483. *ptrd = (T)ten[0]; ptrd+=siz;
  23484. *ptrd = (T)ten[1]; ptrd+=siz;
  23485. *ptrd = (T)ten[2]; ptrd+=siz;
  23486. *ptrd = (T)ten[4]; ptrd+=siz;
  23487. *ptrd = (T)ten[5]; ptrd+=siz;
  23488. *ptrd = (T)ten[8];
  23489. }
  23490. return *this;
  23491. }
  23492. //! Unroll pixel values along axis \c y.
  23493. /**
  23494. \note Equivalent to \code unroll('y'); \endcode.
  23495. **/
  23496. CImg<T>& vector() {
  23497. return unroll('y');
  23498. }
  23499. //! Unroll pixel values along axis \c y \newinstance.
  23500. CImg<T> get_vector() const {
  23501. return get_unroll('y');
  23502. }
  23503. //! Resize image to become a scalar square matrix.
  23504. /**
  23505. **/
  23506. CImg<T>& matrix() {
  23507. const ulongT siz = size();
  23508. switch (siz) {
  23509. case 1 : break;
  23510. case 4 : _width = _height = 2; break;
  23511. case 9 : _width = _height = 3; break;
  23512. case 16 : _width = _height = 4; break;
  23513. case 25 : _width = _height = 5; break;
  23514. case 36 : _width = _height = 6; break;
  23515. case 49 : _width = _height = 7; break;
  23516. case 64 : _width = _height = 8; break;
  23517. case 81 : _width = _height = 9; break;
  23518. case 100 : _width = _height = 10; break;
  23519. default : {
  23520. ulongT i = 11, i2 = i*i;
  23521. while (i2<siz) { i2+=2*i + 1; ++i; }
  23522. if (i2==siz) _width = _height = i;
  23523. else throw CImgInstanceException(_cimg_instance
  23524. "matrix(): Invalid instance size %u (should be a square integer).",
  23525. cimg_instance,
  23526. siz);
  23527. }
  23528. }
  23529. return *this;
  23530. }
  23531. //! Resize image to become a scalar square matrix \newinstance.
  23532. CImg<T> get_matrix() const {
  23533. return (+*this).matrix();
  23534. }
  23535. //! Resize image to become a symmetric tensor.
  23536. /**
  23537. **/
  23538. CImg<T>& tensor() {
  23539. return get_tensor().move_to(*this);
  23540. }
  23541. //! Resize image to become a symmetric tensor \newinstance.
  23542. CImg<T> get_tensor() const {
  23543. CImg<T> res;
  23544. const ulongT siz = size();
  23545. switch (siz) {
  23546. case 1 : break;
  23547. case 3 :
  23548. res.assign(2,2);
  23549. res(0,0) = (*this)(0);
  23550. res(1,0) = res(0,1) = (*this)(1);
  23551. res(1,1) = (*this)(2);
  23552. break;
  23553. case 6 :
  23554. res.assign(3,3);
  23555. res(0,0) = (*this)(0);
  23556. res(1,0) = res(0,1) = (*this)(1);
  23557. res(2,0) = res(0,2) = (*this)(2);
  23558. res(1,1) = (*this)(3);
  23559. res(2,1) = res(1,2) = (*this)(4);
  23560. res(2,2) = (*this)(5);
  23561. break;
  23562. default :
  23563. throw CImgInstanceException(_cimg_instance
  23564. "tensor(): Invalid instance size (does not define a 1x1, 2x2 or 3x3 tensor).",
  23565. cimg_instance);
  23566. }
  23567. return res;
  23568. }
  23569. //! Resize image to become a diagonal matrix.
  23570. /**
  23571. \note Transform the image as a diagonal matrix so that each of its initial value becomes a diagonal coefficient.
  23572. **/
  23573. CImg<T>& diagonal() {
  23574. return get_diagonal().move_to(*this);
  23575. }
  23576. //! Resize image to become a diagonal matrix \newinstance.
  23577. CImg<T> get_diagonal() const {
  23578. if (is_empty()) return *this;
  23579. const unsigned int siz = (unsigned int)size();
  23580. CImg<T> res(siz,siz,1,1,0);
  23581. cimg_foroff(*this,off) res((unsigned int)off,(unsigned int)off) = (*this)[off];
  23582. return res;
  23583. }
  23584. //! Replace the image by an identity matrix.
  23585. /**
  23586. \note If the instance image is not square, it is resized to a square matrix using its maximum
  23587. dimension as a reference.
  23588. **/
  23589. CImg<T>& identity_matrix() {
  23590. return identity_matrix(std::max(_width,_height)).move_to(*this);
  23591. }
  23592. //! Replace the image by an identity matrix \newinstance.
  23593. CImg<T> get_identity_matrix() const {
  23594. return identity_matrix(std::max(_width,_height));
  23595. }
  23596. //! Fill image with a linear sequence of values.
  23597. /**
  23598. \param a0 Starting value of the sequence.
  23599. \param a1 Ending value of the sequence.
  23600. **/
  23601. CImg<T>& sequence(const T& a0, const T& a1) {
  23602. if (is_empty()) return *this;
  23603. const ulongT siz = size() - 1;
  23604. T* ptr = _data;
  23605. if (siz) {
  23606. const double delta = (double)a1 - (double)a0;
  23607. cimg_foroff(*this,l) *(ptr++) = (T)(a0 + delta*l/siz);
  23608. } else *ptr = a0;
  23609. return *this;
  23610. }
  23611. //! Fill image with a linear sequence of values \newinstance.
  23612. CImg<T> get_sequence(const T& a0, const T& a1) const {
  23613. return (+*this).sequence(a0,a1);
  23614. }
  23615. //! Transpose the image, viewed as a matrix.
  23616. /**
  23617. \note Equivalent to \code permute_axes("yxzc"); \endcode
  23618. **/
  23619. CImg<T>& transpose() {
  23620. if (_width==1) { _width = _height; _height = 1; return *this; }
  23621. if (_height==1) { _height = _width; _width = 1; return *this; }
  23622. if (_width==_height) {
  23623. 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));
  23624. return *this;
  23625. }
  23626. return get_transpose().move_to(*this);
  23627. }
  23628. //! Transpose the image, viewed as a matrix \newinstance.
  23629. CImg<T> get_transpose() const {
  23630. return get_permute_axes("yxzc");
  23631. }
  23632. //! Compute the cross product between two \c 1x3 images, viewed as 3d vectors.
  23633. /**
  23634. \param img Image used as the second argument of the cross product.
  23635. \note The first argument of the cross product is \c *this.
  23636. **/
  23637. template<typename t>
  23638. CImg<T>& cross(const CImg<t>& img) {
  23639. if (_width!=1 || _height<3 || img._width!=1 || img._height<3)
  23640. throw CImgInstanceException(_cimg_instance
  23641. "cross(): Instance and/or specified image (%u,%u,%u,%u,%p) are not 3d vectors.",
  23642. cimg_instance,
  23643. img._width,img._height,img._depth,img._spectrum,img._data);
  23644. const T x = (*this)[0], y = (*this)[1], z = (*this)[2];
  23645. (*this)[0] = (T)(y*img[2] - z*img[1]);
  23646. (*this)[1] = (T)(z*img[0] - x*img[2]);
  23647. (*this)[2] = (T)(x*img[1] - y*img[0]);
  23648. return *this;
  23649. }
  23650. //! Compute the cross product between two \c 1x3 images, viewed as 3d vectors \newinstance.
  23651. template<typename t>
  23652. CImg<_cimg_Tt> get_cross(const CImg<t>& img) const {
  23653. return CImg<_cimg_Tt>(*this).cross(img);
  23654. }
  23655. //! Invert the instance image, viewed as a matrix.
  23656. /**
  23657. \param use_LU Choose the inverting algorithm. Can be:
  23658. - \c true: LU-based matrix inversion.
  23659. - \c false: SVD-based matrix inversion.
  23660. **/
  23661. CImg<T>& invert(const bool use_LU=true) {
  23662. if (_width!=_height || _depth!=1 || _spectrum!=1)
  23663. throw CImgInstanceException(_cimg_instance
  23664. "invert(): Instance is not a square matrix.",
  23665. cimg_instance);
  23666. #ifdef cimg_use_lapack
  23667. int INFO = (int)use_LU, N = _width, LWORK = 4*N, *const IPIV = new int[N];
  23668. Tfloat
  23669. *const lapA = new Tfloat[N*N],
  23670. *const WORK = new Tfloat[LWORK];
  23671. cimg_forXY(*this,k,l) lapA[k*N + l] = (Tfloat)((*this)(k,l));
  23672. cimg::getrf(N,lapA,IPIV,INFO);
  23673. if (INFO)
  23674. cimg::warn(_cimg_instance
  23675. "invert(): LAPACK function dgetrf_() returned error code %d.",
  23676. cimg_instance,
  23677. INFO);
  23678. else {
  23679. cimg::getri(N,lapA,IPIV,WORK,LWORK,INFO);
  23680. if (INFO)
  23681. cimg::warn(_cimg_instance
  23682. "invert(): LAPACK function dgetri_() returned error code %d.",
  23683. cimg_instance,
  23684. INFO);
  23685. }
  23686. if (!INFO) cimg_forXY(*this,k,l) (*this)(k,l) = (T)(lapA[k*N + l]); else fill(0);
  23687. delete[] IPIV; delete[] lapA; delete[] WORK;
  23688. #else
  23689. const double dete = _width>3?-1.0:det();
  23690. if (dete!=0.0 && _width==2) {
  23691. const double
  23692. a = _data[0], c = _data[1],
  23693. b = _data[2], d = _data[3];
  23694. _data[0] = (T)(d/dete); _data[1] = (T)(-c/dete);
  23695. _data[2] = (T)(-b/dete); _data[3] = (T)(a/dete);
  23696. } else if (dete!=0.0 && _width==3) {
  23697. const double
  23698. a = _data[0], d = _data[1], g = _data[2],
  23699. b = _data[3], e = _data[4], h = _data[5],
  23700. c = _data[6], f = _data[7], i = _data[8];
  23701. _data[0] = (T)((i*e - f*h)/dete), _data[1] = (T)((g*f - i*d)/dete), _data[2] = (T)((d*h - g*e)/dete);
  23702. _data[3] = (T)((h*c - i*b)/dete), _data[4] = (T)((i*a - c*g)/dete), _data[5] = (T)((g*b - a*h)/dete);
  23703. _data[6] = (T)((b*f - e*c)/dete), _data[7] = (T)((d*c - a*f)/dete), _data[8] = (T)((a*e - d*b)/dete);
  23704. } else {
  23705. if (use_LU) { // LU-based inverse computation
  23706. CImg<Tfloat> A(*this,false), indx, col(1,_width);
  23707. bool d;
  23708. A._LU(indx,d);
  23709. cimg_forX(*this,j) {
  23710. col.fill(0);
  23711. col(j) = 1;
  23712. col._solve(A,indx);
  23713. cimg_forX(*this,i) (*this)(j,i) = (T)col(i);
  23714. }
  23715. } else { // SVD-based inverse computation
  23716. CImg<Tfloat> U(_width,_width), S(1,_width), V(_width,_width);
  23717. SVD(U,S,V,false);
  23718. U.transpose();
  23719. cimg_forY(S,k) if (S[k]!=0) S[k]=1/S[k];
  23720. S.diagonal();
  23721. *this = V*S*U;
  23722. }
  23723. }
  23724. #endif
  23725. return *this;
  23726. }
  23727. //! Invert the instance image, viewed as a matrix \newinstance.
  23728. CImg<Tfloat> get_invert(const bool use_LU=true) const {
  23729. return CImg<Tfloat>(*this,false).invert(use_LU);
  23730. }
  23731. //! Compute the Moore-Penrose pseudo-inverse of the instance image, viewed as a matrix.
  23732. /**
  23733. **/
  23734. CImg<T>& pseudoinvert() {
  23735. return get_pseudoinvert().move_to(*this);
  23736. }
  23737. //! Compute the Moore-Penrose pseudo-inverse of the instance image, viewed as a matrix \newinstance.
  23738. CImg<Tfloat> get_pseudoinvert() const {
  23739. CImg<Tfloat> U, S, V;
  23740. SVD(U,S,V);
  23741. const Tfloat tolerance = (sizeof(Tfloat)<=4?5.96e-8f:1.11e-16f)*std::max(_width,_height)*S.max();
  23742. cimg_forX(V,x) {
  23743. const Tfloat s = S(x), invs = s>tolerance?1/s:0;
  23744. cimg_forY(V,y) V(x,y)*=invs;
  23745. }
  23746. return V*U.transpose();
  23747. }
  23748. //! Solve a system of linear equations.
  23749. /**
  23750. \param A Matrix of the linear system.
  23751. \note Solve \c AX=B where \c B=*this.
  23752. **/
  23753. template<typename t>
  23754. CImg<T>& solve(const CImg<t>& A) {
  23755. if (_depth!=1 || _spectrum!=1 || _height!=A._height || A._depth!=1 || A._spectrum!=1)
  23756. throw CImgArgumentException(_cimg_instance
  23757. "solve(): Instance and specified matrix (%u,%u,%u,%u,%p) have "
  23758. "incompatible dimensions.",
  23759. cimg_instance,
  23760. A._width,A._height,A._depth,A._spectrum,A._data);
  23761. typedef _cimg_Ttfloat Ttfloat;
  23762. if (A._width==A._height) { // Classical linear system
  23763. if (_width!=1) {
  23764. CImg<T> res(_width,A._width);
  23765. cimg_forX(*this,i) res.draw_image(i,get_column(i).solve(A));
  23766. return res.move_to(*this);
  23767. }
  23768. #ifdef cimg_use_lapack
  23769. char TRANS = 'N';
  23770. int INFO, N = _height, LWORK = 4*N, *const IPIV = new int[N];
  23771. Ttfloat
  23772. *const lapA = new Ttfloat[N*N],
  23773. *const lapB = new Ttfloat[N],
  23774. *const WORK = new Ttfloat[LWORK];
  23775. cimg_forXY(A,k,l) lapA[k*N + l] = (Ttfloat)(A(k,l));
  23776. cimg_forY(*this,i) lapB[i] = (Ttfloat)((*this)(i));
  23777. cimg::getrf(N,lapA,IPIV,INFO);
  23778. if (INFO)
  23779. cimg::warn(_cimg_instance
  23780. "solve(): LAPACK library function dgetrf_() returned error code %d.",
  23781. cimg_instance,
  23782. INFO);
  23783. if (!INFO) {
  23784. cimg::getrs(TRANS,N,lapA,IPIV,lapB,INFO);
  23785. if (INFO)
  23786. cimg::warn(_cimg_instance
  23787. "solve(): LAPACK library function dgetrs_() returned error code %d.",
  23788. cimg_instance,
  23789. INFO);
  23790. }
  23791. if (!INFO) cimg_forY(*this,i) (*this)(i) = (T)(lapB[i]); else fill(0);
  23792. delete[] IPIV; delete[] lapA; delete[] lapB; delete[] WORK;
  23793. #else
  23794. CImg<Ttfloat> lu(A,false);
  23795. CImg<Ttfloat> indx;
  23796. bool d;
  23797. lu._LU(indx,d);
  23798. _solve(lu,indx);
  23799. #endif
  23800. } else { // Least-square solution for non-square systems.
  23801. #ifdef cimg_use_lapack
  23802. if (_width!=1) {
  23803. CImg<T> res(_width,A._width);
  23804. cimg_forX(*this,i) res.draw_image(i,get_column(i).solve(A));
  23805. return res.move_to(*this);
  23806. }
  23807. char TRANS = 'N';
  23808. int INFO, N = A._width, M = A._height, LWORK = -1, LDA = M, LDB = M, NRHS = _width;
  23809. Ttfloat WORK_QUERY;
  23810. Ttfloat
  23811. * const lapA = new Ttfloat[M*N],
  23812. * const lapB = new Ttfloat[M*NRHS];
  23813. cimg::sgels(TRANS, M, N, NRHS, lapA, LDA, lapB, LDB, &WORK_QUERY, LWORK, INFO);
  23814. LWORK = (int) WORK_QUERY;
  23815. Ttfloat *const WORK = new Ttfloat[LWORK];
  23816. cimg_forXY(A,k,l) lapA[k*M + l] = (Ttfloat)(A(k,l));
  23817. cimg_forXY(*this,k,l) lapB[k*M + l] = (Ttfloat)((*this)(k,l));
  23818. cimg::sgels(TRANS, M, N, NRHS, lapA, LDA, lapB, LDB, WORK, LWORK, INFO);
  23819. if (INFO != 0)
  23820. cimg::warn(_cimg_instance
  23821. "solve(): LAPACK library function sgels() returned error code %d.",
  23822. cimg_instance,
  23823. INFO);
  23824. assign(NRHS, N);
  23825. if (!INFO)
  23826. cimg_forXY(*this,k,l) (*this)(k,l) = (T)lapB[k*M + l];
  23827. else
  23828. assign(A.get_pseudoinvert()*(*this));
  23829. delete[] lapA; delete[] lapB; delete[] WORK;
  23830. #else
  23831. assign(A.get_pseudoinvert()*(*this));
  23832. #endif
  23833. }
  23834. return *this;
  23835. }
  23836. //! Solve a system of linear equations \newinstance.
  23837. template<typename t>
  23838. CImg<_cimg_Ttfloat> get_solve(const CImg<t>& A) const {
  23839. return CImg<_cimg_Ttfloat>(*this,false).solve(A);
  23840. }
  23841. template<typename t, typename ti>
  23842. CImg<T>& _solve(const CImg<t>& A, const CImg<ti>& indx) {
  23843. typedef _cimg_Ttfloat Ttfloat;
  23844. const int N = (int)size();
  23845. int ii = -1;
  23846. Ttfloat sum;
  23847. for (int i = 0; i<N; ++i) {
  23848. const int ip = (int)indx[i];
  23849. Ttfloat sum = (*this)(ip);
  23850. (*this)(ip) = (*this)(i);
  23851. if (ii>=0) for (int j = ii; j<=i - 1; ++j) sum-=A(j,i)*(*this)(j);
  23852. else if (sum!=0) ii = i;
  23853. (*this)(i) = (T)sum;
  23854. }
  23855. for (int i = N - 1; i>=0; --i) {
  23856. sum = (*this)(i);
  23857. for (int j = i + 1; j<N; ++j) sum-=A(j,i)*(*this)(j);
  23858. (*this)(i) = (T)(sum/A(i,i));
  23859. }
  23860. return *this;
  23861. }
  23862. //! Solve a tridiagonal system of linear equations.
  23863. /**
  23864. \param A Coefficients of the tridiagonal system.
  23865. A is a tridiagonal matrix A = [ b0,c0,0,...; a1,b1,c1,0,... ; ... ; ...,0,aN,bN ],
  23866. stored as a 3 columns matrix
  23867. \note Solve AX=B where \c B=*this, using the Thomas algorithm.
  23868. **/
  23869. template<typename t>
  23870. CImg<T>& solve_tridiagonal(const CImg<t>& A) {
  23871. const unsigned int siz = (unsigned int)size();
  23872. if (A._width!=3 || A._height!=siz)
  23873. throw CImgArgumentException(_cimg_instance
  23874. "solve_tridiagonal(): Instance and tridiagonal matrix "
  23875. "(%u,%u,%u,%u,%p) have incompatible dimensions.",
  23876. cimg_instance,
  23877. A._width,A._height,A._depth,A._spectrum,A._data);
  23878. typedef _cimg_Ttfloat Ttfloat;
  23879. const Ttfloat epsilon = 1e-4f;
  23880. CImg<Ttfloat> B = A.get_column(1), V(*this,false);
  23881. for (int i = 1; i<(int)siz; ++i) {
  23882. const Ttfloat m = A(0,i)/(B[i - 1]?B[i - 1]:epsilon);
  23883. B[i] -= m*A(2,i - 1);
  23884. V[i] -= m*V[i - 1];
  23885. }
  23886. (*this)[siz - 1] = (T)(V[siz - 1]/(B[siz - 1]?B[siz - 1]:epsilon));
  23887. 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));
  23888. return *this;
  23889. }
  23890. //! Solve a tridiagonal system of linear equations \newinstance.
  23891. template<typename t>
  23892. CImg<_cimg_Ttfloat> get_solve_tridiagonal(const CImg<t>& A) const {
  23893. return CImg<_cimg_Ttfloat>(*this,false).solve_tridiagonal(A);
  23894. }
  23895. //! Compute eigenvalues and eigenvectors of the instance image, viewed as a matrix.
  23896. /**
  23897. \param[out] val Vector of the estimated eigenvalues, in decreasing order.
  23898. \param[out] vec Matrix of the estimated eigenvectors, sorted by columns.
  23899. **/
  23900. template<typename t>
  23901. const CImg<T>& eigen(CImg<t>& val, CImg<t> &vec) const {
  23902. if (is_empty()) { val.assign(); vec.assign(); }
  23903. else {
  23904. if (_width!=_height || _depth>1 || _spectrum>1)
  23905. throw CImgInstanceException(_cimg_instance
  23906. "eigen(): Instance is not a square matrix.",
  23907. cimg_instance);
  23908. if (val.size()<(ulongT)_width) val.assign(1,_width);
  23909. if (vec.size()<(ulongT)_width*_width) vec.assign(_width,_width);
  23910. switch (_width) {
  23911. case 1 : { val[0] = (t)(*this)[0]; vec[0] = (t)1; } break;
  23912. case 2 : {
  23913. const double a = (*this)[0], b = (*this)[1], c = (*this)[2], d = (*this)[3], e = a + d;
  23914. double f = e*e - 4*(a*d - b*c);
  23915. if (f<0)
  23916. cimg::warn(_cimg_instance
  23917. "eigen(): Complex eigenvalues found.",
  23918. cimg_instance);
  23919. f = std::sqrt(f);
  23920. const double
  23921. l1 = 0.5*(e - f),
  23922. l2 = 0.5*(e + f),
  23923. b2 = b*b,
  23924. norm1 = std::sqrt(cimg::sqr(l2 - a) + b2),
  23925. norm2 = std::sqrt(cimg::sqr(l1 - a) + b2);
  23926. val[0] = (t)l2;
  23927. val[1] = (t)l1;
  23928. 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; }
  23929. 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; }
  23930. } break;
  23931. default :
  23932. throw CImgInstanceException(_cimg_instance
  23933. "eigen(): Eigenvalues computation of general matrices is limited "
  23934. "to 2x2 matrices.",
  23935. cimg_instance);
  23936. }
  23937. }
  23938. return *this;
  23939. }
  23940. //! Compute eigenvalues and eigenvectors of the instance image, viewed as a matrix.
  23941. /**
  23942. \return A list of two images <tt>[val; vec]</tt>, whose meaning is similar as in eigen(CImg<t>&,CImg<t>&) const.
  23943. **/
  23944. CImgList<Tfloat> get_eigen() const {
  23945. CImgList<Tfloat> res(2);
  23946. eigen(res[0],res[1]);
  23947. return res;
  23948. }
  23949. //! Compute eigenvalues and eigenvectors of the instance image, viewed as a symmetric matrix.
  23950. /**
  23951. \param[out] val Vector of the estimated eigenvalues, in decreasing order.
  23952. \param[out] vec Matrix of the estimated eigenvectors, sorted by columns.
  23953. **/
  23954. template<typename t>
  23955. const CImg<T>& symmetric_eigen(CImg<t>& val, CImg<t>& vec) const {
  23956. if (is_empty()) { val.assign(); vec.assign(); }
  23957. else {
  23958. #ifdef cimg_use_lapack
  23959. char JOB = 'V', UPLO = 'U';
  23960. int N = _width, LWORK = 4*N, INFO;
  23961. Tfloat
  23962. *const lapA = new Tfloat[N*N],
  23963. *const lapW = new Tfloat[N],
  23964. *const WORK = new Tfloat[LWORK];
  23965. cimg_forXY(*this,k,l) lapA[k*N + l] = (Tfloat)((*this)(k,l));
  23966. cimg::syev(JOB,UPLO,N,lapA,lapW,WORK,LWORK,INFO);
  23967. if (INFO)
  23968. cimg::warn(_cimg_instance
  23969. "symmetric_eigen(): LAPACK library function dsyev_() returned error code %d.",
  23970. cimg_instance,
  23971. INFO);
  23972. val.assign(1,N);
  23973. vec.assign(N,N);
  23974. if (!INFO) {
  23975. cimg_forY(val,i) val(i) = (T)lapW[N - 1 -i];
  23976. cimg_forXY(vec,k,l) vec(k,l) = (T)(lapA[(N - 1 - k)*N + l]);
  23977. } else { val.fill(0); vec.fill(0); }
  23978. delete[] lapA; delete[] lapW; delete[] WORK;
  23979. #else
  23980. if (_width!=_height || _depth>1 || _spectrum>1)
  23981. throw CImgInstanceException(_cimg_instance
  23982. "eigen(): Instance is not a square matrix.",
  23983. cimg_instance);
  23984. val.assign(1,_width);
  23985. if (vec._data) vec.assign(_width,_width);
  23986. if (_width<3) {
  23987. eigen(val,vec);
  23988. if (_width==2) { vec[1] = -vec[2]; vec[3] = vec[0]; } // Force orthogonality for 2x2 matrices.
  23989. return *this;
  23990. }
  23991. CImg<t> V(_width,_width);
  23992. Tfloat M = 0, m = (Tfloat)min_max(M), maxabs = cimg::max((Tfloat)1,cimg::abs(m),cimg::abs(M));
  23993. (CImg<Tfloat>(*this,false)/=maxabs).SVD(vec,val,V,false);
  23994. if (maxabs!=1) val*=maxabs;
  23995. bool is_ambiguous = false;
  23996. float eig = 0;
  23997. cimg_forY(val,p) { // check for ambiguous cases.
  23998. if (val[p]>eig) eig = (float)val[p];
  23999. t scal = 0;
  24000. cimg_forY(vec,y) scal+=vec(p,y)*V(p,y);
  24001. if (cimg::abs(scal)<0.9f) is_ambiguous = true;
  24002. if (scal<0) val[p] = -val[p];
  24003. }
  24004. if (is_ambiguous) {
  24005. ++(eig*=2);
  24006. SVD(vec,val,V,false,40,eig);
  24007. val-=eig;
  24008. }
  24009. CImg<intT> permutations; // sort eigenvalues in decreasing order
  24010. CImg<t> tmp(_width);
  24011. val.sort(permutations,false);
  24012. cimg_forY(vec,k) {
  24013. cimg_forY(permutations,y) tmp(y) = vec(permutations(y),k);
  24014. std::memcpy(vec.data(0,k),tmp._data,sizeof(t)*_width);
  24015. }
  24016. #endif
  24017. }
  24018. return *this;
  24019. }
  24020. //! Compute eigenvalues and eigenvectors of the instance image, viewed as a symmetric matrix.
  24021. /**
  24022. \return A list of two images <tt>[val; vec]</tt>, whose meaning are similar as in
  24023. symmetric_eigen(CImg<t>&,CImg<t>&) const.
  24024. **/
  24025. CImgList<Tfloat> get_symmetric_eigen() const {
  24026. CImgList<Tfloat> res(2);
  24027. symmetric_eigen(res[0],res[1]);
  24028. return res;
  24029. }
  24030. //! Sort pixel values and get sorting permutations.
  24031. /**
  24032. \param[out] permutations Permutation map used for the sorting.
  24033. \param is_increasing Tells if pixel values are sorted in an increasing (\c true) or decreasing (\c false) way.
  24034. **/
  24035. template<typename t>
  24036. CImg<T>& sort(CImg<t>& permutations, const bool is_increasing=true) {
  24037. permutations.assign(_width,_height,_depth,_spectrum);
  24038. if (is_empty()) return *this;
  24039. cimg_foroff(permutations,off) permutations[off] = (t)off;
  24040. return _quicksort(0,size() - 1,permutations,is_increasing,true);
  24041. }
  24042. //! Sort pixel values and get sorting permutations \newinstance.
  24043. template<typename t>
  24044. CImg<T> get_sort(CImg<t>& permutations, const bool is_increasing=true) const {
  24045. return (+*this).sort(permutations,is_increasing);
  24046. }
  24047. //! Sort pixel values.
  24048. /**
  24049. \param is_increasing Tells if pixel values are sorted in an increasing (\c true) or decreasing (\c false) way.
  24050. \param axis Tells if the value sorting must be done along a specific axis. Can be:
  24051. - \c 0: All pixel values are sorted, independently on their initial position.
  24052. - \c 'x': Image columns are sorted, according to the first value in each column.
  24053. - \c 'y': Image rows are sorted, according to the first value in each row.
  24054. - \c 'z': Image slices are sorted, according to the first value in each slice.
  24055. - \c 'c': Image channels are sorted, according to the first value in each channel.
  24056. **/
  24057. CImg<T>& sort(const bool is_increasing=true, const char axis=0) {
  24058. if (is_empty()) return *this;
  24059. CImg<uintT> perm;
  24060. switch (cimg::lowercase(axis)) {
  24061. case 0 :
  24062. _quicksort(0,size() - 1,perm,is_increasing,false);
  24063. break;
  24064. case 'x' : {
  24065. perm.assign(_width);
  24066. get_crop(0,0,0,0,_width - 1,0,0,0).sort(perm,is_increasing);
  24067. CImg<T> img(*this,false);
  24068. cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(perm[x],y,z,c);
  24069. } break;
  24070. case 'y' : {
  24071. perm.assign(_height);
  24072. get_crop(0,0,0,0,0,_height - 1,0,0).sort(perm,is_increasing);
  24073. CImg<T> img(*this,false);
  24074. cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,perm[y],z,c);
  24075. } break;
  24076. case 'z' : {
  24077. perm.assign(_depth);
  24078. get_crop(0,0,0,0,0,0,_depth - 1,0).sort(perm,is_increasing);
  24079. CImg<T> img(*this,false);
  24080. cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,perm[z],c);
  24081. } break;
  24082. case 'c' : {
  24083. perm.assign(_spectrum);
  24084. get_crop(0,0,0,0,0,0,0,_spectrum - 1).sort(perm,is_increasing);
  24085. CImg<T> img(*this,false);
  24086. cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,z,perm[c]);
  24087. } break;
  24088. default :
  24089. throw CImgArgumentException(_cimg_instance
  24090. "sort(): Invalid specified axis '%c' "
  24091. "(should be { x | y | z | c }).",
  24092. cimg_instance,axis);
  24093. }
  24094. return *this;
  24095. }
  24096. //! Sort pixel values \newinstance.
  24097. CImg<T> get_sort(const bool is_increasing=true, const char axis=0) const {
  24098. return (+*this).sort(is_increasing,axis);
  24099. }
  24100. template<typename t>
  24101. CImg<T>& _quicksort(const long indm, const long indM, CImg<t>& permutations,
  24102. const bool is_increasing, const bool is_permutations) {
  24103. if (indm<indM) {
  24104. const long mid = (indm + indM)/2;
  24105. if (is_increasing) {
  24106. if ((*this)[indm]>(*this)[mid]) {
  24107. cimg::swap((*this)[indm],(*this)[mid]);
  24108. if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
  24109. }
  24110. if ((*this)[mid]>(*this)[indM]) {
  24111. cimg::swap((*this)[indM],(*this)[mid]);
  24112. if (is_permutations) cimg::swap(permutations[indM],permutations[mid]);
  24113. }
  24114. if ((*this)[indm]>(*this)[mid]) {
  24115. cimg::swap((*this)[indm],(*this)[mid]);
  24116. if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
  24117. }
  24118. } else {
  24119. if ((*this)[indm]<(*this)[mid]) {
  24120. cimg::swap((*this)[indm],(*this)[mid]);
  24121. if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
  24122. }
  24123. if ((*this)[mid]<(*this)[indM]) {
  24124. cimg::swap((*this)[indM],(*this)[mid]);
  24125. if (is_permutations) cimg::swap(permutations[indM],permutations[mid]);
  24126. }
  24127. if ((*this)[indm]<(*this)[mid]) {
  24128. cimg::swap((*this)[indm],(*this)[mid]);
  24129. if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
  24130. }
  24131. }
  24132. if (indM - indm>=3) {
  24133. const T pivot = (*this)[mid];
  24134. long i = indm, j = indM;
  24135. if (is_increasing) {
  24136. do {
  24137. while ((*this)[i]<pivot) ++i;
  24138. while ((*this)[j]>pivot) --j;
  24139. if (i<=j) {
  24140. if (is_permutations) cimg::swap(permutations[i],permutations[j]);
  24141. cimg::swap((*this)[i++],(*this)[j--]);
  24142. }
  24143. } while (i<=j);
  24144. } else {
  24145. do {
  24146. while ((*this)[i]>pivot) ++i;
  24147. while ((*this)[j]<pivot) --j;
  24148. if (i<=j) {
  24149. if (is_permutations) cimg::swap(permutations[i],permutations[j]);
  24150. cimg::swap((*this)[i++],(*this)[j--]);
  24151. }
  24152. } while (i<=j);
  24153. }
  24154. if (indm<j) _quicksort(indm,j,permutations,is_increasing,is_permutations);
  24155. if (i<indM) _quicksort(i,indM,permutations,is_increasing,is_permutations);
  24156. }
  24157. }
  24158. return *this;
  24159. }
  24160. //! Compute the SVD of the instance image, viewed as a general matrix.
  24161. /**
  24162. Compute the SVD decomposition \c *this=U*S*V' where \c U and \c V are orthogonal matrices
  24163. and \c S is a diagonal matrix. \c V' denotes the matrix transpose of \c V.
  24164. \param[out] U First matrix of the SVD product.
  24165. \param[out] S Coefficients of the second (diagonal) matrix of the SVD product.
  24166. These coefficients are stored as a vector.
  24167. \param[out] V Third matrix of the SVD product.
  24168. \param sorting Tells if the diagonal coefficients are sorted (in decreasing order).
  24169. \param max_iteration Maximum number of iterations considered for the algorithm convergence.
  24170. \param lambda Epsilon used for the algorithm convergence.
  24171. \note The instance matrix can be computed from \c U,\c S and \c V by
  24172. \code
  24173. const CImg<> A; // Input matrix (assumed to contain some values).
  24174. CImg<> U,S,V;
  24175. A.SVD(U,S,V)
  24176. \endcode
  24177. **/
  24178. template<typename t>
  24179. const CImg<T>& SVD(CImg<t>& U, CImg<t>& S, CImg<t>& V, const bool sorting=true,
  24180. const unsigned int max_iteration=40, const float lambda=0) const {
  24181. if (is_empty()) { U.assign(); S.assign(); V.assign(); }
  24182. else {
  24183. U = *this;
  24184. if (lambda!=0) {
  24185. const unsigned int delta = std::min(U._width,U._height);
  24186. for (unsigned int i = 0; i<delta; ++i) U(i,i) = (t)(U(i,i) + lambda);
  24187. }
  24188. if (S.size()<_width) S.assign(1,_width);
  24189. if (V._width<_width || V._height<_height) V.assign(_width,_width);
  24190. CImg<t> rv1(_width);
  24191. t anorm = 0, c, f, g = 0, h, s, scale = 0;
  24192. int l = 0, nm = 0;
  24193. cimg_forX(U,i) {
  24194. l = i + 1; rv1[i] = scale*g; g = s = scale = 0;
  24195. if (i<height()) {
  24196. for (int k = i; k<height(); ++k) scale+=cimg::abs(U(i,k));
  24197. if (scale) {
  24198. for (int k = i; k<height(); ++k) { U(i,k)/=scale; s+=U(i,k)*U(i,k); }
  24199. f = U(i,i); g = (t)((f>=0?-1:1)*std::sqrt(s)); h=f*g-s; U(i,i) = f-g;
  24200. for (int j = l; j<width(); ++j) {
  24201. s = 0;
  24202. for (int k=i; k<height(); ++k) s+=U(i,k)*U(j,k);
  24203. f = s/h;
  24204. for (int k = i; k<height(); ++k) U(j,k)+=f*U(i,k);
  24205. }
  24206. for (int k = i; k<height(); ++k) U(i,k)*=scale;
  24207. }
  24208. }
  24209. S[i]=scale*g;
  24210. g = s = scale = 0;
  24211. if (i<height() && i!=width() - 1) {
  24212. for (int k = l; k<width(); ++k) scale+=cimg::abs(U(k,i));
  24213. if (scale) {
  24214. for (int k = l; k<width(); ++k) { U(k,i)/= scale; s+=U(k,i)*U(k,i); }
  24215. f = U(l,i); g = (t)((f>=0?-1:1)*std::sqrt(s)); h = f*g-s; U(l,i) = f-g;
  24216. for (int k = l; k<width(); ++k) rv1[k]=U(k,i)/h;
  24217. for (int j = l; j<height(); ++j) {
  24218. s = 0;
  24219. for (int k = l; k<width(); ++k) s+=U(k,j)*U(k,i);
  24220. for (int k = l; k<width(); ++k) U(k,j)+=s*rv1[k];
  24221. }
  24222. for (int k = l; k<width(); ++k) U(k,i)*=scale;
  24223. }
  24224. }
  24225. anorm = (t)std::max((float)anorm,(float)(cimg::abs(S[i]) + cimg::abs(rv1[i])));
  24226. }
  24227. for (int i = width() - 1; i>=0; --i) {
  24228. if (i<width()-1) {
  24229. if (g) {
  24230. for (int j = l; j<width(); ++j) V(i,j) =(U(j,i)/U(l,i))/g;
  24231. for (int j = l; j<width(); ++j) {
  24232. s = 0;
  24233. for (int k = l; k<width(); ++k) s+=U(k,i)*V(j,k);
  24234. for (int k = l; k<width(); ++k) V(j,k)+=s*V(i,k);
  24235. }
  24236. }
  24237. for (int j = l; j<width(); ++j) V(j,i) = V(i,j) = (t)0.0;
  24238. }
  24239. V(i,i) = (t)1.0; g = rv1[i]; l = i;
  24240. }
  24241. for (int i = std::min(width(),height()) - 1; i>=0; --i) {
  24242. l = i + 1; g = S[i];
  24243. for (int j = l; j<width(); ++j) U(j,i) = 0;
  24244. if (g) {
  24245. g = 1/g;
  24246. for (int j = l; j<width(); ++j) {
  24247. s = 0; for (int k = l; k<height(); ++k) s+=U(i,k)*U(j,k);
  24248. f = (s/U(i,i))*g;
  24249. for (int k = i; k<height(); ++k) U(j,k)+=f*U(i,k);
  24250. }
  24251. for (int j = i; j<height(); ++j) U(i,j)*= g;
  24252. } else for (int j = i; j<height(); ++j) U(i,j) = 0;
  24253. ++U(i,i);
  24254. }
  24255. for (int k = width() - 1; k>=0; --k) {
  24256. for (unsigned int its = 0; its<max_iteration; ++its) {
  24257. bool flag = true;
  24258. for (l = k; l>=1; --l) {
  24259. nm = l - 1;
  24260. if ((cimg::abs(rv1[l]) + anorm)==anorm) { flag = false; break; }
  24261. if ((cimg::abs(S[nm]) + anorm)==anorm) break;
  24262. }
  24263. if (flag) {
  24264. c = 0; s = 1;
  24265. for (int i = l; i<=k; ++i) {
  24266. f = s*rv1[i]; rv1[i] = c*rv1[i];
  24267. if ((cimg::abs(f) + anorm)==anorm) break;
  24268. g = S[i]; h = cimg::_hypot(f,g); S[i] = h; h = 1/h; c = g*h; s = -f*h;
  24269. cimg_forY(U,j) { const t y = U(nm,j), z = U(i,j); U(nm,j) = y*c + z*s; U(i,j) = z*c - y*s; }
  24270. }
  24271. }
  24272. const t z = S[k];
  24273. if (l==k) { if (z<0) { S[k] = -z; cimg_forX(U,j) V(k,j) = -V(k,j); } break; }
  24274. nm = k - 1;
  24275. t x = S[l], y = S[nm];
  24276. g = rv1[nm]; h = rv1[k];
  24277. f = ((y - z)*(y + z)+(g - h)*(g + h))/std::max((t)1e-25,2*h*y);
  24278. g = cimg::_hypot(f,(t)1);
  24279. f = ((x - z)*(x + z)+h*((y/(f + (f>=0?g:-g))) - h))/std::max((t)1e-25,x);
  24280. c = s = 1;
  24281. for (int j = l; j<=nm; ++j) {
  24282. const int i = j + 1;
  24283. g = rv1[i]; h = s*g; g = c*g;
  24284. t y = S[i];
  24285. t z = cimg::_hypot(f,h);
  24286. rv1[j] = z; c = f/std::max((t)1e-25,z); s = h/std::max((t)1e-25,z);
  24287. f = x*c + g*s; g = g*c - x*s; h = y*s; y*=c;
  24288. cimg_forX(U,jj) { const t x = V(j,jj), z = V(i,jj); V(j,jj) = x*c + z*s; V(i,jj) = z*c - x*s; }
  24289. z = cimg::_hypot(f,h); S[j] = z;
  24290. if (z) { z = 1/std::max((t)1e-25,z); c = f*z; s = h*z; }
  24291. f = c*g + s*y; x = c*y - s*g;
  24292. cimg_forY(U,jj) { const t y = U(j,jj); z = U(i,jj); U(j,jj) = y*c + z*s; U(i,jj) = z*c - y*s; }
  24293. }
  24294. rv1[l] = 0; rv1[k]=f; S[k]=x;
  24295. }
  24296. }
  24297. if (sorting) {
  24298. CImg<intT> permutations;
  24299. CImg<t> tmp(_width);
  24300. S.sort(permutations,false);
  24301. cimg_forY(U,k) {
  24302. cimg_forY(permutations,y) tmp(y) = U(permutations(y),k);
  24303. std::memcpy(U.data(0,k),tmp._data,sizeof(t)*_width);
  24304. }
  24305. cimg_forY(V,k) {
  24306. cimg_forY(permutations,y) tmp(y) = V(permutations(y),k);
  24307. std::memcpy(V.data(0,k),tmp._data,sizeof(t)*_width);
  24308. }
  24309. }
  24310. }
  24311. return *this;
  24312. }
  24313. //! Compute the SVD of the instance image, viewed as a general matrix.
  24314. /**
  24315. \return A list of three images <tt>[U; S; V]</tt>, whose meaning is similar as in
  24316. SVD(CImg<t>&,CImg<t>&,CImg<t>&,bool,unsigned int,float) const.
  24317. **/
  24318. CImgList<Tfloat> get_SVD(const bool sorting=true,
  24319. const unsigned int max_iteration=40, const float lambda=0) const {
  24320. CImgList<Tfloat> res(3);
  24321. SVD(res[0],res[1],res[2],sorting,max_iteration,lambda);
  24322. return res;
  24323. }
  24324. // [internal] Compute the LU decomposition of a permuted matrix.
  24325. template<typename t>
  24326. CImg<T>& _LU(CImg<t>& indx, bool& d) {
  24327. const int N = width();
  24328. int imax = 0;
  24329. CImg<Tfloat> vv(N);
  24330. indx.assign(N);
  24331. d = true;
  24332. cimg_forX(*this,i) {
  24333. Tfloat vmax = 0;
  24334. cimg_forX(*this,j) {
  24335. const Tfloat tmp = cimg::abs((*this)(j,i));
  24336. if (tmp>vmax) vmax = tmp;
  24337. }
  24338. if (vmax==0) { indx.fill(0); return fill(0); }
  24339. vv[i] = 1/vmax;
  24340. }
  24341. cimg_forX(*this,j) {
  24342. for (int i = 0; i<j; ++i) {
  24343. Tfloat sum=(*this)(j,i);
  24344. for (int k = 0; k<i; ++k) sum-=(*this)(k,i)*(*this)(j,k);
  24345. (*this)(j,i) = (T)sum;
  24346. }
  24347. Tfloat vmax = 0;
  24348. for (int i = j; i<width(); ++i) {
  24349. Tfloat sum=(*this)(j,i);
  24350. for (int k = 0; k<j; ++k) sum-=(*this)(k,i)*(*this)(j,k);
  24351. (*this)(j,i) = (T)sum;
  24352. const Tfloat tmp = vv[i]*cimg::abs(sum);
  24353. if (tmp>=vmax) { vmax=tmp; imax=i; }
  24354. }
  24355. if (j!=imax) {
  24356. cimg_forX(*this,k) cimg::swap((*this)(k,imax),(*this)(k,j));
  24357. d =!d;
  24358. vv[imax] = vv[j];
  24359. }
  24360. indx[j] = (t)imax;
  24361. if ((*this)(j,j)==0) (*this)(j,j) = (T)1e-20;
  24362. if (j<N) {
  24363. const Tfloat tmp = 1/(Tfloat)(*this)(j,j);
  24364. for (int i = j + 1; i<N; ++i) (*this)(j,i) = (T)((*this)(j,i)*tmp);
  24365. }
  24366. }
  24367. return *this;
  24368. }
  24369. //! Compute minimal path in a graph, using the Dijkstra algorithm.
  24370. /**
  24371. \param distance An object having operator()(unsigned int i, unsigned int j) which returns distance
  24372. between two nodes (i,j).
  24373. \param nb_nodes Number of graph nodes.
  24374. \param starting_node Indice of the starting node.
  24375. \param ending_node Indice of the ending node (set to ~0U to ignore ending node).
  24376. \param previous_node Array that gives the previous node indice in the path to the starting node
  24377. (optional parameter).
  24378. \return Array of distances of each node to the starting node.
  24379. **/
  24380. template<typename tf, typename t>
  24381. static CImg<T> dijkstra(const tf& distance, const unsigned int nb_nodes,
  24382. const unsigned int starting_node, const unsigned int ending_node,
  24383. CImg<t>& previous_node) {
  24384. if (starting_node>=nb_nodes)
  24385. throw CImgArgumentException("CImg<%s>::dijkstra(): Specified indice of starting node %u is higher "
  24386. "than number of nodes %u.",
  24387. pixel_type(),starting_node,nb_nodes);
  24388. CImg<T> dist(1,nb_nodes,1,1,cimg::type<T>::max());
  24389. dist(starting_node) = 0;
  24390. previous_node.assign(1,nb_nodes,1,1,(t)-1);
  24391. previous_node(starting_node) = (t)starting_node;
  24392. CImg<uintT> Q(nb_nodes);
  24393. cimg_forX(Q,u) Q(u) = (unsigned int)u;
  24394. cimg::swap(Q(starting_node),Q(0));
  24395. unsigned int sizeQ = nb_nodes;
  24396. while (sizeQ) {
  24397. // Update neighbors from minimal vertex
  24398. const unsigned int umin = Q(0);
  24399. if (umin==ending_node) sizeQ = 0;
  24400. else {
  24401. const T dmin = dist(umin);
  24402. const T infty = cimg::type<T>::max();
  24403. for (unsigned int q = 1; q<sizeQ; ++q) {
  24404. const unsigned int v = Q(q);
  24405. const T d = (T)distance(v,umin);
  24406. if (d<infty) {
  24407. const T alt = dmin + d;
  24408. if (alt<dist(v)) {
  24409. dist(v) = alt;
  24410. previous_node(v) = (t)umin;
  24411. const T distpos = dist(Q(q));
  24412. for (unsigned int pos = q, par = 0; pos && distpos<dist(Q(par=(pos + 1)/2 - 1)); pos=par)
  24413. cimg::swap(Q(pos),Q(par));
  24414. }
  24415. }
  24416. }
  24417. // Remove minimal vertex from queue
  24418. Q(0) = Q(--sizeQ);
  24419. const T distpos = dist(Q(0));
  24420. for (unsigned int pos = 0, left = 0, right = 0;
  24421. ((right=2*(pos + 1),(left=right - 1))<sizeQ && distpos>dist(Q(left))) ||
  24422. (right<sizeQ && distpos>dist(Q(right)));) {
  24423. if (right<sizeQ) {
  24424. if (dist(Q(left))<dist(Q(right))) { cimg::swap(Q(pos),Q(left)); pos = left; }
  24425. else { cimg::swap(Q(pos),Q(right)); pos = right; }
  24426. } else { cimg::swap(Q(pos),Q(left)); pos = left; }
  24427. }
  24428. }
  24429. }
  24430. return dist;
  24431. }
  24432. //! Return minimal path in a graph, using the Dijkstra algorithm.
  24433. template<typename tf, typename t>
  24434. static CImg<T> dijkstra(const tf& distance, const unsigned int nb_nodes,
  24435. const unsigned int starting_node, const unsigned int ending_node=~0U) {
  24436. CImg<uintT> foo;
  24437. return dijkstra(distance,nb_nodes,starting_node,ending_node,foo);
  24438. }
  24439. //! Return minimal path in a graph, using the Dijkstra algorithm.
  24440. /**
  24441. \param starting_node Indice of the starting node.
  24442. \param ending_node Indice of the ending node.
  24443. \param previous_node Array that gives the previous node indice in the path to the starting node
  24444. (optional parameter).
  24445. \return Array of distances of each node to the starting node.
  24446. \note image instance corresponds to the adjacency matrix of the graph.
  24447. **/
  24448. template<typename t>
  24449. CImg<T>& dijkstra(const unsigned int starting_node, const unsigned int ending_node,
  24450. CImg<t>& previous_node) {
  24451. return get_dijkstra(starting_node,ending_node,previous_node).move_to(*this);
  24452. }
  24453. //! Return minimal path in a graph, using the Dijkstra algorithm \newinstance.
  24454. template<typename t>
  24455. CImg<T> get_dijkstra(const unsigned int starting_node, const unsigned int ending_node,
  24456. CImg<t>& previous_node) const {
  24457. if (_width!=_height || _depth!=1 || _spectrum!=1)
  24458. throw CImgInstanceException(_cimg_instance
  24459. "dijkstra(): Instance is not a graph adjacency matrix.",
  24460. cimg_instance);
  24461. return dijkstra(*this,_width,starting_node,ending_node,previous_node);
  24462. }
  24463. //! Return minimal path in a graph, using the Dijkstra algorithm.
  24464. CImg<T>& dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) {
  24465. return get_dijkstra(starting_node,ending_node).move_to(*this);
  24466. }
  24467. //! Return minimal path in a graph, using the Dijkstra algorithm \newinstance.
  24468. CImg<Tfloat> get_dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) const {
  24469. CImg<uintT> foo;
  24470. return get_dijkstra(starting_node,ending_node,foo);
  24471. }
  24472. //! Return an image containing the ascii codes of the specified string.
  24473. /**
  24474. \param str input C-string to encode as an image.
  24475. \param is_last_zero Tells if the ending \c '0' character appear in the resulting image.
  24476. \param is_shared Return result that shares its buffer with \p str.
  24477. **/
  24478. static CImg<T> string(const char *const str, const bool is_last_zero=true, const bool is_shared=false) {
  24479. if (!str) return CImg<T>();
  24480. return CImg<T>(str,(unsigned int)std::strlen(str) + (is_last_zero?1:0),1,1,1,is_shared);
  24481. }
  24482. //! Return a \c 1x1 image containing specified value.
  24483. /**
  24484. \param a0 First vector value.
  24485. **/
  24486. static CImg<T> vector(const T& a0) {
  24487. CImg<T> r(1,1);
  24488. r[0] = a0;
  24489. return r;
  24490. }
  24491. //! Return a \c 1x2 image containing specified values.
  24492. /**
  24493. \param a0 First vector value.
  24494. \param a1 Second vector value.
  24495. **/
  24496. static CImg<T> vector(const T& a0, const T& a1) {
  24497. CImg<T> r(1,2); T *ptr = r._data;
  24498. *(ptr++) = a0; *(ptr++) = a1;
  24499. return r;
  24500. }
  24501. //! Return a \c 1x3 image containing specified values.
  24502. /**
  24503. \param a0 First vector value.
  24504. \param a1 Second vector value.
  24505. \param a2 Third vector value.
  24506. **/
  24507. static CImg<T> vector(const T& a0, const T& a1, const T& a2) {
  24508. CImg<T> r(1,3); T *ptr = r._data;
  24509. *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2;
  24510. return r;
  24511. }
  24512. //! Return a \c 1x4 image containing specified values.
  24513. /**
  24514. \param a0 First vector value.
  24515. \param a1 Second vector value.
  24516. \param a2 Third vector value.
  24517. \param a3 Fourth vector value.
  24518. **/
  24519. static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3) {
  24520. CImg<T> r(1,4); T *ptr = r._data;
  24521. *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
  24522. return r;
  24523. }
  24524. //! Return a \c 1x5 image containing specified values.
  24525. static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) {
  24526. CImg<T> r(1,5); T *ptr = r._data;
  24527. *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4;
  24528. return r;
  24529. }
  24530. //! Return a \c 1x6 image containing specified values.
  24531. static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) {
  24532. CImg<T> r(1,6); T *ptr = r._data;
  24533. *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5;
  24534. return r;
  24535. }
  24536. //! Return a \c 1x7 image containing specified values.
  24537. static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
  24538. const T& a4, const T& a5, const T& a6) {
  24539. CImg<T> r(1,7); T *ptr = r._data;
  24540. *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
  24541. *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6;
  24542. return r;
  24543. }
  24544. //! Return a \c 1x8 image containing specified values.
  24545. static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
  24546. const T& a4, const T& a5, const T& a6, const T& a7) {
  24547. CImg<T> r(1,8); T *ptr = r._data;
  24548. *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
  24549. *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
  24550. return r;
  24551. }
  24552. //! Return a \c 1x9 image containing specified values.
  24553. static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
  24554. const T& a4, const T& a5, const T& a6, const T& a7,
  24555. const T& a8) {
  24556. CImg<T> r(1,9); T *ptr = r._data;
  24557. *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
  24558. *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
  24559. *(ptr++) = a8;
  24560. return r;
  24561. }
  24562. //! Return a \c 1x10 image containing specified values.
  24563. static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
  24564. const T& a4, const T& a5, const T& a6, const T& a7,
  24565. const T& a8, const T& a9) {
  24566. CImg<T> r(1,10); T *ptr = r._data;
  24567. *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
  24568. *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
  24569. *(ptr++) = a8; *(ptr++) = a9;
  24570. return r;
  24571. }
  24572. //! Return a \c 1x11 image containing specified values.
  24573. static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
  24574. const T& a4, const T& a5, const T& a6, const T& a7,
  24575. const T& a8, const T& a9, const T& a10) {
  24576. CImg<T> r(1,11); T *ptr = r._data;
  24577. *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
  24578. *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
  24579. *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10;
  24580. return r;
  24581. }
  24582. //! Return a \c 1x12 image containing specified values.
  24583. static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
  24584. const T& a4, const T& a5, const T& a6, const T& a7,
  24585. const T& a8, const T& a9, const T& a10, const T& a11) {
  24586. CImg<T> r(1,12); T *ptr = r._data;
  24587. *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
  24588. *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
  24589. *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
  24590. return r;
  24591. }
  24592. //! Return a \c 1x13 image containing specified values.
  24593. static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
  24594. const T& a4, const T& a5, const T& a6, const T& a7,
  24595. const T& a8, const T& a9, const T& a10, const T& a11,
  24596. const T& a12) {
  24597. CImg<T> r(1,13); T *ptr = r._data;
  24598. *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
  24599. *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
  24600. *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
  24601. *(ptr++) = a12;
  24602. return r;
  24603. }
  24604. //! Return a \c 1x14 image containing specified values.
  24605. static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
  24606. const T& a4, const T& a5, const T& a6, const T& a7,
  24607. const T& a8, const T& a9, const T& a10, const T& a11,
  24608. const T& a12, const T& a13) {
  24609. CImg<T> r(1,14); T *ptr = r._data;
  24610. *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
  24611. *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
  24612. *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
  24613. *(ptr++) = a12; *(ptr++) = a13;
  24614. return r;
  24615. }
  24616. //! Return a \c 1x15 image containing specified values.
  24617. static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
  24618. const T& a4, const T& a5, const T& a6, const T& a7,
  24619. const T& a8, const T& a9, const T& a10, const T& a11,
  24620. const T& a12, const T& a13, const T& a14) {
  24621. CImg<T> r(1,15); T *ptr = r._data;
  24622. *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
  24623. *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
  24624. *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
  24625. *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14;
  24626. return r;
  24627. }
  24628. //! Return a \c 1x16 image containing specified values.
  24629. static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
  24630. const T& a4, const T& a5, const T& a6, const T& a7,
  24631. const T& a8, const T& a9, const T& a10, const T& a11,
  24632. const T& a12, const T& a13, const T& a14, const T& a15) {
  24633. CImg<T> r(1,16); T *ptr = r._data;
  24634. *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
  24635. *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
  24636. *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
  24637. *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15;
  24638. return r;
  24639. }
  24640. //! Return a 1x1 matrix containing specified coefficients.
  24641. /**
  24642. \param a0 First matrix value.
  24643. \note Equivalent to vector(const T&).
  24644. **/
  24645. static CImg<T> matrix(const T& a0) {
  24646. return vector(a0);
  24647. }
  24648. //! Return a 2x2 matrix containing specified coefficients.
  24649. /**
  24650. \param a0 First matrix value.
  24651. \param a1 Second matrix value.
  24652. \param a2 Third matrix value.
  24653. \param a3 Fourth matrix value.
  24654. **/
  24655. static CImg<T> matrix(const T& a0, const T& a1,
  24656. const T& a2, const T& a3) {
  24657. CImg<T> r(2,2); T *ptr = r._data;
  24658. *(ptr++) = a0; *(ptr++) = a1;
  24659. *(ptr++) = a2; *(ptr++) = a3;
  24660. return r;
  24661. }
  24662. //! Return a 3x3 matrix containing specified coefficients.
  24663. /**
  24664. \param a0 First matrix value.
  24665. \param a1 Second matrix value.
  24666. \param a2 Third matrix value.
  24667. \param a3 Fourth matrix value.
  24668. \param a4 Fifth matrix value.
  24669. \param a5 Sixth matrix value.
  24670. \param a6 Seventh matrix value.
  24671. \param a7 Eighth matrix value.
  24672. \param a8 Nineth matrix value.
  24673. **/
  24674. static CImg<T> matrix(const T& a0, const T& a1, const T& a2,
  24675. const T& a3, const T& a4, const T& a5,
  24676. const T& a6, const T& a7, const T& a8) {
  24677. CImg<T> r(3,3); T *ptr = r._data;
  24678. *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2;
  24679. *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5;
  24680. *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8;
  24681. return r;
  24682. }
  24683. //! Return a 4x4 matrix containing specified coefficients.
  24684. static CImg<T> matrix(const T& a0, const T& a1, const T& a2, const T& a3,
  24685. const T& a4, const T& a5, const T& a6, const T& a7,
  24686. const T& a8, const T& a9, const T& a10, const T& a11,
  24687. const T& a12, const T& a13, const T& a14, const T& a15) {
  24688. CImg<T> r(4,4); T *ptr = r._data;
  24689. *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
  24690. *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
  24691. *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
  24692. *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15;
  24693. return r;
  24694. }
  24695. //! Return a 5x5 matrix containing specified coefficients.
  24696. static CImg<T> matrix(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4,
  24697. const T& a5, const T& a6, const T& a7, const T& a8, const T& a9,
  24698. const T& a10, const T& a11, const T& a12, const T& a13, const T& a14,
  24699. const T& a15, const T& a16, const T& a17, const T& a18, const T& a19,
  24700. const T& a20, const T& a21, const T& a22, const T& a23, const T& a24) {
  24701. CImg<T> r(5,5); T *ptr = r._data;
  24702. *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4;
  24703. *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9;
  24704. *(ptr++) = a10; *(ptr++) = a11; *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14;
  24705. *(ptr++) = a15; *(ptr++) = a16; *(ptr++) = a17; *(ptr++) = a18; *(ptr++) = a19;
  24706. *(ptr++) = a20; *(ptr++) = a21; *(ptr++) = a22; *(ptr++) = a23; *(ptr++) = a24;
  24707. return r;
  24708. }
  24709. //! Return a 1x1 symmetric matrix containing specified coefficients.
  24710. /**
  24711. \param a0 First matrix value.
  24712. \note Equivalent to vector(const T&).
  24713. **/
  24714. static CImg<T> tensor(const T& a0) {
  24715. return matrix(a0);
  24716. }
  24717. //! Return a 2x2 symmetric matrix tensor containing specified coefficients.
  24718. static CImg<T> tensor(const T& a0, const T& a1, const T& a2) {
  24719. return matrix(a0,a1,a1,a2);
  24720. }
  24721. //! Return a 3x3 symmetric matrix containing specified coefficients.
  24722. static CImg<T> tensor(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) {
  24723. return matrix(a0,a1,a2,a1,a3,a4,a2,a4,a5);
  24724. }
  24725. //! Return a 1x1 diagonal matrix containing specified coefficients.
  24726. static CImg<T> diagonal(const T& a0) {
  24727. return matrix(a0);
  24728. }
  24729. //! Return a 2x2 diagonal matrix containing specified coefficients.
  24730. static CImg<T> diagonal(const T& a0, const T& a1) {
  24731. return matrix(a0,0,0,a1);
  24732. }
  24733. //! Return a 3x3 diagonal matrix containing specified coefficients.
  24734. static CImg<T> diagonal(const T& a0, const T& a1, const T& a2) {
  24735. return matrix(a0,0,0,0,a1,0,0,0,a2);
  24736. }
  24737. //! Return a 4x4 diagonal matrix containing specified coefficients.
  24738. static CImg<T> diagonal(const T& a0, const T& a1, const T& a2, const T& a3) {
  24739. return matrix(a0,0,0,0,0,a1,0,0,0,0,a2,0,0,0,0,a3);
  24740. }
  24741. //! Return a 5x5 diagonal matrix containing specified coefficients.
  24742. static CImg<T> diagonal(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) {
  24743. 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);
  24744. }
  24745. //! Return a NxN identity matrix.
  24746. /**
  24747. \param N Dimension of the matrix.
  24748. **/
  24749. static CImg<T> identity_matrix(const unsigned int N) {
  24750. CImg<T> res(N,N,1,1,0);
  24751. cimg_forX(res,x) res(x,x) = 1;
  24752. return res;
  24753. }
  24754. //! Return a N-numbered sequence vector from \p a0 to \p a1.
  24755. /**
  24756. \param N Size of the resulting vector.
  24757. \param a0 Starting value of the sequence.
  24758. \param a1 Ending value of the sequence.
  24759. **/
  24760. static CImg<T> sequence(const unsigned int N, const T& a0, const T& a1) {
  24761. if (N) return CImg<T>(1,N).sequence(a0,a1);
  24762. return CImg<T>();
  24763. }
  24764. //! Return a 3x3 rotation matrix from an { axis + angle } or a quaternion.
  24765. /**
  24766. \param x X-coordinate of the rotation axis, or first quaternion coordinate.
  24767. \param y Y-coordinate of the rotation axis, or second quaternion coordinate.
  24768. \param z Z-coordinate of the rotation axis, or third quaternion coordinate.
  24769. \param w Angle of the rotation axis (in degree), or fourth quaternion coordinate.
  24770. \param is_quaternion Tell is the four arguments denotes a set { axis + angle } or a quaternion (x,y,z,w).
  24771. **/
  24772. static CImg<T> rotation_matrix(const float x, const float y, const float z, const float w,
  24773. const bool is_quaternion=false) {
  24774. double X, Y, Z, W, N;
  24775. if (is_quaternion) {
  24776. N = std::sqrt((double)x*x + (double)y*y + (double)z*z + (double)w*w);
  24777. if (N>0) { X = x/N; Y = y/N; Z = z/N; W = w/N; }
  24778. else { X = Y = Z = 0; W = 1; }
  24779. 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),
  24780. (T)(2*X*W + 2*Y*Z),(T)(X*X - Y*Y + Z*Z - W*W),(T)(2*Z*W - 2*X*Y),
  24781. (T)(2*Y*W - 2*X*Z),(T)(2*X*Y + 2*Z*W),(T)(X*X - Y*Y - Z*Z + W*W));
  24782. }
  24783. N = cimg::hypot((double)x,(double)y,(double)z);
  24784. if (N>0) { X = x/N; Y = y/N; Z = z/N; }
  24785. else { X = Y = 0; Z = 1; }
  24786. const double ang = w*cimg::PI/180, c = std::cos(ang), omc = 1 - c, s = std::sin(ang);
  24787. return CImg<T>::matrix((T)(X*X*omc + c),(T)(X*Y*omc - Z*s),(T)(X*Z*omc + Y*s),
  24788. (T)(X*Y*omc + Z*s),(T)(Y*Y*omc + c),(T)(Y*Z*omc - X*s),
  24789. (T)(X*Z*omc - Y*s),(T)(Y*Z*omc + X*s),(T)(Z*Z*omc + c));
  24790. }
  24791. //@}
  24792. //-----------------------------------
  24793. //
  24794. //! \name Value Manipulation
  24795. //@{
  24796. //-----------------------------------
  24797. //! Fill all pixel values with specified value.
  24798. /**
  24799. \param val Fill value.
  24800. **/
  24801. CImg<T>& fill(const T& val) {
  24802. if (is_empty()) return *this;
  24803. if (val && sizeof(T)!=1) cimg_for(*this,ptrd,T) *ptrd = val;
  24804. else std::memset(_data,(int)(ulongT)val,sizeof(T)*size()); // Double cast to allow val to be (void*)
  24805. return *this;
  24806. }
  24807. //! Fill all pixel values with specified value \newinstance.
  24808. CImg<T> get_fill(const T& val) const {
  24809. return CImg<T>(_width,_height,_depth,_spectrum).fill(val);
  24810. }
  24811. //! Fill sequentially all pixel values with specified values.
  24812. /**
  24813. \param val0 First fill value.
  24814. \param val1 Second fill value.
  24815. **/
  24816. CImg<T>& fill(const T& val0, const T& val1) {
  24817. if (is_empty()) return *this;
  24818. T *ptrd, *ptre = end() - 1;
  24819. for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; }
  24820. if (ptrd!=ptre + 1) *(ptrd++) = val0;
  24821. return *this;
  24822. }
  24823. //! Fill sequentially all pixel values with specified values \newinstance.
  24824. CImg<T> get_fill(const T& val0, const T& val1) const {
  24825. return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1);
  24826. }
  24827. //! Fill sequentially all pixel values with specified values \overloading.
  24828. CImg<T>& fill(const T& val0, const T& val1, const T& val2) {
  24829. if (is_empty()) return *this;
  24830. T *ptrd, *ptre = end() - 2;
  24831. for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; }
  24832. ptre+=2;
  24833. switch (ptre - ptrd) {
  24834. case 2 : *(--ptre) = val1; // fallthrough
  24835. case 1 : *(--ptre) = val0; // fallthrough
  24836. }
  24837. return *this;
  24838. }
  24839. //! Fill sequentially all pixel values with specified values \newinstance.
  24840. CImg<T> get_fill(const T& val0, const T& val1, const T& val2) const {
  24841. return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2);
  24842. }
  24843. //! Fill sequentially all pixel values with specified values \overloading.
  24844. CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3) {
  24845. if (is_empty()) return *this;
  24846. T *ptrd, *ptre = end() - 3;
  24847. for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; }
  24848. ptre+=3;
  24849. switch (ptre - ptrd) {
  24850. case 3 : *(--ptre) = val2; // fallthrough
  24851. case 2 : *(--ptre) = val1; // fallthrough
  24852. case 1 : *(--ptre) = val0; // fallthrough
  24853. }
  24854. return *this;
  24855. }
  24856. //! Fill sequentially all pixel values with specified values \newinstance.
  24857. CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3) const {
  24858. return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3);
  24859. }
  24860. //! Fill sequentially all pixel values with specified values \overloading.
  24861. CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4) {
  24862. if (is_empty()) return *this;
  24863. T *ptrd, *ptre = end() - 4;
  24864. for (ptrd = _data; ptrd<ptre; ) {
  24865. *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
  24866. }
  24867. ptre+=4;
  24868. switch (ptre - ptrd) {
  24869. case 4 : *(--ptre) = val3; // fallthrough
  24870. case 3 : *(--ptre) = val2; // fallthrough
  24871. case 2 : *(--ptre) = val1; // fallthrough
  24872. case 1 : *(--ptre) = val0; // fallthrough
  24873. }
  24874. return *this;
  24875. }
  24876. //! Fill sequentially all pixel values with specified values \newinstance.
  24877. CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4) const {
  24878. return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4);
  24879. }
  24880. //! Fill sequentially all pixel values with specified values \overloading.
  24881. CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5) {
  24882. if (is_empty()) return *this;
  24883. T *ptrd, *ptre = end() - 5;
  24884. for (ptrd = _data; ptrd<ptre; ) {
  24885. *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
  24886. }
  24887. ptre+=5;
  24888. switch (ptre - ptrd) {
  24889. case 5 : *(--ptre) = val4; // fallthrough
  24890. case 4 : *(--ptre) = val3; // fallthrough
  24891. case 3 : *(--ptre) = val2; // fallthrough
  24892. case 2 : *(--ptre) = val1; // fallthrough
  24893. case 1 : *(--ptre) = val0; // fallthrough
  24894. }
  24895. return *this;
  24896. }
  24897. //! Fill sequentially all pixel values with specified values \newinstance.
  24898. CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5) const {
  24899. return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5);
  24900. }
  24901. //! Fill sequentially all pixel values with specified values \overloading.
  24902. CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  24903. const T& val6) {
  24904. if (is_empty()) return *this;
  24905. T *ptrd, *ptre = end() - 6;
  24906. for (ptrd = _data; ptrd<ptre; ) {
  24907. *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
  24908. *(ptrd++) = val6;
  24909. }
  24910. ptre+=6;
  24911. switch (ptre - ptrd) {
  24912. case 6 : *(--ptre) = val5; // fallthrough
  24913. case 5 : *(--ptre) = val4; // fallthrough
  24914. case 4 : *(--ptre) = val3; // fallthrough
  24915. case 3 : *(--ptre) = val2; // fallthrough
  24916. case 2 : *(--ptre) = val1; // fallthrough
  24917. case 1 : *(--ptre) = val0; // fallthrough
  24918. }
  24919. return *this;
  24920. }
  24921. //! Fill sequentially all pixel values with specified values \newinstance.
  24922. CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  24923. const T& val6) const {
  24924. return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6);
  24925. }
  24926. //! Fill sequentially all pixel values with specified values \overloading.
  24927. CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  24928. const T& val6, const T& val7) {
  24929. if (is_empty()) return *this;
  24930. T *ptrd, *ptre = end() - 7;
  24931. for (ptrd = _data; ptrd<ptre; ) {
  24932. *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3;
  24933. *(ptrd++) = val4; *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7;
  24934. }
  24935. ptre+=7;
  24936. switch (ptre - ptrd) {
  24937. case 7 : *(--ptre) = val6; // fallthrough
  24938. case 6 : *(--ptre) = val5; // fallthrough
  24939. case 5 : *(--ptre) = val4; // fallthrough
  24940. case 4 : *(--ptre) = val3; // fallthrough
  24941. case 3 : *(--ptre) = val2; // fallthrough
  24942. case 2 : *(--ptre) = val1; // fallthrough
  24943. case 1 : *(--ptre) = val0; // fallthrough
  24944. }
  24945. return *this;
  24946. }
  24947. //! Fill sequentially all pixel values with specified values \newinstance.
  24948. CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  24949. const T& val6, const T& val7) const {
  24950. return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7);
  24951. }
  24952. //! Fill sequentially all pixel values with specified values \overloading.
  24953. CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  24954. const T& val6, const T& val7, const T& val8) {
  24955. if (is_empty()) return *this;
  24956. T *ptrd, *ptre = end() - 8;
  24957. for (ptrd = _data; ptrd<ptre; ) {
  24958. *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2;
  24959. *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
  24960. *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8;
  24961. }
  24962. ptre+=8;
  24963. switch (ptre - ptrd) {
  24964. case 8 : *(--ptre) = val7; // fallthrough
  24965. case 7 : *(--ptre) = val6; // fallthrough
  24966. case 6 : *(--ptre) = val5; // fallthrough
  24967. case 5 : *(--ptre) = val4; // fallthrough
  24968. case 4 : *(--ptre) = val3; // fallthrough
  24969. case 3 : *(--ptre) = val2; // fallthrough
  24970. case 2 : *(--ptre) = val1; // fallthrough
  24971. case 1 : *(--ptre) = val0; // fallthrough
  24972. }
  24973. return *this;
  24974. }
  24975. //! Fill sequentially all pixel values with specified values \newinstance.
  24976. CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  24977. const T& val6, const T& val7, const T& val8) const {
  24978. return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8);
  24979. }
  24980. //! Fill sequentially all pixel values with specified values \overloading.
  24981. CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  24982. const T& val6, const T& val7, const T& val8, const T& val9) {
  24983. if (is_empty()) return *this;
  24984. T *ptrd, *ptre = end() - 9;
  24985. for (ptrd = _data; ptrd<ptre; ) {
  24986. *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
  24987. *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9;
  24988. }
  24989. ptre+=9;
  24990. switch (ptre - ptrd) {
  24991. case 9 : *(--ptre) = val8; // fallthrough
  24992. case 8 : *(--ptre) = val7; // fallthrough
  24993. case 7 : *(--ptre) = val6; // fallthrough
  24994. case 6 : *(--ptre) = val5; // fallthrough
  24995. case 5 : *(--ptre) = val4; // fallthrough
  24996. case 4 : *(--ptre) = val3; // fallthrough
  24997. case 3 : *(--ptre) = val2; // fallthrough
  24998. case 2 : *(--ptre) = val1; // fallthrough
  24999. case 1 : *(--ptre) = val0; // fallthrough
  25000. }
  25001. return *this;
  25002. }
  25003. //! Fill sequentially all pixel values with specified values \newinstance.
  25004. CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  25005. const T& val6, const T& val7, const T& val8, const T& val9) const {
  25006. return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9);
  25007. }
  25008. //! Fill sequentially all pixel values with specified values \overloading.
  25009. CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  25010. const T& val6, const T& val7, const T& val8, const T& val9, const T& val10) {
  25011. if (is_empty()) return *this;
  25012. T *ptrd, *ptre = end() - 10;
  25013. for (ptrd = _data; ptrd<ptre; ) {
  25014. *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
  25015. *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9;
  25016. *(ptrd++) = val10;
  25017. }
  25018. ptre+=10;
  25019. switch (ptre - ptrd) {
  25020. case 10 : *(--ptre) = val9; // fallthrough
  25021. case 9 : *(--ptre) = val8; // fallthrough
  25022. case 8 : *(--ptre) = val7; // fallthrough
  25023. case 7 : *(--ptre) = val6; // fallthrough
  25024. case 6 : *(--ptre) = val5; // fallthrough
  25025. case 5 : *(--ptre) = val4; // fallthrough
  25026. case 4 : *(--ptre) = val3; // fallthrough
  25027. case 3 : *(--ptre) = val2; // fallthrough
  25028. case 2 : *(--ptre) = val1; // fallthrough
  25029. case 1 : *(--ptre) = val0; // fallthrough
  25030. }
  25031. return *this;
  25032. }
  25033. //! Fill sequentially all pixel values with specified values \newinstance.
  25034. CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  25035. const T& val6, const T& val7, const T& val8, const T& val9, const T& val10) const {
  25036. return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10);
  25037. }
  25038. //! Fill sequentially all pixel values with specified values \overloading.
  25039. CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  25040. const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11) {
  25041. if (is_empty()) return *this;
  25042. T *ptrd, *ptre = end() - 11;
  25043. for (ptrd = _data; ptrd<ptre; ) {
  25044. *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
  25045. *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
  25046. }
  25047. ptre+=11;
  25048. switch (ptre - ptrd) {
  25049. case 11 : *(--ptre) = val10; // fallthrough
  25050. case 10 : *(--ptre) = val9; // fallthrough
  25051. case 9 : *(--ptre) = val8; // fallthrough
  25052. case 8 : *(--ptre) = val7; // fallthrough
  25053. case 7 : *(--ptre) = val6; // fallthrough
  25054. case 6 : *(--ptre) = val5; // fallthrough
  25055. case 5 : *(--ptre) = val4; // fallthrough
  25056. case 4 : *(--ptre) = val3; // fallthrough
  25057. case 3 : *(--ptre) = val2; // fallthrough
  25058. case 2 : *(--ptre) = val1; // fallthrough
  25059. case 1 : *(--ptre) = val0; // fallthrough
  25060. }
  25061. return *this;
  25062. }
  25063. //! Fill sequentially all pixel values with specified values \newinstance.
  25064. CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  25065. const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11) const {
  25066. return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
  25067. val11);
  25068. }
  25069. //! Fill sequentially all pixel values with specified values \overloading.
  25070. CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  25071. const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
  25072. const T& val12) {
  25073. if (is_empty()) return *this;
  25074. T *ptrd, *ptre = end() - 12;
  25075. for (ptrd = _data; ptrd<ptre; ) {
  25076. *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
  25077. *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
  25078. *(ptrd++) = val12;
  25079. }
  25080. ptre+=12;
  25081. switch (ptre - ptrd) {
  25082. case 12 : *(--ptre) = val11; // fallthrough
  25083. case 11 : *(--ptre) = val10; // fallthrough
  25084. case 10 : *(--ptre) = val9; // fallthrough
  25085. case 9 : *(--ptre) = val8; // fallthrough
  25086. case 8 : *(--ptre) = val7; // fallthrough
  25087. case 7 : *(--ptre) = val6; // fallthrough
  25088. case 6 : *(--ptre) = val5; // fallthrough
  25089. case 5 : *(--ptre) = val4; // fallthrough
  25090. case 4 : *(--ptre) = val3; // fallthrough
  25091. case 3 : *(--ptre) = val2; // fallthrough
  25092. case 2 : *(--ptre) = val1; // fallthrough
  25093. case 1 : *(--ptre) = val0; // fallthrough
  25094. }
  25095. return *this;
  25096. }
  25097. //! Fill sequentially all pixel values with specified values \newinstance.
  25098. CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  25099. const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
  25100. const T& val12) const {
  25101. return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
  25102. val11,val12);
  25103. }
  25104. //! Fill sequentially all pixel values with specified values \overloading.
  25105. CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  25106. const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
  25107. const T& val12, const T& val13) {
  25108. if (is_empty()) return *this;
  25109. T *ptrd, *ptre = end() - 13;
  25110. for (ptrd = _data; ptrd<ptre; ) {
  25111. *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
  25112. *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
  25113. *(ptrd++) = val12; *(ptrd++) = val13;
  25114. }
  25115. ptre+=13;
  25116. switch (ptre - ptrd) {
  25117. case 13 : *(--ptre) = val12; // fallthrough
  25118. case 12 : *(--ptre) = val11; // fallthrough
  25119. case 11 : *(--ptre) = val10; // fallthrough
  25120. case 10 : *(--ptre) = val9; // fallthrough
  25121. case 9 : *(--ptre) = val8; // fallthrough
  25122. case 8 : *(--ptre) = val7; // fallthrough
  25123. case 7 : *(--ptre) = val6; // fallthrough
  25124. case 6 : *(--ptre) = val5; // fallthrough
  25125. case 5 : *(--ptre) = val4; // fallthrough
  25126. case 4 : *(--ptre) = val3; // fallthrough
  25127. case 3 : *(--ptre) = val2; // fallthrough
  25128. case 2 : *(--ptre) = val1; // fallthrough
  25129. case 1 : *(--ptre) = val0; // fallthrough
  25130. }
  25131. return *this;
  25132. }
  25133. //! Fill sequentially all pixel values with specified values \newinstance.
  25134. CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  25135. const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
  25136. const T& val12, const T& val13) const {
  25137. return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
  25138. val11,val12,val13);
  25139. }
  25140. //! Fill sequentially all pixel values with specified values \overloading.
  25141. CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  25142. const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
  25143. const T& val12, const T& val13, const T& val14) {
  25144. if (is_empty()) return *this;
  25145. T *ptrd, *ptre = end() - 14;
  25146. for (ptrd = _data; ptrd<ptre; ) {
  25147. *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
  25148. *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
  25149. *(ptrd++) = val12; *(ptrd++) = val13; *(ptrd++) = val14;
  25150. }
  25151. ptre+=14;
  25152. switch (ptre - ptrd) {
  25153. case 14 : *(--ptre) = val13; // fallthrough
  25154. case 13 : *(--ptre) = val12; // fallthrough
  25155. case 12 : *(--ptre) = val11; // fallthrough
  25156. case 11 : *(--ptre) = val10; // fallthrough
  25157. case 10 : *(--ptre) = val9; // fallthrough
  25158. case 9 : *(--ptre) = val8; // fallthrough
  25159. case 8 : *(--ptre) = val7; // fallthrough
  25160. case 7 : *(--ptre) = val6; // fallthrough
  25161. case 6 : *(--ptre) = val5; // fallthrough
  25162. case 5 : *(--ptre) = val4; // fallthrough
  25163. case 4 : *(--ptre) = val3; // fallthrough
  25164. case 3 : *(--ptre) = val2; // fallthrough
  25165. case 2 : *(--ptre) = val1; // fallthrough
  25166. case 1 : *(--ptre) = val0; // fallthrough
  25167. }
  25168. return *this;
  25169. }
  25170. //! Fill sequentially all pixel values with specified values \newinstance.
  25171. CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  25172. const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
  25173. const T& val12, const T& val13, const T& val14) const {
  25174. return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
  25175. val11,val12,val13,val14);
  25176. }
  25177. //! Fill sequentially all pixel values with specified values \overloading.
  25178. CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  25179. const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
  25180. const T& val12, const T& val13, const T& val14, const T& val15) {
  25181. if (is_empty()) return *this;
  25182. T *ptrd, *ptre = end() - 15;
  25183. for (ptrd = _data; ptrd<ptre; ) {
  25184. *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
  25185. *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
  25186. *(ptrd++) = val12; *(ptrd++) = val13; *(ptrd++) = val14; *(ptrd++) = val15;
  25187. }
  25188. ptre+=15;
  25189. switch (ptre - ptrd) {
  25190. case 15 : *(--ptre) = val14; // fallthrough
  25191. case 14 : *(--ptre) = val13; // fallthrough
  25192. case 13 : *(--ptre) = val12; // fallthrough
  25193. case 12 : *(--ptre) = val11; // fallthrough
  25194. case 11 : *(--ptre) = val10; // fallthrough
  25195. case 10 : *(--ptre) = val9; // fallthrough
  25196. case 9 : *(--ptre) = val8; // fallthrough
  25197. case 8 : *(--ptre) = val7; // fallthrough
  25198. case 7 : *(--ptre) = val6; // fallthrough
  25199. case 6 : *(--ptre) = val5; // fallthrough
  25200. case 5 : *(--ptre) = val4; // fallthrough
  25201. case 4 : *(--ptre) = val3; // fallthrough
  25202. case 3 : *(--ptre) = val2; // fallthrough
  25203. case 2 : *(--ptre) = val1; // fallthrough
  25204. case 1 : *(--ptre) = val0; // fallthrough
  25205. }
  25206. return *this;
  25207. }
  25208. //! Fill sequentially all pixel values with specified values \newinstance.
  25209. CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  25210. const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
  25211. const T& val12, const T& val13, const T& val14, const T& val15) const {
  25212. return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
  25213. val11,val12,val13,val14,val15);
  25214. }
  25215. //! Fill sequentially pixel values according to a given expression.
  25216. /**
  25217. \param expression C-string describing a math formula, or a sequence of values.
  25218. \param repeat_values In case a list of values is provided, tells if this list must be repeated for the filling.
  25219. \param allow_formula Tells that mathematical formulas are authorized for the filling.
  25220. \param list_inputs In case of a mathematical expression, attach a list of images to the specified expression.
  25221. \param[out] list_outputs In case of a math expression, list of images atatched to the specified expression.
  25222. **/
  25223. CImg<T>& fill(const char *const expression, const bool repeat_values, const bool allow_formula=true,
  25224. const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) {
  25225. return _fill(expression,repeat_values,allow_formula,list_inputs,list_outputs,"fill",0);
  25226. }
  25227. CImg<T>& _fill(const char *const expression, const bool repeat_values, bool allow_formula,
  25228. const CImgList<T> *const list_inputs, CImgList<T> *const list_outputs,
  25229. const char *const calling_function, const CImg<T> *provides_copy) {
  25230. if (is_empty() || !expression || !*expression) return *this;
  25231. const unsigned int omode = cimg::exception_mode();
  25232. cimg::exception_mode(0);
  25233. CImg<charT> is_error;
  25234. bool is_value_sequence = false;
  25235. cimg_abort_init;
  25236. if (allow_formula) {
  25237. // Try to pre-detect regular value sequence to avoid exception thrown by _cimg_math_parser.
  25238. double value;
  25239. char sep;
  25240. const int err = cimg_sscanf(expression,"%lf %c",&value,&sep);
  25241. if (err==1 || (err==2 && sep==',')) {
  25242. if (err==1) return fill((T)value);
  25243. else is_value_sequence = true;
  25244. }
  25245. // Try to fill values according to a formula.
  25246. _cimg_abort_init_omp;
  25247. if (!is_value_sequence) try {
  25248. CImg<T> base = provides_copy?provides_copy->get_shared():get_shared();
  25249. _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' ||
  25250. *expression=='*' || *expression==':'),
  25251. calling_function,base,this,list_inputs,list_outputs,true);
  25252. if (!provides_copy && expression && *expression!='>' && *expression!='<' && *expression!=':' &&
  25253. mp.need_input_copy)
  25254. base.assign().assign(*this,false); // Needs input copy
  25255. bool do_in_parallel = false;
  25256. #ifdef cimg_use_openmp
  25257. cimg_openmp_if(*expression=='*' || *expression==':' ||
  25258. (mp.is_parallelizable && _width>=320 && _height*_depth*_spectrum>=2))
  25259. do_in_parallel = true;
  25260. #endif
  25261. if (mp.result_dim) { // Vector-valued expression
  25262. const unsigned int N = std::min(mp.result_dim,_spectrum);
  25263. const ulongT whd = (ulongT)_width*_height*_depth;
  25264. T *ptrd = *expression=='<'?_data + _width*_height*_depth - 1:_data;
  25265. if (*expression=='<') {
  25266. CImg<doubleT> res(1,mp.result_dim);
  25267. cimg_rofYZ(*this,y,z) {
  25268. cimg_abort_test;
  25269. cimg_rofX(*this,x) {
  25270. mp(x,y,z,0,res._data);
  25271. const double *ptrs = res._data;
  25272. T *_ptrd = ptrd--; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; }
  25273. }
  25274. }
  25275. } else if (*expression=='>' || !do_in_parallel) {
  25276. CImg<doubleT> res(1,mp.result_dim);
  25277. cimg_forYZ(*this,y,z) {
  25278. cimg_abort_test;
  25279. cimg_forX(*this,x) {
  25280. mp(x,y,z,0,res._data);
  25281. const double *ptrs = res._data;
  25282. T *_ptrd = ptrd++; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; }
  25283. }
  25284. }
  25285. } else {
  25286. #ifdef cimg_use_openmp
  25287. cimg_pragma_openmp(parallel)
  25288. {
  25289. _cimg_math_parser
  25290. _mp = omp_get_thread_num()?mp:_cimg_math_parser(),
  25291. &lmp = omp_get_thread_num()?_mp:mp;
  25292. lmp.is_fill = true;
  25293. cimg_pragma_openmp(for collapse(2))
  25294. cimg_forYZ(*this,y,z) _cimg_abort_try_omp {
  25295. cimg_abort_test;
  25296. CImg<doubleT> res(1,lmp.result_dim);
  25297. T *ptrd = data(0,y,z,0);
  25298. cimg_forX(*this,x) {
  25299. lmp(x,y,z,0,res._data);
  25300. const double *ptrs = res._data;
  25301. T *_ptrd = ptrd++; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; }
  25302. }
  25303. } _cimg_abort_catch_omp _cimg_abort_catch_fill_omp
  25304. }
  25305. #endif
  25306. }
  25307. } else { // Scalar-valued expression
  25308. T *ptrd = *expression=='<'?end() - 1:_data;
  25309. if (*expression=='<')
  25310. cimg_rofYZC(*this,y,z,c) { cimg_abort_test; cimg_rofX(*this,x) *(ptrd--) = (T)mp(x,y,z,c); }
  25311. else if (*expression=='>' || !do_in_parallel)
  25312. cimg_forYZC(*this,y,z,c) { cimg_abort_test; cimg_forX(*this,x) *(ptrd++) = (T)mp(x,y,z,c); }
  25313. else {
  25314. #ifdef cimg_use_openmp
  25315. cimg_pragma_openmp(parallel)
  25316. {
  25317. _cimg_math_parser
  25318. _mp = omp_get_thread_num()?mp:_cimg_math_parser(),
  25319. &lmp = omp_get_thread_num()?_mp:mp;
  25320. lmp.is_fill = true;
  25321. cimg_pragma_openmp(for collapse(3))
  25322. cimg_forYZC(*this,y,z,c) _cimg_abort_try_omp {
  25323. cimg_abort_test;
  25324. T *ptrd = data(0,y,z,c);
  25325. cimg_forX(*this,x) *ptrd++ = (T)lmp(x,y,z,c);
  25326. } _cimg_abort_catch_omp _cimg_abort_catch_fill_omp
  25327. }
  25328. #endif
  25329. }
  25330. }
  25331. mp.end();
  25332. } catch (CImgException& e) { CImg<charT>::string(e._message).move_to(is_error); }
  25333. }
  25334. // Try to fill values according to a value sequence.
  25335. if (!allow_formula || is_value_sequence || is_error) {
  25336. CImg<charT> item(256);
  25337. char sep = 0;
  25338. const char *nexpression = expression;
  25339. ulongT nb = 0;
  25340. const ulongT siz = size();
  25341. T *ptrd = _data;
  25342. for (double val = 0; *nexpression && nb<siz; ++nb) {
  25343. sep = 0;
  25344. const int err = cimg_sscanf(nexpression,"%255[ \n\t0-9.eEinfa+-]%c",item._data,&sep);
  25345. if (err>0 && cimg_sscanf(item,"%lf",&val)==1 && (sep==',' || sep==';' || err==1)) {
  25346. nexpression+=std::strlen(item) + (err>1);
  25347. *(ptrd++) = (T)val;
  25348. } else break;
  25349. }
  25350. cimg::exception_mode(omode);
  25351. if (nb<siz && (sep || *nexpression)) {
  25352. if (is_error) throw CImgArgumentException("%s",is_error._data);
  25353. else throw CImgArgumentException(_cimg_instance
  25354. "%s(): Invalid sequence of filling values '%s'.",
  25355. cimg_instance,calling_function,expression);
  25356. }
  25357. if (repeat_values && nb && nb<siz)
  25358. for (T *ptrs = _data, *const ptre = _data + siz; ptrd<ptre; ++ptrs) *(ptrd++) = *ptrs;
  25359. }
  25360. cimg::exception_mode(omode);
  25361. cimg_abort_test;
  25362. return *this;
  25363. }
  25364. //! Fill sequentially pixel values according to a given expression \newinstance.
  25365. CImg<T> get_fill(const char *const expression, const bool repeat_values, const bool allow_formula=true,
  25366. const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const {
  25367. return (+*this).fill(expression,repeat_values,allow_formula,list_inputs,list_outputs);
  25368. }
  25369. //! Fill sequentially pixel values according to the values found in another image.
  25370. /**
  25371. \param values Image containing the values used for the filling.
  25372. \param repeat_values In case there are less values than necessary in \c values, tells if these values must be
  25373. repeated for the filling.
  25374. **/
  25375. template<typename t>
  25376. CImg<T>& fill(const CImg<t>& values, const bool repeat_values=true) {
  25377. if (is_empty() || !values) return *this;
  25378. T *ptrd = _data, *ptre = ptrd + size();
  25379. for (t *ptrs = values._data, *ptrs_end = ptrs + values.size(); ptrs<ptrs_end && ptrd<ptre; ++ptrs)
  25380. *(ptrd++) = (T)*ptrs;
  25381. if (repeat_values && ptrd<ptre) for (T *ptrs = _data; ptrd<ptre; ++ptrs) *(ptrd++) = *ptrs;
  25382. return *this;
  25383. }
  25384. //! Fill sequentially pixel values according to the values found in another image \newinstance.
  25385. template<typename t>
  25386. CImg<T> get_fill(const CImg<t>& values, const bool repeat_values=true) const {
  25387. return repeat_values?CImg<T>(_width,_height,_depth,_spectrum).fill(values,repeat_values):
  25388. (+*this).fill(values,repeat_values);
  25389. }
  25390. //! Fill pixel values along the X-axis at a specified pixel position.
  25391. /**
  25392. \param y Y-coordinate of the filled column.
  25393. \param z Z-coordinate of the filled column.
  25394. \param c C-coordinate of the filled column.
  25395. \param a0 First fill value.
  25396. **/
  25397. CImg<T>& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const int a0, ...) {
  25398. #define _cimg_fill1(x,y,z,c,off,siz,t) { \
  25399. va_list ap; va_start(ap,a0); T *ptrd = data(x,y,z,c); *ptrd = (T)a0; \
  25400. for (unsigned int k = 1; k<siz; ++k) { ptrd+=off; *ptrd = (T)va_arg(ap,t); } \
  25401. va_end(ap); }
  25402. if (y<_height && z<_depth && c<_spectrum) _cimg_fill1(0,y,z,c,1,_width,int);
  25403. return *this;
  25404. }
  25405. //! Fill pixel values along the X-axis at a specified pixel position \overloading.
  25406. CImg<T>& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const double a0, ...) {
  25407. if (y<_height && z<_depth && c<_spectrum) _cimg_fill1(0,y,z,c,1,_width,double);
  25408. return *this;
  25409. }
  25410. //! Fill pixel values along the Y-axis at a specified pixel position.
  25411. /**
  25412. \param x X-coordinate of the filled row.
  25413. \param z Z-coordinate of the filled row.
  25414. \param c C-coordinate of the filled row.
  25415. \param a0 First fill value.
  25416. **/
  25417. CImg<T>& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const int a0, ...) {
  25418. if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,int);
  25419. return *this;
  25420. }
  25421. //! Fill pixel values along the Y-axis at a specified pixel position \overloading.
  25422. CImg<T>& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const double a0, ...) {
  25423. if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,double);
  25424. return *this;
  25425. }
  25426. //! Fill pixel values along the Z-axis at a specified pixel position.
  25427. /**
  25428. \param x X-coordinate of the filled slice.
  25429. \param y Y-coordinate of the filled slice.
  25430. \param c C-coordinate of the filled slice.
  25431. \param a0 First fill value.
  25432. **/
  25433. CImg<T>& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const int a0, ...) {
  25434. const ulongT wh = (ulongT)_width*_height;
  25435. if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,int);
  25436. return *this;
  25437. }
  25438. //! Fill pixel values along the Z-axis at a specified pixel position \overloading.
  25439. CImg<T>& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const double a0, ...) {
  25440. const ulongT wh = (ulongT)_width*_height;
  25441. if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,double);
  25442. return *this;
  25443. }
  25444. //! Fill pixel values along the C-axis at a specified pixel position.
  25445. /**
  25446. \param x X-coordinate of the filled channel.
  25447. \param y Y-coordinate of the filled channel.
  25448. \param z Z-coordinate of the filled channel.
  25449. \param a0 First filling value.
  25450. **/
  25451. CImg<T>& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const int a0, ...) {
  25452. const ulongT whd = (ulongT)_width*_height*_depth;
  25453. if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,int);
  25454. return *this;
  25455. }
  25456. //! Fill pixel values along the C-axis at a specified pixel position \overloading.
  25457. CImg<T>& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const double a0, ...) {
  25458. const ulongT whd = (ulongT)_width*_height*_depth;
  25459. if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,double);
  25460. return *this;
  25461. }
  25462. //! Discard specified sequence of values in the image buffer, along a specific axis.
  25463. /**
  25464. \param values Sequence of values to discard.
  25465. \param axis Axis along which the values are discarded. If set to \c 0 (default value)
  25466. the method does it for all the buffer values and returns a one-column vector.
  25467. \note Discarded values will change the image geometry, so the resulting image
  25468. is returned as a one-column vector.
  25469. **/
  25470. template<typename t>
  25471. CImg<T>& discard(const CImg<t>& values, const char axis=0) {
  25472. if (is_empty() || !values) return *this;
  25473. return get_discard(values,axis).move_to(*this);
  25474. }
  25475. template<typename t>
  25476. CImg<T> get_discard(const CImg<t>& values, const char axis=0) const {
  25477. CImg<T> res;
  25478. if (!values) return +*this;
  25479. if (is_empty()) return res;
  25480. const ulongT vsiz = values.size();
  25481. const char _axis = cimg::lowercase(axis);
  25482. ulongT j = 0;
  25483. unsigned int k = 0;
  25484. int i0 = 0;
  25485. res.assign(width(),height(),depth(),spectrum());
  25486. switch (_axis) {
  25487. case 'x' : {
  25488. cimg_forX(*this,i) {
  25489. if ((*this)(i)!=(T)values[j]) {
  25490. if (j) --i;
  25491. res.draw_image(k,get_columns(i0,i));
  25492. k+=i - i0 + 1; i0 = i + 1; j = 0;
  25493. } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
  25494. }
  25495. if (i0<width()) { res.draw_image(k,get_columns(i0,width() - 1)); k+=width() - i0; }
  25496. res.resize(k,-100,-100,-100,0);
  25497. } break;
  25498. case 'y' : {
  25499. cimg_forY(*this,i) {
  25500. if ((*this)(0,i)!=(T)values[j]) {
  25501. if (j) --i;
  25502. res.draw_image(0,k,get_rows(i0,i));
  25503. k+=i - i0 + 1; i0 = i + 1; j = 0;
  25504. } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
  25505. }
  25506. if (i0<height()) { res.draw_image(0,k,get_rows(i0,height() - 1)); k+=height() - i0; }
  25507. res.resize(-100,k,-100,-100,0);
  25508. } break;
  25509. case 'z' : {
  25510. cimg_forZ(*this,i) {
  25511. if ((*this)(0,0,i)!=(T)values[j]) {
  25512. if (j) --i;
  25513. res.draw_image(0,0,k,get_slices(i0,i));
  25514. k+=i - i0 + 1; i0 = i + 1; j = 0;
  25515. } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
  25516. }
  25517. if (i0<depth()) { res.draw_image(0,0,k,get_slices(i0,height() - 1)); k+=depth() - i0; }
  25518. res.resize(-100,-100,k,-100,0);
  25519. } break;
  25520. case 'c' : {
  25521. cimg_forC(*this,i) {
  25522. if ((*this)(0,0,0,i)!=(T)values[j]) {
  25523. if (j) --i;
  25524. res.draw_image(0,0,0,k,get_channels(i0,i));
  25525. k+=i - i0 + 1; i0 = i + 1; j = 0;
  25526. } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
  25527. }
  25528. if (i0<spectrum()) { res.draw_image(0,0,k,get_channels(i0,height() - 1)); k+=spectrum() - i0; }
  25529. res.resize(-100,-100,-100,k,0);
  25530. } break;
  25531. default : {
  25532. res.unroll('y');
  25533. cimg_foroff(*this,i) {
  25534. if ((*this)[i]!=(T)values[j]) {
  25535. if (j) --i;
  25536. std::memcpy(res._data + k,_data + i0,(i - i0 + 1)*sizeof(T));
  25537. k+=i - i0 + 1; i0 = (int)i + 1; j = 0;
  25538. } else { ++j; if (j>=vsiz) { j = 0; i0 = (int)i + 1; }}
  25539. }
  25540. const ulongT siz = size();
  25541. if ((ulongT)i0<siz) { std::memcpy(res._data + k,_data + i0,(siz - i0)*sizeof(T)); k+=siz - i0; }
  25542. res.resize(1,k,1,1,0);
  25543. }
  25544. }
  25545. return res;
  25546. }
  25547. //! Discard neighboring duplicates in the image buffer, along the specified axis.
  25548. CImg<T>& discard(const char axis=0) {
  25549. return get_discard(axis).move_to(*this);
  25550. }
  25551. //! Discard neighboring duplicates in the image buffer, along the specified axis \newinstance.
  25552. CImg<T> get_discard(const char axis=0) const {
  25553. CImg<T> res;
  25554. if (is_empty()) return res;
  25555. const char _axis = cimg::lowercase(axis);
  25556. T current = *_data?(T)0:(T)1;
  25557. int j = 0;
  25558. res.assign(width(),height(),depth(),spectrum());
  25559. switch (_axis) {
  25560. case 'x' : {
  25561. cimg_forX(*this,i)
  25562. if ((*this)(i)!=current) { res.draw_image(j++,get_column(i)); current = (*this)(i); }
  25563. res.resize(j,-100,-100,-100,0);
  25564. } break;
  25565. case 'y' : {
  25566. cimg_forY(*this,i)
  25567. if ((*this)(0,i)!=current) { res.draw_image(0,j++,get_row(i)); current = (*this)(0,i); }
  25568. res.resize(-100,j,-100,-100,0);
  25569. } break;
  25570. case 'z' : {
  25571. cimg_forZ(*this,i)
  25572. if ((*this)(0,0,i)!=current) { res.draw_image(0,0,j++,get_slice(i)); current = (*this)(0,0,i); }
  25573. res.resize(-100,-100,j,-100,0);
  25574. } break;
  25575. case 'c' : {
  25576. cimg_forC(*this,i)
  25577. if ((*this)(0,0,0,i)!=current) { res.draw_image(0,0,0,j++,get_channel(i)); current = (*this)(0,0,0,i); }
  25578. res.resize(-100,-100,-100,j,0);
  25579. } break;
  25580. default : {
  25581. res.unroll('y');
  25582. cimg_foroff(*this,i)
  25583. if ((*this)[i]!=current) res[j++] = current = (*this)[i];
  25584. res.resize(-100,j,-100,-100,0);
  25585. }
  25586. }
  25587. return res;
  25588. }
  25589. //! Invert endianness of all pixel values.
  25590. /**
  25591. **/
  25592. CImg<T>& invert_endianness() {
  25593. cimg::invert_endianness(_data,size());
  25594. return *this;
  25595. }
  25596. //! Invert endianness of all pixel values \newinstance.
  25597. CImg<T> get_invert_endianness() const {
  25598. return (+*this).invert_endianness();
  25599. }
  25600. //! Fill image with random values in specified range.
  25601. /**
  25602. \param val_min Minimal authorized random value.
  25603. \param val_max Maximal authorized random value.
  25604. \note Random variables are uniformely distributed in [val_min,val_max].
  25605. **/
  25606. CImg<T>& rand(const T& val_min, const T& val_max) {
  25607. const float delta = (float)val_max - (float)val_min + (cimg::type<T>::is_float()?0:1);
  25608. if (cimg::type<T>::is_float()) cimg_for(*this,ptrd,T) *ptrd = (T)(val_min + cimg::rand()*delta);
  25609. else cimg_for(*this,ptrd,T) *ptrd = std::min(val_max,(T)(val_min + cimg::rand()*delta));
  25610. return *this;
  25611. }
  25612. //! Fill image with random values in specified range \newinstance.
  25613. CImg<T> get_rand(const T& val_min, const T& val_max) const {
  25614. return (+*this).rand(val_min,val_max);
  25615. }
  25616. //! Round pixel values.
  25617. /**
  25618. \param y Rounding precision.
  25619. \param rounding_type Rounding type. Can be:
  25620. - \c -1: Backward.
  25621. - \c 0: Nearest.
  25622. - \c 1: Forward.
  25623. **/
  25624. CImg<T>& round(const double y=1, const int rounding_type=0) {
  25625. if (y>0)
  25626. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=8192))
  25627. cimg_rof(*this,ptrd,T) *ptrd = cimg::round(*ptrd,y,rounding_type);
  25628. return *this;
  25629. }
  25630. //! Round pixel values \newinstance.
  25631. CImg<T> get_round(const double y=1, const unsigned int rounding_type=0) const {
  25632. return (+*this).round(y,rounding_type);
  25633. }
  25634. //! Add random noise to pixel values.
  25635. /**
  25636. \param sigma Amplitude of the random additive noise. If \p sigma<0, it stands for a percentage of the
  25637. global value range.
  25638. \param noise_type Type of additive noise (can be \p 0=gaussian, \p 1=uniform, \p 2=Salt and Pepper,
  25639. \p 3=Poisson or \p 4=Rician).
  25640. \return A reference to the modified image instance.
  25641. \note
  25642. - For Poisson noise (\p noise_type=3), parameter \p sigma is ignored, as Poisson noise only depends on
  25643. the image value itself.
  25644. - Function \p CImg<T>::get_noise() is also defined. It returns a non-shared modified copy of the image instance.
  25645. \par Example
  25646. \code
  25647. const CImg<float> img("reference.jpg"), res = img.get_noise(40);
  25648. (img,res.normalize(0,255)).display();
  25649. \endcode
  25650. \image html ref_noise.jpg
  25651. **/
  25652. CImg<T>& noise(const double sigma, const unsigned int noise_type=0) {
  25653. if (is_empty()) return *this;
  25654. const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
  25655. Tfloat nsigma = (Tfloat)sigma, m = 0, M = 0;
  25656. if (nsigma==0 && noise_type!=3) return *this;
  25657. if (nsigma<0 || noise_type==2) m = (Tfloat)min_max(M);
  25658. if (nsigma<0) nsigma = (Tfloat)(-nsigma*(M-m)/100.0);
  25659. switch (noise_type) {
  25660. case 0 : { // Gaussian noise
  25661. cimg_rof(*this,ptrd,T) {
  25662. Tfloat val = (Tfloat)(*ptrd + nsigma*cimg::grand());
  25663. if (val>vmax) val = vmax;
  25664. if (val<vmin) val = vmin;
  25665. *ptrd = (T)val;
  25666. }
  25667. } break;
  25668. case 1 : { // Uniform noise
  25669. cimg_rof(*this,ptrd,T) {
  25670. Tfloat val = (Tfloat)(*ptrd + nsigma*cimg::rand(-1,1));
  25671. if (val>vmax) val = vmax;
  25672. if (val<vmin) val = vmin;
  25673. *ptrd = (T)val;
  25674. }
  25675. } break;
  25676. case 2 : { // Salt & Pepper noise
  25677. if (nsigma<0) nsigma = -nsigma;
  25678. if (M==m) { m = 0; M = cimg::type<T>::is_float()?(Tfloat)1:(Tfloat)cimg::type<T>::max(); }
  25679. cimg_rof(*this,ptrd,T) if (cimg::rand(100)<nsigma) *ptrd = (T)(cimg::rand()<0.5?M:m);
  25680. } break;
  25681. case 3 : { // Poisson Noise
  25682. cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::prand(*ptrd);
  25683. } break;
  25684. case 4 : { // Rice noise
  25685. const Tfloat sqrt2 = (Tfloat)std::sqrt(2.0);
  25686. cimg_rof(*this,ptrd,T) {
  25687. const Tfloat
  25688. val0 = (Tfloat)*ptrd/sqrt2,
  25689. re = (Tfloat)(val0 + nsigma*cimg::grand()),
  25690. im = (Tfloat)(val0 + nsigma*cimg::grand());
  25691. Tfloat val = cimg::hypot(re,im);
  25692. if (val>vmax) val = vmax;
  25693. if (val<vmin) val = vmin;
  25694. *ptrd = (T)val;
  25695. }
  25696. } break;
  25697. default :
  25698. throw CImgArgumentException(_cimg_instance
  25699. "noise(): Invalid specified noise type %d "
  25700. "(should be { 0=gaussian | 1=uniform | 2=salt&Pepper | 3=poisson }).",
  25701. cimg_instance,
  25702. noise_type);
  25703. }
  25704. return *this;
  25705. }
  25706. //! Add random noise to pixel values \newinstance.
  25707. CImg<T> get_noise(const double sigma, const unsigned int noise_type=0) const {
  25708. return (+*this).noise(sigma,noise_type);
  25709. }
  25710. //! Linearly normalize pixel values.
  25711. /**
  25712. \param min_value Minimum desired value of the resulting image.
  25713. \param max_value Maximum desired value of the resulting image.
  25714. \par Example
  25715. \code
  25716. const CImg<float> img("reference.jpg"), res = img.get_normalize(160,220);
  25717. (img,res).display();
  25718. \endcode
  25719. \image html ref_normalize2.jpg
  25720. **/
  25721. CImg<T>& normalize(const T& min_value, const T& max_value) {
  25722. if (is_empty()) return *this;
  25723. const T a = min_value<max_value?min_value:max_value, b = min_value<max_value?max_value:min_value;
  25724. T m, M = max_min(m);
  25725. const Tfloat fm = (Tfloat)m, fM = (Tfloat)M;
  25726. if (m==M) return fill(min_value);
  25727. if (m!=a || M!=b) cimg_rof(*this,ptrd,T) *ptrd = (T)((*ptrd - fm)/(fM - fm)*(b - a) + a);
  25728. return *this;
  25729. }
  25730. //! Linearly normalize pixel values \newinstance.
  25731. CImg<Tfloat> get_normalize(const T& min_value, const T& max_value) const {
  25732. return CImg<Tfloat>(*this,false).normalize((Tfloat)min_value,(Tfloat)max_value);
  25733. }
  25734. //! Normalize multi-valued pixels of the image instance, with respect to their L2-norm.
  25735. /**
  25736. \par Example
  25737. \code
  25738. const CImg<float> img("reference.jpg"), res = img.get_normalize();
  25739. (img,res.normalize(0,255)).display();
  25740. \endcode
  25741. \image html ref_normalize.jpg
  25742. **/
  25743. CImg<T>& normalize() {
  25744. const ulongT whd = (ulongT)_width*_height*_depth;
  25745. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=512 && _height*_depth>=16))
  25746. cimg_forYZ(*this,y,z) {
  25747. T *ptrd = data(0,y,z,0);
  25748. cimg_forX(*this,x) {
  25749. const T *ptrs = ptrd;
  25750. float n = 0;
  25751. cimg_forC(*this,c) { n+=cimg::sqr((float)*ptrs); ptrs+=whd; }
  25752. n = (float)std::sqrt(n);
  25753. T *_ptrd = ptrd++;
  25754. if (n>0) cimg_forC(*this,c) { *_ptrd = (T)(*_ptrd/n); _ptrd+=whd; }
  25755. else cimg_forC(*this,c) { *_ptrd = (T)0; _ptrd+=whd; }
  25756. }
  25757. }
  25758. return *this;
  25759. }
  25760. //! Normalize multi-valued pixels of the image instance, with respect to their L2-norm \newinstance.
  25761. CImg<Tfloat> get_normalize() const {
  25762. return CImg<Tfloat>(*this,false).normalize();
  25763. }
  25764. //! Compute Lp-norm of each multi-valued pixel of the image instance.
  25765. /**
  25766. \param norm_type Type of computed vector norm (can be \p -1=Linf, or \p greater or equal than 0).
  25767. \par Example
  25768. \code
  25769. const CImg<float> img("reference.jpg"), res = img.get_norm();
  25770. (img,res.normalize(0,255)).display();
  25771. \endcode
  25772. \image html ref_norm.jpg
  25773. **/
  25774. CImg<T>& norm(const int norm_type=2) {
  25775. if (_spectrum==1 && norm_type) return abs();
  25776. return get_norm(norm_type).move_to(*this);
  25777. }
  25778. //! Compute L2-norm of each multi-valued pixel of the image instance \newinstance.
  25779. CImg<Tfloat> get_norm(const int norm_type=2) const {
  25780. if (is_empty()) return *this;
  25781. if (_spectrum==1 && norm_type) return get_abs();
  25782. const ulongT whd = (ulongT)_width*_height*_depth;
  25783. CImg<Tfloat> res(_width,_height,_depth);
  25784. switch (norm_type) {
  25785. case -1 : { // Linf-norm.
  25786. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=512 && _height*_depth>=16))
  25787. cimg_forYZ(*this,y,z) {
  25788. const ulongT off = (ulongT)offset(0,y,z);
  25789. const T *ptrs = _data + off;
  25790. Tfloat *ptrd = res._data + off;
  25791. cimg_forX(*this,x) {
  25792. Tfloat n = 0;
  25793. const T *_ptrs = ptrs++;
  25794. cimg_forC(*this,c) { const Tfloat val = (Tfloat)cimg::abs(*_ptrs); if (val>n) n = val; _ptrs+=whd; }
  25795. *(ptrd++) = n;
  25796. }
  25797. }
  25798. } break;
  25799. case 0 : { // L0-norm.
  25800. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=512 && _height*_depth>=16))
  25801. cimg_forYZ(*this,y,z) {
  25802. const ulongT off = (ulongT)offset(0,y,z);
  25803. const T *ptrs = _data + off;
  25804. Tfloat *ptrd = res._data + off;
  25805. cimg_forX(*this,x) {
  25806. unsigned int n = 0;
  25807. const T *_ptrs = ptrs++;
  25808. cimg_forC(*this,c) { n+=*_ptrs==0?0:1; _ptrs+=whd; }
  25809. *(ptrd++) = (Tfloat)n;
  25810. }
  25811. }
  25812. } break;
  25813. case 1 : { // L1-norm.
  25814. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=512 && _height*_depth>=16))
  25815. cimg_forYZ(*this,y,z) {
  25816. const ulongT off = (ulongT)offset(0,y,z);
  25817. const T *ptrs = _data + off;
  25818. Tfloat *ptrd = res._data + off;
  25819. cimg_forX(*this,x) {
  25820. Tfloat n = 0;
  25821. const T *_ptrs = ptrs++;
  25822. cimg_forC(*this,c) { n+=cimg::abs(*_ptrs); _ptrs+=whd; }
  25823. *(ptrd++) = n;
  25824. }
  25825. }
  25826. } break;
  25827. case 2 : { // L2-norm.
  25828. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=512 && _height*_depth>=16))
  25829. cimg_forYZ(*this,y,z) {
  25830. const ulongT off = (ulongT)offset(0,y,z);
  25831. const T *ptrs = _data + off;
  25832. Tfloat *ptrd = res._data + off;
  25833. cimg_forX(*this,x) {
  25834. Tfloat n = 0;
  25835. const T *_ptrs = ptrs++;
  25836. cimg_forC(*this,c) { n+=cimg::sqr((Tfloat)*_ptrs); _ptrs+=whd; }
  25837. *(ptrd++) = (Tfloat)std::sqrt((Tfloat)n);
  25838. }
  25839. }
  25840. } break;
  25841. default : { // Linf-norm.
  25842. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=512 && _height*_depth>=16))
  25843. cimg_forYZ(*this,y,z) {
  25844. const ulongT off = (ulongT)offset(0,y,z);
  25845. const T *ptrs = _data + off;
  25846. Tfloat *ptrd = res._data + off;
  25847. cimg_forX(*this,x) {
  25848. Tfloat n = 0;
  25849. const T *_ptrs = ptrs++;
  25850. cimg_forC(*this,c) { n+=std::pow(cimg::abs((Tfloat)*_ptrs),(Tfloat)norm_type); _ptrs+=whd; }
  25851. *(ptrd++) = (Tfloat)std::pow((Tfloat)n,1/(Tfloat)norm_type);
  25852. }
  25853. }
  25854. }
  25855. }
  25856. return res;
  25857. }
  25858. //! Cut pixel values in specified range.
  25859. /**
  25860. \param min_value Minimum desired value of the resulting image.
  25861. \param max_value Maximum desired value of the resulting image.
  25862. \par Example
  25863. \code
  25864. const CImg<float> img("reference.jpg"), res = img.get_cut(160,220);
  25865. (img,res).display();
  25866. \endcode
  25867. \image html ref_cut.jpg
  25868. **/
  25869. CImg<T>& cut(const T& min_value, const T& max_value) {
  25870. if (is_empty()) return *this;
  25871. const T a = min_value<max_value?min_value:max_value, b = min_value<max_value?max_value:min_value;
  25872. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
  25873. cimg_rof(*this,ptrd,T) *ptrd = (*ptrd<a)?a:((*ptrd>b)?b:*ptrd);
  25874. return *this;
  25875. }
  25876. //! Cut pixel values in specified range \newinstance.
  25877. CImg<T> get_cut(const T& min_value, const T& max_value) const {
  25878. return (+*this).cut(min_value,max_value);
  25879. }
  25880. //! Uniformly quantize pixel values.
  25881. /**
  25882. \param nb_levels Number of quantization levels.
  25883. \param keep_range Tells if resulting values keep the same range as the original ones.
  25884. \par Example
  25885. \code
  25886. const CImg<float> img("reference.jpg"), res = img.get_quantize(4);
  25887. (img,res).display();
  25888. \endcode
  25889. \image html ref_quantize.jpg
  25890. **/
  25891. CImg<T>& quantize(const unsigned int nb_levels, const bool keep_range=true) {
  25892. if (!nb_levels)
  25893. throw CImgArgumentException(_cimg_instance
  25894. "quantize(): Invalid quantization request with 0 values.",
  25895. cimg_instance);
  25896. if (is_empty()) return *this;
  25897. Tfloat m, M = (Tfloat)max_min(m), range = M - m;
  25898. if (range>0) {
  25899. if (keep_range)
  25900. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
  25901. cimg_rof(*this,ptrd,T) {
  25902. const unsigned int val = (unsigned int)((*ptrd-m)*nb_levels/range);
  25903. *ptrd = (T)(m + std::min(val,nb_levels - 1)*range/nb_levels);
  25904. } else
  25905. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
  25906. cimg_rof(*this,ptrd,T) {
  25907. const unsigned int val = (unsigned int)((*ptrd-m)*nb_levels/range);
  25908. *ptrd = (T)std::min(val,nb_levels - 1);
  25909. }
  25910. }
  25911. return *this;
  25912. }
  25913. //! Uniformly quantize pixel values \newinstance.
  25914. CImg<T> get_quantize(const unsigned int n, const bool keep_range=true) const {
  25915. return (+*this).quantize(n,keep_range);
  25916. }
  25917. //! Threshold pixel values.
  25918. /**
  25919. \param value Threshold value
  25920. \param soft_threshold Tells if soft thresholding must be applied (instead of hard one).
  25921. \param strict_threshold Tells if threshold value is strict.
  25922. \par Example
  25923. \code
  25924. const CImg<float> img("reference.jpg"), res = img.get_threshold(128);
  25925. (img,res.normalize(0,255)).display();
  25926. \endcode
  25927. \image html ref_threshold.jpg
  25928. **/
  25929. CImg<T>& threshold(const T& value, const bool soft_threshold=false, const bool strict_threshold=false) {
  25930. if (is_empty()) return *this;
  25931. if (strict_threshold) {
  25932. if (soft_threshold)
  25933. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
  25934. cimg_rof(*this,ptrd,T) {
  25935. const T v = *ptrd;
  25936. *ptrd = v>value?(T)(v-value):v<-(float)value?(T)(v + value):(T)0;
  25937. }
  25938. else
  25939. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=65536))
  25940. cimg_rof(*this,ptrd,T) *ptrd = *ptrd>value?(T)1:(T)0;
  25941. } else {
  25942. if (soft_threshold)
  25943. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
  25944. cimg_rof(*this,ptrd,T) {
  25945. const T v = *ptrd;
  25946. *ptrd = v>=value?(T)(v-value):v<=-(float)value?(T)(v + value):(T)0;
  25947. }
  25948. else
  25949. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=65536))
  25950. cimg_rof(*this,ptrd,T) *ptrd = *ptrd>=value?(T)1:(T)0;
  25951. }
  25952. return *this;
  25953. }
  25954. //! Threshold pixel values \newinstance.
  25955. CImg<T> get_threshold(const T& value, const bool soft_threshold=false, const bool strict_threshold=false) const {
  25956. return (+*this).threshold(value,soft_threshold,strict_threshold);
  25957. }
  25958. //! Compute the histogram of pixel values.
  25959. /**
  25960. \param nb_levels Number of desired histogram levels.
  25961. \param min_value Minimum pixel value considered for the histogram computation.
  25962. All pixel values lower than \p min_value will not be counted.
  25963. \param max_value Maximum pixel value considered for the histogram computation.
  25964. All pixel values higher than \p max_value will not be counted.
  25965. \note
  25966. - The histogram H of an image I is the 1d function where H(x) counts the number of occurences of the value x
  25967. in the image I.
  25968. - The resulting histogram is always defined in 1d. Histograms of multi-valued images are not multi-dimensional.
  25969. \par Example
  25970. \code
  25971. const CImg<float> img = CImg<float>("reference.jpg").histogram(256);
  25972. img.display_graph(0,3);
  25973. \endcode
  25974. \image html ref_histogram.jpg
  25975. **/
  25976. CImg<T>& histogram(const unsigned int nb_levels, const T& min_value, const T& max_value) {
  25977. return get_histogram(nb_levels,min_value,max_value).move_to(*this);
  25978. }
  25979. //! Compute the histogram of pixel values \overloading.
  25980. CImg<T>& histogram(const unsigned int nb_levels) {
  25981. return get_histogram(nb_levels).move_to(*this);
  25982. }
  25983. //! Compute the histogram of pixel values \newinstance.
  25984. CImg<ulongT> get_histogram(const unsigned int nb_levels, const T& min_value, const T& max_value) const {
  25985. if (!nb_levels || is_empty()) return CImg<ulongT>();
  25986. const double
  25987. vmin = (double)(min_value<max_value?min_value:max_value),
  25988. vmax = (double)(min_value<max_value?max_value:min_value);
  25989. CImg<ulongT> res(nb_levels,1,1,1,0);
  25990. cimg_rof(*this,ptrs,T) {
  25991. const T val = *ptrs;
  25992. if (val>=vmin && val<=vmax) ++res[val==vmax?nb_levels - 1:(unsigned int)((val - vmin)*nb_levels/(vmax - vmin))];
  25993. }
  25994. return res;
  25995. }
  25996. //! Compute the histogram of pixel values \newinstance.
  25997. CImg<ulongT> get_histogram(const unsigned int nb_levels) const {
  25998. if (!nb_levels || is_empty()) return CImg<ulongT>();
  25999. T vmax = 0, vmin = min_max(vmax);
  26000. return get_histogram(nb_levels,vmin,vmax);
  26001. }
  26002. //! Equalize histogram of pixel values.
  26003. /**
  26004. \param nb_levels Number of histogram levels used for the equalization.
  26005. \param min_value Minimum pixel value considered for the histogram computation.
  26006. All pixel values lower than \p min_value will not be counted.
  26007. \param max_value Maximum pixel value considered for the histogram computation.
  26008. All pixel values higher than \p max_value will not be counted.
  26009. \par Example
  26010. \code
  26011. const CImg<float> img("reference.jpg"), res = img.get_equalize(256);
  26012. (img,res).display();
  26013. \endcode
  26014. \image html ref_equalize.jpg
  26015. **/
  26016. CImg<T>& equalize(const unsigned int nb_levels, const T& min_value, const T& max_value) {
  26017. if (!nb_levels || is_empty()) return *this;
  26018. const T
  26019. vmin = min_value<max_value?min_value:max_value,
  26020. vmax = min_value<max_value?max_value:min_value;
  26021. CImg<ulongT> hist = get_histogram(nb_levels,vmin,vmax);
  26022. ulongT cumul = 0;
  26023. cimg_forX(hist,pos) { cumul+=hist[pos]; hist[pos] = cumul; }
  26024. if (!cumul) cumul = 1;
  26025. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=1048576))
  26026. cimg_rof(*this,ptrd,T) {
  26027. const int pos = (int)((*ptrd-vmin)*(nb_levels - 1.)/(vmax-vmin));
  26028. if (pos>=0 && pos<(int)nb_levels) *ptrd = (T)(vmin + (vmax-vmin)*hist[pos]/cumul);
  26029. }
  26030. return *this;
  26031. }
  26032. //! Equalize histogram of pixel values \overloading.
  26033. CImg<T>& equalize(const unsigned int nb_levels) {
  26034. if (!nb_levels || is_empty()) return *this;
  26035. T vmax = 0, vmin = min_max(vmax);
  26036. return equalize(nb_levels,vmin,vmax);
  26037. }
  26038. //! Equalize histogram of pixel values \newinstance.
  26039. CImg<T> get_equalize(const unsigned int nblevels, const T& val_min, const T& val_max) const {
  26040. return (+*this).equalize(nblevels,val_min,val_max);
  26041. }
  26042. //! Equalize histogram of pixel values \newinstance.
  26043. CImg<T> get_equalize(const unsigned int nblevels) const {
  26044. return (+*this).equalize(nblevels);
  26045. }
  26046. //! Index multi-valued pixels regarding to a specified colormap.
  26047. /**
  26048. \param colormap Multi-valued colormap used as the basis for multi-valued pixel indexing.
  26049. \param dithering Level of dithering (0=disable, 1=standard level).
  26050. \param map_indexes Tell if the values of the resulting image are the colormap indices or the colormap vectors.
  26051. \note
  26052. - \p img.index(colormap,dithering,1) is equivalent to <tt>img.index(colormap,dithering,0).map(colormap)</tt>.
  26053. \par Example
  26054. \code
  26055. const CImg<float> img("reference.jpg"), colormap(3,1,1,3, 0,128,255, 0,128,255, 0,128,255);
  26056. const CImg<float> res = img.get_index(colormap,1,true);
  26057. (img,res).display();
  26058. \endcode
  26059. \image html ref_index.jpg
  26060. **/
  26061. template<typename t>
  26062. CImg<T>& index(const CImg<t>& colormap, const float dithering=1, const bool map_indexes=false) {
  26063. return get_index(colormap,dithering,map_indexes).move_to(*this);
  26064. }
  26065. //! Index multi-valued pixels regarding to a specified colormap \newinstance.
  26066. template<typename t>
  26067. CImg<typename CImg<t>::Tuint>
  26068. get_index(const CImg<t>& colormap, const float dithering=1, const bool map_indexes=true) const {
  26069. if (colormap._spectrum!=_spectrum)
  26070. throw CImgArgumentException(_cimg_instance
  26071. "index(): Instance and specified colormap (%u,%u,%u,%u,%p) "
  26072. "have incompatible dimensions.",
  26073. cimg_instance,
  26074. colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data);
  26075. typedef typename CImg<t>::Tuint tuint;
  26076. if (is_empty()) return CImg<tuint>();
  26077. const ulongT
  26078. whd = (ulongT)_width*_height*_depth,
  26079. pwhd = (ulongT)colormap._width*colormap._height*colormap._depth;
  26080. CImg<tuint> res(_width,_height,_depth,map_indexes?_spectrum:1);
  26081. tuint *ptrd = res._data;
  26082. if (dithering>0) { // Dithered versions.
  26083. const float ndithering = cimg::cut(dithering,0,1)/16;
  26084. Tfloat valm = 0, valM = (Tfloat)max_min(valm);
  26085. if (valm==valM && valm>=0 && valM<=255) { valm = 0; valM = 255; }
  26086. CImg<Tfloat> cache = get_crop(-1,0,0,0,_width,1,0,_spectrum - 1);
  26087. Tfloat *cache_current = cache.data(1,0,0,0), *cache_next = cache.data(1,1,0,0);
  26088. const ulongT cwhd = (ulongT)cache._width*cache._height*cache._depth;
  26089. switch (_spectrum) {
  26090. case 1 : { // Optimized for scalars.
  26091. cimg_forYZ(*this,y,z) {
  26092. if (y<height() - 2) {
  26093. Tfloat *ptrc0 = cache_next; const T *ptrs0 = data(0,y + 1,z,0);
  26094. cimg_forX(*this,x) *(ptrc0++) = (Tfloat)*(ptrs0++);
  26095. }
  26096. Tfloat *ptrs0 = cache_current, *ptrsn0 = cache_next;
  26097. cimg_forX(*this,x) {
  26098. const Tfloat _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0;
  26099. Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
  26100. for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0<ptrp_end; ) {
  26101. const Tfloat pval0 = (Tfloat)*(ptrp0++) - val0, dist = pval0*pval0;
  26102. if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
  26103. }
  26104. const Tfloat err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering;
  26105. *ptrs0+=7*err0; *(ptrsn0 - 1)+=3*err0; *(ptrsn0++)+=5*err0; *ptrsn0+=err0;
  26106. if (map_indexes) *(ptrd++) = (tuint)*ptrmin0; else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
  26107. }
  26108. cimg::swap(cache_current,cache_next);
  26109. }
  26110. } break;
  26111. case 2 : { // Optimized for 2d vectors.
  26112. tuint *ptrd1 = ptrd + whd;
  26113. cimg_forYZ(*this,y,z) {
  26114. if (y<height() - 2) {
  26115. Tfloat *ptrc0 = cache_next, *ptrc1 = ptrc0 + cwhd;
  26116. const T *ptrs0 = data(0,y + 1,z,0), *ptrs1 = ptrs0 + whd;
  26117. cimg_forX(*this,x) { *(ptrc0++) = (Tfloat)*(ptrs0++); *(ptrc1++) = (Tfloat)*(ptrs1++); }
  26118. }
  26119. Tfloat
  26120. *ptrs0 = cache_current, *ptrs1 = ptrs0 + cwhd,
  26121. *ptrsn0 = cache_next, *ptrsn1 = ptrsn0 + cwhd;
  26122. cimg_forX(*this,x) {
  26123. const Tfloat
  26124. _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0,
  26125. _val1 = (Tfloat)*ptrs1, val1 = _val1<valm?valm:_val1>valM?valM:_val1;
  26126. Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
  26127. for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
  26128. const Tfloat
  26129. pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1,
  26130. dist = pval0*pval0 + pval1*pval1;
  26131. if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
  26132. }
  26133. const t *const ptrmin1 = ptrmin0 + pwhd;
  26134. const Tfloat
  26135. err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering,
  26136. err1 = ((*(ptrs1++)=val1) - (Tfloat)*ptrmin1)*ndithering;
  26137. *ptrs0+=7*err0; *ptrs1+=7*err1;
  26138. *(ptrsn0 - 1)+=3*err0; *(ptrsn1 - 1)+=3*err1;
  26139. *(ptrsn0++)+=5*err0; *(ptrsn1++)+=5*err1;
  26140. *ptrsn0+=err0; *ptrsn1+=err1;
  26141. if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*ptrmin1; }
  26142. else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
  26143. }
  26144. cimg::swap(cache_current,cache_next);
  26145. }
  26146. } break;
  26147. case 3 : { // Optimized for 3d vectors (colors).
  26148. tuint *ptrd1 = ptrd + whd, *ptrd2 = ptrd1 + whd;
  26149. cimg_forYZ(*this,y,z) {
  26150. if (y<height() - 2) {
  26151. Tfloat *ptrc0 = cache_next, *ptrc1 = ptrc0 + cwhd, *ptrc2 = ptrc1 + cwhd;
  26152. const T *ptrs0 = data(0,y + 1,z,0), *ptrs1 = ptrs0 + whd, *ptrs2 = ptrs1 + whd;
  26153. cimg_forX(*this,x) {
  26154. *(ptrc0++) = (Tfloat)*(ptrs0++); *(ptrc1++) = (Tfloat)*(ptrs1++); *(ptrc2++) = (Tfloat)*(ptrs2++);
  26155. }
  26156. }
  26157. Tfloat
  26158. *ptrs0 = cache_current, *ptrs1 = ptrs0 + cwhd, *ptrs2 = ptrs1 + cwhd,
  26159. *ptrsn0 = cache_next, *ptrsn1 = ptrsn0 + cwhd, *ptrsn2 = ptrsn1 + cwhd;
  26160. cimg_forX(*this,x) {
  26161. const Tfloat
  26162. _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0,
  26163. _val1 = (Tfloat)*ptrs1, val1 = _val1<valm?valm:_val1>valM?valM:_val1,
  26164. _val2 = (Tfloat)*ptrs2, val2 = _val2<valm?valm:_val2>valM?valM:_val2;
  26165. Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
  26166. for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd,
  26167. *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
  26168. const Tfloat
  26169. pval0 = (Tfloat)*(ptrp0++) - val0,
  26170. pval1 = (Tfloat)*(ptrp1++) - val1,
  26171. pval2 = (Tfloat)*(ptrp2++) - val2,
  26172. dist = pval0*pval0 + pval1*pval1 + pval2*pval2;
  26173. if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
  26174. }
  26175. const t *const ptrmin1 = ptrmin0 + pwhd, *const ptrmin2 = ptrmin1 + pwhd;
  26176. const Tfloat
  26177. err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering,
  26178. err1 = ((*(ptrs1++)=val1) - (Tfloat)*ptrmin1)*ndithering,
  26179. err2 = ((*(ptrs2++)=val2) - (Tfloat)*ptrmin2)*ndithering;
  26180. *ptrs0+=7*err0; *ptrs1+=7*err1; *ptrs2+=7*err2;
  26181. *(ptrsn0 - 1)+=3*err0; *(ptrsn1 - 1)+=3*err1; *(ptrsn2 - 1)+=3*err2;
  26182. *(ptrsn0++)+=5*err0; *(ptrsn1++)+=5*err1; *(ptrsn2++)+=5*err2;
  26183. *ptrsn0+=err0; *ptrsn1+=err1; *ptrsn2+=err2;
  26184. if (map_indexes) {
  26185. *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*ptrmin1; *(ptrd2++) = (tuint)*ptrmin2;
  26186. } else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
  26187. }
  26188. cimg::swap(cache_current,cache_next);
  26189. }
  26190. } break;
  26191. default : // Generic version
  26192. cimg_forYZ(*this,y,z) {
  26193. if (y<height() - 2) {
  26194. Tfloat *ptrc = cache_next;
  26195. cimg_forC(*this,c) {
  26196. Tfloat *_ptrc = ptrc; const T *_ptrs = data(0,y + 1,z,c);
  26197. cimg_forX(*this,x) *(_ptrc++) = (Tfloat)*(_ptrs++);
  26198. ptrc+=cwhd;
  26199. }
  26200. }
  26201. Tfloat *ptrs = cache_current, *ptrsn = cache_next;
  26202. cimg_forX(*this,x) {
  26203. Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin = colormap._data;
  26204. for (const t *ptrp = colormap._data, *ptrp_end = ptrp + pwhd; ptrp<ptrp_end; ++ptrp) {
  26205. Tfloat dist = 0; Tfloat *_ptrs = ptrs; const t *_ptrp = ptrp;
  26206. cimg_forC(*this,c) {
  26207. const Tfloat _val = *_ptrs, val = _val<valm?valm:_val>valM?valM:_val;
  26208. dist+=cimg::sqr((*_ptrs=val) - (Tfloat)*_ptrp); _ptrs+=cwhd; _ptrp+=pwhd;
  26209. }
  26210. if (dist<distmin) { ptrmin = ptrp; distmin = dist; }
  26211. }
  26212. const t *_ptrmin = ptrmin; Tfloat *_ptrs = ptrs++, *_ptrsn = (ptrsn++) - 1;
  26213. cimg_forC(*this,c) {
  26214. const Tfloat err = (*(_ptrs++) - (Tfloat)*_ptrmin)*ndithering;
  26215. *_ptrs+=7*err; *(_ptrsn++)+=3*err; *(_ptrsn++)+=5*err; *_ptrsn+=err;
  26216. _ptrmin+=pwhd; _ptrs+=cwhd - 1; _ptrsn+=cwhd - 2;
  26217. }
  26218. if (map_indexes) {
  26219. tuint *_ptrd = ptrd++;
  26220. cimg_forC(*this,c) { *_ptrd = (tuint)*ptrmin; _ptrd+=whd; ptrmin+=pwhd; }
  26221. }
  26222. else *(ptrd++) = (tuint)(ptrmin - colormap._data);
  26223. }
  26224. cimg::swap(cache_current,cache_next);
  26225. }
  26226. }
  26227. } else { // Non-dithered versions
  26228. switch (_spectrum) {
  26229. case 1 : { // Optimized for scalars.
  26230. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=64 && _height*_depth>=16 && pwhd>=16))
  26231. cimg_forYZ(*this,y,z) {
  26232. tuint *ptrd = res.data(0,y,z);
  26233. for (const T *ptrs0 = data(0,y,z), *ptrs_end = ptrs0 + _width; ptrs0<ptrs_end; ) {
  26234. const Tfloat val0 = (Tfloat)*(ptrs0++);
  26235. Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
  26236. for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0<ptrp_end; ) {
  26237. const Tfloat pval0 = (Tfloat)*(ptrp0++) - val0, dist = pval0*pval0;
  26238. if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
  26239. }
  26240. if (map_indexes) *(ptrd++) = (tuint)*ptrmin0; else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
  26241. }
  26242. }
  26243. } break;
  26244. case 2 : { // Optimized for 2d vectors.
  26245. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=64 && _height*_depth>=16 && pwhd>=16))
  26246. cimg_forYZ(*this,y,z) {
  26247. tuint *ptrd = res.data(0,y,z), *ptrd1 = ptrd + whd;
  26248. for (const T *ptrs0 = data(0,y,z), *ptrs1 = ptrs0 + whd, *ptrs_end = ptrs0 + _width; ptrs0<ptrs_end; ) {
  26249. const Tfloat val0 = (Tfloat)*(ptrs0++), val1 = (Tfloat)*(ptrs1++);
  26250. Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
  26251. for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
  26252. const Tfloat
  26253. pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1,
  26254. dist = pval0*pval0 + pval1*pval1;
  26255. if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
  26256. }
  26257. if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*(ptrmin0 + pwhd); }
  26258. else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
  26259. }
  26260. }
  26261. } break;
  26262. case 3 : { // Optimized for 3d vectors (colors).
  26263. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=64 && _height*_depth>=16 && pwhd>=16))
  26264. cimg_forYZ(*this,y,z) {
  26265. tuint *ptrd = res.data(0,y,z), *ptrd1 = ptrd + whd, *ptrd2 = ptrd1 + whd;
  26266. for (const T *ptrs0 = data(0,y,z), *ptrs1 = ptrs0 + whd, *ptrs2 = ptrs1 + whd,
  26267. *ptrs_end = ptrs0 + _width; ptrs0<ptrs_end; ) {
  26268. const Tfloat val0 = (Tfloat)*(ptrs0++), val1 = (Tfloat)*(ptrs1++), val2 = (Tfloat)*(ptrs2++);
  26269. Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
  26270. for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd,
  26271. *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
  26272. const Tfloat
  26273. pval0 = (Tfloat)*(ptrp0++) - val0,
  26274. pval1 = (Tfloat)*(ptrp1++) - val1,
  26275. pval2 = (Tfloat)*(ptrp2++) - val2,
  26276. dist = pval0*pval0 + pval1*pval1 + pval2*pval2;
  26277. if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
  26278. }
  26279. if (map_indexes) {
  26280. *(ptrd++) = (tuint)*ptrmin0;
  26281. *(ptrd1++) = (tuint)*(ptrmin0 + pwhd);
  26282. *(ptrd2++) = (tuint)*(ptrmin0 + 2*pwhd);
  26283. } else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
  26284. }
  26285. }
  26286. } break;
  26287. default : // Generic version.
  26288. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=64 && _height*_depth>=16 && pwhd>=16))
  26289. cimg_forYZ(*this,y,z) {
  26290. tuint *ptrd = res.data(0,y,z);
  26291. for (const T *ptrs = data(0,y,z), *ptrs_end = ptrs + _width; ptrs<ptrs_end; ++ptrs) {
  26292. Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin = colormap._data;
  26293. for (const t *ptrp = colormap._data, *ptrp_end = ptrp + pwhd; ptrp<ptrp_end; ++ptrp) {
  26294. Tfloat dist = 0; const T *_ptrs = ptrs; const t *_ptrp = ptrp;
  26295. cimg_forC(*this,c) { dist+=cimg::sqr((Tfloat)*_ptrs - (Tfloat)*_ptrp); _ptrs+=whd; _ptrp+=pwhd; }
  26296. if (dist<distmin) { ptrmin = ptrp; distmin = dist; }
  26297. }
  26298. if (map_indexes) {
  26299. tuint *_ptrd = ptrd++;
  26300. cimg_forC(*this,c) { *_ptrd = (tuint)*ptrmin; _ptrd+=whd; ptrmin+=pwhd; }
  26301. }
  26302. else *(ptrd++) = (tuint)(ptrmin - colormap._data);
  26303. }
  26304. }
  26305. }
  26306. }
  26307. return res;
  26308. }
  26309. //! Map predefined colormap on the scalar (indexed) image instance.
  26310. /**
  26311. \param colormap Multi-valued colormap used for mapping the indexes.
  26312. \param boundary_conditions The border condition type { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
  26313. \par Example
  26314. \code
  26315. const CImg<float> img("reference.jpg"),
  26316. colormap1(3,1,1,3, 0,128,255, 0,128,255, 0,128,255),
  26317. colormap2(3,1,1,3, 255,0,0, 0,255,0, 0,0,255),
  26318. res = img.get_index(colormap1,0).map(colormap2);
  26319. (img,res).display();
  26320. \endcode
  26321. \image html ref_map.jpg
  26322. **/
  26323. template<typename t>
  26324. CImg<T>& map(const CImg<t>& colormap, const unsigned int boundary_conditions=0) {
  26325. return get_map(colormap,boundary_conditions).move_to(*this);
  26326. }
  26327. //! Map predefined colormap on the scalar (indexed) image instance \newinstance.
  26328. template<typename t>
  26329. CImg<t> get_map(const CImg<t>& colormap, const unsigned int boundary_conditions=0) const {
  26330. if (_spectrum!=1 && colormap._spectrum!=1)
  26331. throw CImgArgumentException(_cimg_instance
  26332. "map(): Instance and specified colormap (%u,%u,%u,%u,%p) "
  26333. "have incompatible dimensions.",
  26334. cimg_instance,
  26335. colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data);
  26336. const ulongT
  26337. whd = (ulongT)_width*_height*_depth,
  26338. cwhd = (ulongT)colormap._width*colormap._height*colormap._depth,
  26339. cwhd2 = 2*cwhd;
  26340. CImg<t> res(_width,_height,_depth,colormap._spectrum==1?_spectrum:colormap._spectrum);
  26341. switch (colormap._spectrum) {
  26342. case 1 : { // Optimized for scalars
  26343. const T *ptrs = _data;
  26344. switch (boundary_conditions) {
  26345. case 3 : // Mirror
  26346. cimg_for(res,ptrd,t) {
  26347. const ulongT ind = ((ulongT)*(ptrs++))%cwhd2;
  26348. *ptrd = colormap[ind<cwhd?ind:cwhd2 - ind - 1];
  26349. }
  26350. break;
  26351. case 2 : // Periodic
  26352. cimg_for(res,ptrd,t) {
  26353. const ulongT ind = (ulongT)*(ptrs++);
  26354. *ptrd = colormap[ind%cwhd];
  26355. } break;
  26356. case 1 : // Neumann
  26357. cimg_for(res,ptrd,t) {
  26358. const longT ind = (longT)*(ptrs++);
  26359. *ptrd = colormap[cimg::cut(ind,(longT)0,(longT)cwhd - 1)];
  26360. } break;
  26361. default : // Dirichlet
  26362. cimg_for(res,ptrd,t) {
  26363. const ulongT ind = (ulongT)*(ptrs++);
  26364. *ptrd = ind<cwhd?colormap[ind]:(t)0;
  26365. }
  26366. }
  26367. } break;
  26368. case 2 : { // Optimized for 2d vectors.
  26369. const t *const ptrp0 = colormap._data, *ptrp1 = ptrp0 + cwhd;
  26370. t *ptrd0 = res._data, *ptrd1 = ptrd0 + whd;
  26371. switch (boundary_conditions) {
  26372. case 3 : // Mirror
  26373. for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
  26374. const ulongT
  26375. _ind = ((ulongT)*(ptrs++))%cwhd2,
  26376. ind = _ind<cwhd?_ind:cwhd2 - _ind - 1;
  26377. *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind];
  26378. }
  26379. break;
  26380. case 2 : // Periodic
  26381. for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
  26382. const ulongT ind = ((ulongT)*(ptrs++))%cwhd;
  26383. *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind];
  26384. }
  26385. break;
  26386. case 1 : // Neumann
  26387. for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
  26388. const longT ind = cimg::cut((longT)*(ptrs++),(longT)0,(longT)cwhd - 1);
  26389. *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind];
  26390. }
  26391. break;
  26392. default : // Dirichlet
  26393. for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
  26394. const ulongT ind = (ulongT)*(ptrs++);
  26395. const bool is_in = ind<cwhd;
  26396. *(ptrd0++) = is_in?ptrp0[ind]:(t)0; *(ptrd1++) = is_in?ptrp1[ind]:(t)0;
  26397. }
  26398. }
  26399. } break;
  26400. case 3 : { // Optimized for 3d vectors (colors).
  26401. const t *const ptrp0 = colormap._data, *ptrp1 = ptrp0 + cwhd, *ptrp2 = ptrp1 + cwhd;
  26402. t *ptrd0 = res._data, *ptrd1 = ptrd0 + whd, *ptrd2 = ptrd1 + whd;
  26403. switch (boundary_conditions) {
  26404. case 3 : // Mirror
  26405. for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
  26406. const ulongT
  26407. _ind = ((ulongT)*(ptrs++))%cwhd2,
  26408. ind = _ind<cwhd?_ind:cwhd2 - _ind - 1;
  26409. *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind]; *(ptrd2++) = ptrp2[ind];
  26410. } break;
  26411. case 2 : // Periodic
  26412. for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
  26413. const ulongT ind = ((ulongT)*(ptrs++))%cwhd;
  26414. *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind]; *(ptrd2++) = ptrp2[ind];
  26415. } break;
  26416. case 1 : // Neumann
  26417. for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
  26418. const longT ind = cimg::cut((longT)*(ptrs++),(longT)0,(longT)cwhd - 1);
  26419. *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind]; *(ptrd2++) = ptrp2[ind];
  26420. } break;
  26421. default : // Dirichlet
  26422. for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
  26423. const ulongT ind = (ulongT)*(ptrs++);
  26424. const bool is_in = ind<cwhd;
  26425. *(ptrd0++) = is_in?ptrp0[ind]:(t)0; *(ptrd1++) = is_in?ptrp1[ind]:(t)0; *(ptrd2++) = is_in?ptrp2[ind]:(t)0;
  26426. }
  26427. }
  26428. } break;
  26429. default : { // Generic version.
  26430. t *ptrd = res._data;
  26431. switch (boundary_conditions) {
  26432. case 3 : // Mirror
  26433. for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
  26434. const ulongT
  26435. _ind = ((ulongT)*(ptrs++))%cwhd,
  26436. ind = _ind<cwhd?_ind:cwhd2 - _ind - 1;
  26437. const t *ptrp = colormap._data + ind;
  26438. t *_ptrd = ptrd++; cimg_forC(res,c) { *_ptrd = *ptrp; _ptrd+=whd; ptrp+=cwhd; }
  26439. } break;
  26440. case 2 : // Periodic
  26441. for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
  26442. const ulongT ind = ((ulongT)*(ptrs++))%cwhd;
  26443. const t *ptrp = colormap._data + ind;
  26444. t *_ptrd = ptrd++; cimg_forC(res,c) { *_ptrd = *ptrp; _ptrd+=whd; ptrp+=cwhd; }
  26445. } break;
  26446. case 1 : // Neumann
  26447. for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
  26448. const longT ind = cimg::cut((longT)*(ptrs++),(longT)0,(longT)cwhd - 1);
  26449. const t *ptrp = colormap._data + ind;
  26450. t *_ptrd = ptrd++; cimg_forC(res,c) { *_ptrd = *ptrp; _ptrd+=whd; ptrp+=cwhd; }
  26451. } break;
  26452. default : // Dirichlet
  26453. for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
  26454. const ulongT ind = (ulongT)*(ptrs++);
  26455. const bool is_in = ind<cwhd;
  26456. if (is_in) {
  26457. const t *ptrp = colormap._data + ind;
  26458. t *_ptrd = ptrd++; cimg_forC(res,c) { *_ptrd = *ptrp; _ptrd+=whd; ptrp+=cwhd; }
  26459. } else {
  26460. t *_ptrd = ptrd++; cimg_forC(res,c) { *_ptrd = (t)0; _ptrd+=whd; }
  26461. }
  26462. }
  26463. }
  26464. }
  26465. }
  26466. return res;
  26467. }
  26468. //! Label connected components.
  26469. /**
  26470. \param is_high_connectivity Boolean that choose between 4(false)- or 8(true)-connectivity
  26471. in 2d case, and between 6(false)- or 26(true)-connectivity in 3d case.
  26472. \param tolerance Tolerance used to determine if two neighboring pixels belong to the same region.
  26473. \note The algorithm of connected components computation has been primarily done
  26474. by A. Meijster, according to the publication:
  26475. 'W.H. Hesselink, A. Meijster, C. Bron, "Concurrent Determination of Connected Components.",
  26476. In: Science of Computer Programming 41 (2001), pp. 173--194'.
  26477. The submitted code has then been modified to fit CImg coding style and constraints.
  26478. **/
  26479. CImg<T>& label(const bool is_high_connectivity=false, const Tfloat tolerance=0) {
  26480. return get_label(is_high_connectivity,tolerance).move_to(*this);
  26481. }
  26482. //! Label connected components \newinstance.
  26483. CImg<ulongT> get_label(const bool is_high_connectivity=false,
  26484. const Tfloat tolerance=0) const {
  26485. if (is_empty()) return CImg<ulongT>();
  26486. // Create neighborhood tables.
  26487. int dx[13], dy[13], dz[13], nb = 0;
  26488. dx[nb] = 1; dy[nb] = 0; dz[nb++] = 0;
  26489. dx[nb] = 0; dy[nb] = 1; dz[nb++] = 0;
  26490. if (is_high_connectivity) {
  26491. dx[nb] = 1; dy[nb] = 1; dz[nb++] = 0;
  26492. dx[nb] = 1; dy[nb] = -1; dz[nb++] = 0;
  26493. }
  26494. if (_depth>1) { // 3d version.
  26495. dx[nb] = 0; dy[nb] = 0; dz[nb++]=1;
  26496. if (is_high_connectivity) {
  26497. dx[nb] = 1; dy[nb] = 1; dz[nb++] = -1;
  26498. dx[nb] = 1; dy[nb] = 0; dz[nb++] = -1;
  26499. dx[nb] = 1; dy[nb] = -1; dz[nb++] = -1;
  26500. dx[nb] = 0; dy[nb] = 1; dz[nb++] = -1;
  26501. dx[nb] = 0; dy[nb] = 1; dz[nb++] = 1;
  26502. dx[nb] = 1; dy[nb] = -1; dz[nb++] = 1;
  26503. dx[nb] = 1; dy[nb] = 0; dz[nb++] = 1;
  26504. dx[nb] = 1; dy[nb] = 1; dz[nb++] = 1;
  26505. }
  26506. }
  26507. return _label(nb,dx,dy,dz,tolerance);
  26508. }
  26509. //! Label connected components \overloading.
  26510. /**
  26511. \param connectivity_mask Mask of the neighboring pixels.
  26512. \param tolerance Tolerance used to determine if two neighboring pixels belong to the same region.
  26513. **/
  26514. template<typename t>
  26515. CImg<T>& label(const CImg<t>& connectivity_mask, const Tfloat tolerance=0) {
  26516. return get_label(connectivity_mask,tolerance).move_to(*this);
  26517. }
  26518. //! Label connected components \newinstance.
  26519. template<typename t>
  26520. CImg<ulongT> get_label(const CImg<t>& connectivity_mask,
  26521. const Tfloat tolerance=0) const {
  26522. int nb = 0;
  26523. cimg_for(connectivity_mask,ptr,t) if (*ptr) ++nb;
  26524. CImg<intT> dx(nb,1,1,1,0), dy(nb,1,1,1,0), dz(nb,1,1,1,0);
  26525. nb = 0;
  26526. cimg_forXYZ(connectivity_mask,x,y,z) if ((x || y || z) &&
  26527. connectivity_mask(x,y,z)) {
  26528. dx[nb] = x; dy[nb] = y; dz[nb++] = z;
  26529. }
  26530. return _label(nb,dx,dy,dz,tolerance);
  26531. }
  26532. CImg<ulongT> _label(const unsigned int nb, const int *const dx,
  26533. const int *const dy, const int *const dz,
  26534. const Tfloat tolerance) const {
  26535. CImg<ulongT> res(_width,_height,_depth,_spectrum);
  26536. cimg_forC(*this,c) {
  26537. CImg<ulongT> _res = res.get_shared_channel(c);
  26538. // Init label numbers.
  26539. ulongT *ptr = _res.data();
  26540. cimg_foroff(_res,p) *(ptr++) = p;
  26541. // For each neighbour-direction, label.
  26542. for (unsigned int n = 0; n<nb; ++n) {
  26543. const int _dx = dx[n], _dy = dy[n], _dz = dz[n];
  26544. if (_dx || _dy || _dz) {
  26545. const int
  26546. x0 = _dx<0?-_dx:0,
  26547. x1 = _dx<0?width():width() - _dx,
  26548. y0 = _dy<0?-_dy:0,
  26549. y1 = _dy<0?height():height() - _dy,
  26550. z0 = _dz<0?-_dz:0,
  26551. z1 = _dz<0?depth():depth() - _dz;
  26552. const longT
  26553. wh = (longT)width()*height(),
  26554. whd = (longT)width()*height()*depth(),
  26555. offset = _dz*wh + _dy*width() + _dx;
  26556. for (longT z = z0, nz = z0 + _dz, pz = z0*wh; z<z1; ++z, ++nz, pz+=wh) {
  26557. for (longT y = y0, ny = y0 + _dy, py = y0*width() + pz; y<y1; ++y, ++ny, py+=width()) {
  26558. for (longT x = x0, nx = x0 + _dx, p = x0 + py; x<x1; ++x, ++nx, ++p) {
  26559. if (cimg::abs((Tfloat)(*this)(x,y,z,c,wh,whd) - (Tfloat)(*this)(nx,ny,nz,c,wh,whd))<=tolerance) {
  26560. const longT q = p + offset;
  26561. ulongT x, y;
  26562. for (x = (ulongT)(p<q?q:p), y = (ulongT)(p<q?p:q); x!=y && _res[x]!=x; ) {
  26563. x = _res[x]; if (x<y) cimg::swap(x,y);
  26564. }
  26565. if (x!=y) _res[x] = (ulongT)y;
  26566. for (ulongT _p = (ulongT)p; _p!=y; ) {
  26567. const ulongT h = _res[_p];
  26568. _res[_p] = (ulongT)y;
  26569. _p = h;
  26570. }
  26571. for (ulongT _q = (ulongT)q; _q!=y; ) {
  26572. const ulongT h = _res[_q];
  26573. _res[_q] = (ulongT)y;
  26574. _q = h;
  26575. }
  26576. }
  26577. }
  26578. }
  26579. }
  26580. }
  26581. }
  26582. // Resolve equivalences.
  26583. ulongT counter = 0;
  26584. ptr = _res.data();
  26585. cimg_foroff(_res,p) { *ptr = *ptr==p?counter++:_res[*ptr]; ++ptr; }
  26586. }
  26587. return res;
  26588. }
  26589. // [internal] Replace possibly malicious characters for commands to be called by system() by their escaped version.
  26590. CImg<T>& _system_strescape() {
  26591. #define cimg_system_strescape(c,s) case c : if (p!=ptrs) CImg<T>(ptrs,(unsigned int)(p-ptrs),1,1,1,false).\
  26592. move_to(list); \
  26593. CImg<T>(s,(unsigned int)std::strlen(s),1,1,1,false).move_to(list); ptrs = p + 1; break
  26594. CImgList<T> list;
  26595. const T *ptrs = _data;
  26596. cimg_for(*this,p,T) switch ((int)*p) {
  26597. cimg_system_strescape('\\',"\\\\");
  26598. cimg_system_strescape('\"',"\\\"");
  26599. cimg_system_strescape('!',"\"\\!\"");
  26600. cimg_system_strescape('`',"\\`");
  26601. cimg_system_strescape('$',"\\$");
  26602. }
  26603. if (ptrs<end()) CImg<T>(ptrs,(unsigned int)(end()-ptrs),1,1,1,false).move_to(list);
  26604. return (list>'x').move_to(*this);
  26605. }
  26606. //@}
  26607. //---------------------------------
  26608. //
  26609. //! \name Color Base Management
  26610. //@{
  26611. //---------------------------------
  26612. //! Return colormap \e "default", containing 256 colors entries in RGB.
  26613. /**
  26614. \return The following \c 256x1x1x3 colormap is returned:
  26615. \image html ref_colormap_default.jpg
  26616. **/
  26617. static const CImg<Tuchar>& default_LUT256() {
  26618. static CImg<Tuchar> colormap;
  26619. cimg::mutex(8);
  26620. if (!colormap) {
  26621. colormap.assign(1,256,1,3);
  26622. for (unsigned int index = 0, r = 16; r<256; r+=32)
  26623. for (unsigned int g = 16; g<256; g+=32)
  26624. for (unsigned int b = 32; b<256; b+=64) {
  26625. colormap(0,index,0) = (Tuchar)r;
  26626. colormap(0,index,1) = (Tuchar)g;
  26627. colormap(0,index++,2) = (Tuchar)b;
  26628. }
  26629. }
  26630. cimg::mutex(8,0);
  26631. return colormap;
  26632. }
  26633. //! Return colormap \e "HSV", containing 256 colors entries in RGB.
  26634. /**
  26635. \return The following \c 256x1x1x3 colormap is returned:
  26636. \image html ref_colormap_hsv.jpg
  26637. **/
  26638. static const CImg<Tuchar>& HSV_LUT256() {
  26639. static CImg<Tuchar> colormap;
  26640. cimg::mutex(8);
  26641. if (!colormap) {
  26642. CImg<Tint> tmp(1,256,1,3,1);
  26643. tmp.get_shared_channel(0).sequence(0,359);
  26644. colormap = tmp.HSVtoRGB();
  26645. }
  26646. cimg::mutex(8,0);
  26647. return colormap;
  26648. }
  26649. //! Return colormap \e "lines", containing 256 colors entries in RGB.
  26650. /**
  26651. \return The following \c 256x1x1x3 colormap is returned:
  26652. \image html ref_colormap_lines.jpg
  26653. **/
  26654. static const CImg<Tuchar>& lines_LUT256() {
  26655. static const unsigned char pal[] = {
  26656. 217,62,88,75,1,237,240,12,56,160,165,116,1,1,204,2,15,248,148,185,133,141,46,246,222,116,16,5,207,226,
  26657. 17,114,247,1,214,53,238,0,95,55,233,235,109,0,17,54,33,0,90,30,3,0,94,27,19,0,68,212,166,130,0,15,7,119,
  26658. 238,2,246,198,0,3,16,10,13,2,25,28,12,6,2,99,18,141,30,4,3,140,12,4,30,233,7,10,0,136,35,160,168,184,20,
  26659. 233,0,1,242,83,90,56,180,44,41,0,6,19,207,5,31,214,4,35,153,180,75,21,76,16,202,218,22,17,2,136,71,74,
  26660. 81,251,244,148,222,17,0,234,24,0,200,16,239,15,225,102,230,186,58,230,110,12,0,7,129,249,22,241,37,219,
  26661. 1,3,254,210,3,212,113,131,197,162,123,252,90,96,209,60,0,17,0,180,249,12,112,165,43,27,229,77,40,195,12,
  26662. 87,1,210,148,47,80,5,9,1,137,2,40,57,205,244,40,8,252,98,0,40,43,206,31,187,0,180,1,69,70,227,131,108,0,
  26663. 223,94,228,35,248,243,4,16,0,34,24,2,9,35,73,91,12,199,51,1,249,12,103,131,20,224,2,70,32,
  26664. 233,1,165,3,8,154,246,233,196,5,0,6,183,227,247,195,208,36,0,0,226,160,210,198,69,153,210,1,23,8,192,2,4,
  26665. 137,1,0,52,2,249,241,129,0,0,234,7,238,71,7,32,15,157,157,252,158,2,250,6,13,30,11,162,0,199,21,11,27,224,
  26666. 4,157,20,181,111,187,218,3,0,11,158,230,196,34,223,22,248,135,254,210,157,219,0,117,239,3,255,4,227,5,247,
  26667. 11,4,3,188,111,11,105,195,2,0,14,1,21,219,192,0,183,191,113,241,1,12,17,248,0,48,7,19,1,254,212,0,239,246,
  26668. 0,23,0,250,165,194,194,17,3,253,0,24,6,0,141,167,221,24,212,2,235,243,0,0,205,1,251,133,204,28,4,6,1,10,
  26669. 141,21,74,12,236,254,228,19,1,0,214,1,186,13,13,6,13,16,27,209,6,216,11,207,251,59,32,9,155,23,19,235,143,
  26670. 116,6,213,6,75,159,23,6,0,228,4,10,245,249,1,7,44,234,4,102,174,0,19,239,103,16,15,18,8,214,22,4,47,244,
  26671. 255,8,0,251,173,1,212,252,250,251,252,6,0,29,29,222,233,246,5,149,0,182,180,13,151,0,203,183,0,35,149,0,
  26672. 235,246,254,78,9,17,203,73,11,195,0,3,5,44,0,0,237,5,106,6,130,16,214,20,168,247,168,4,207,11,5,1,232,251,
  26673. 129,210,116,231,217,223,214,27,45,38,4,177,186,249,7,215,172,16,214,27,249,230,236,2,34,216,217,0,175,30,
  26674. 243,225,244,182,20,212,2,226,21,255,20,0,2,13,62,13,191,14,76,64,20,121,4,118,0,216,1,147,0,2,210,1,215,
  26675. 95,210,236,225,184,46,0,248,24,11,1,9,141,250,243,9,221,233,160,11,147,2,55,8,23,12,253,9,0,54,0,231,6,3,
  26676. 141,8,2,246,9,180,5,11,8,227,8,43,110,242,1,130,5,97,36,10,6,219,86,133,11,108,6,1,5,244,67,19,28,0,174,
  26677. 154,16,127,149,252,188,196,196,228,244,9,249,0,0,0,37,170,32,250,0,73,255,23,3,224,234,38,195,198,0,255,87,
  26678. 33,221,174,31,3,0,189,228,6,153,14,144,14,108,197,0,9,206,245,254,3,16,253,178,248,0,95,125,8,0,3,168,21,
  26679. 23,168,19,50,240,244,185,0,1,144,10,168,31,82,1,13 };
  26680. static const CImg<Tuchar> colormap(pal,1,256,1,3,false);
  26681. return colormap;
  26682. }
  26683. //! Return colormap \e "hot", containing 256 colors entries in RGB.
  26684. /**
  26685. \return The following \c 256x1x1x3 colormap is returned:
  26686. \image html ref_colormap_hot.jpg
  26687. **/
  26688. static const CImg<Tuchar>& hot_LUT256() {
  26689. static CImg<Tuchar> colormap;
  26690. cimg::mutex(8);
  26691. if (!colormap) {
  26692. colormap.assign(1,4,1,3,(T)0);
  26693. colormap[1] = colormap[2] = colormap[3] = colormap[6] = colormap[7] = colormap[11] = 255;
  26694. colormap.resize(1,256,1,3,3);
  26695. }
  26696. cimg::mutex(8,0);
  26697. return colormap;
  26698. }
  26699. //! Return colormap \e "cool", containing 256 colors entries in RGB.
  26700. /**
  26701. \return The following \c 256x1x1x3 colormap is returned:
  26702. \image html ref_colormap_cool.jpg
  26703. **/
  26704. static const CImg<Tuchar>& cool_LUT256() {
  26705. static CImg<Tuchar> colormap;
  26706. cimg::mutex(8);
  26707. if (!colormap) colormap.assign(1,2,1,3).fill((T)0,(T)255,(T)255,(T)0,(T)255,(T)255).resize(1,256,1,3,3);
  26708. cimg::mutex(8,0);
  26709. return colormap;
  26710. }
  26711. //! Return colormap \e "jet", containing 256 colors entries in RGB.
  26712. /**
  26713. \return The following \c 256x1x1x3 colormap is returned:
  26714. \image html ref_colormap_jet.jpg
  26715. **/
  26716. static const CImg<Tuchar>& jet_LUT256() {
  26717. static CImg<Tuchar> colormap;
  26718. cimg::mutex(8);
  26719. if (!colormap) {
  26720. colormap.assign(1,4,1,3,(T)0);
  26721. colormap[2] = colormap[3] = colormap[5] = colormap[6] = colormap[8] = colormap[9] = 255;
  26722. colormap.resize(1,256,1,3,3);
  26723. }
  26724. cimg::mutex(8,0);
  26725. return colormap;
  26726. }
  26727. //! Return colormap \e "flag", containing 256 colors entries in RGB.
  26728. /**
  26729. \return The following \c 256x1x1x3 colormap is returned:
  26730. \image html ref_colormap_flag.jpg
  26731. **/
  26732. static const CImg<Tuchar>& flag_LUT256() {
  26733. static CImg<Tuchar> colormap;
  26734. cimg::mutex(8);
  26735. if (!colormap) {
  26736. colormap.assign(1,4,1,3,(T)0);
  26737. colormap[0] = colormap[1] = colormap[5] = colormap[9] = colormap[10] = 255;
  26738. colormap.resize(1,256,1,3,0,2);
  26739. }
  26740. cimg::mutex(8,0);
  26741. return colormap;
  26742. }
  26743. //! Return colormap \e "cube", containing 256 colors entries in RGB.
  26744. /**
  26745. \return The following \c 256x1x1x3 colormap is returned:
  26746. \image html ref_colormap_cube.jpg
  26747. **/
  26748. static const CImg<Tuchar>& cube_LUT256() {
  26749. static CImg<Tuchar> colormap;
  26750. cimg::mutex(8);
  26751. if (!colormap) {
  26752. colormap.assign(1,8,1,3,(T)0);
  26753. colormap[1] = colormap[3] = colormap[5] = colormap[7] =
  26754. colormap[10] = colormap[11] = colormap[12] = colormap[13] =
  26755. colormap[20] = colormap[21] = colormap[22] = colormap[23] = 255;
  26756. colormap.resize(1,256,1,3,3);
  26757. }
  26758. cimg::mutex(8,0);
  26759. return colormap;
  26760. }
  26761. //! Convert pixel values from sRGB to RGB color spaces.
  26762. CImg<T>& sRGBtoRGB() {
  26763. if (is_empty()) return *this;
  26764. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32))
  26765. cimg_rof(*this,ptr,T) {
  26766. const Tfloat
  26767. sval = (Tfloat)*ptr/255,
  26768. val = (Tfloat)(sval<=0.04045f?sval/12.92f:std::pow((sval + 0.055f)/(1.055f),2.4f));
  26769. *ptr = (T)cimg::cut(val*255,0,255);
  26770. }
  26771. return *this;
  26772. }
  26773. //! Convert pixel values from sRGB to RGB color spaces \newinstance.
  26774. CImg<Tfloat> get_sRGBtoRGB() const {
  26775. return CImg<Tfloat>(*this,false).sRGBtoRGB();
  26776. }
  26777. //! Convert pixel values from RGB to sRGB color spaces.
  26778. CImg<T>& RGBtosRGB() {
  26779. if (is_empty()) return *this;
  26780. cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32))
  26781. cimg_rof(*this,ptr,T) {
  26782. const Tfloat
  26783. val = (Tfloat)*ptr/255,
  26784. sval = (Tfloat)(val<=0.0031308f?val*12.92f:1.055f*std::pow(val,0.416667f) - 0.055f);
  26785. *ptr = (T)cimg::cut(sval*255,0,255);
  26786. }
  26787. return *this;
  26788. }
  26789. //! Convert pixel values from RGB to sRGB color spaces \newinstance.
  26790. CImg<Tfloat> get_RGBtosRGB() const {
  26791. return CImg<Tfloat>(*this,false).RGBtosRGB();
  26792. }
  26793. //! Convert pixel values from RGB to HSI color spaces.
  26794. CImg<T>& RGBtoHSI() {
  26795. if (_spectrum!=3)
  26796. throw CImgInstanceException(_cimg_instance
  26797. "RGBtoHSI(): Instance is not a RGB image.",
  26798. cimg_instance);
  26799. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  26800. const ulongT whd = (ulongT)_width*_height*_depth;
  26801. cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=256))
  26802. for (ulongT N = 0; N<whd; ++N) {
  26803. const Tfloat
  26804. R = (Tfloat)p1[N],
  26805. G = (Tfloat)p2[N],
  26806. B = (Tfloat)p3[N],
  26807. theta = (Tfloat)(std::acos(0.5f*((R - G) + (R - B))/
  26808. std::sqrt(cimg::sqr(R - G) + (R - B)*(G - B)))*180/cimg::PI),
  26809. m = cimg::min(R,G,B),
  26810. sum = R + G + B;
  26811. Tfloat H = 0, S = 0, I = 0;
  26812. if (theta>0) H = B<=G?theta:360 - theta;
  26813. if (sum>0) S = 1 - 3*m/sum;
  26814. I = sum/(3*255);
  26815. p1[N] = (T)cimg::cut(H,0,360);
  26816. p2[N] = (T)cimg::cut(S,0,1);
  26817. p3[N] = (T)cimg::cut(I,0,1);
  26818. }
  26819. return *this;
  26820. }
  26821. //! Convert pixel values from RGB to HSI color spaces \newinstance.
  26822. CImg<Tfloat> get_RGBtoHSI() const {
  26823. return CImg<Tfloat>(*this,false).RGBtoHSI();
  26824. }
  26825. //! Convert pixel values from HSI to RGB color spaces.
  26826. CImg<T>& HSItoRGB() {
  26827. if (_spectrum!=3)
  26828. throw CImgInstanceException(_cimg_instance
  26829. "HSItoRGB(): Instance is not a HSI image.",
  26830. cimg_instance);
  26831. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  26832. const ulongT whd = (ulongT)_width*_height*_depth;
  26833. cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=256))
  26834. for (ulongT N = 0; N<whd; ++N) {
  26835. Tfloat
  26836. H = cimg::mod((Tfloat)p1[N],(Tfloat)360),
  26837. S = (Tfloat)p2[N],
  26838. I = (Tfloat)p3[N],
  26839. a = I*(1 - S),
  26840. R = 0, G = 0, B = 0;
  26841. if (H<120) {
  26842. B = a;
  26843. R = (Tfloat)(I*(1 + S*std::cos(H*cimg::PI/180)/std::cos((60 - H)*cimg::PI/180)));
  26844. G = 3*I - (R + B);
  26845. } else if (H<240) {
  26846. H-=120;
  26847. R = a;
  26848. G = (Tfloat)(I*(1 + S*std::cos(H*cimg::PI/180)/std::cos((60 - H)*cimg::PI/180)));
  26849. B = 3*I - (R + G);
  26850. } else {
  26851. H-=240;
  26852. G = a;
  26853. B = (Tfloat)(I*(1 + S*std::cos(H*cimg::PI/180)/std::cos((60 - H)*cimg::PI/180)));
  26854. R = 3*I - (G + B);
  26855. }
  26856. p1[N] = (T)cimg::cut(R*255,0,255);
  26857. p2[N] = (T)cimg::cut(G*255,0,255);
  26858. p3[N] = (T)cimg::cut(B*255,0,255);
  26859. }
  26860. return *this;
  26861. }
  26862. //! Convert pixel values from HSI to RGB color spaces \newinstance.
  26863. CImg<Tfloat> get_HSItoRGB() const {
  26864. return CImg< Tuchar>(*this,false).HSItoRGB();
  26865. }
  26866. //! Convert pixel values from RGB to HSL color spaces.
  26867. CImg<T>& RGBtoHSL() {
  26868. if (_spectrum!=3)
  26869. throw CImgInstanceException(_cimg_instance
  26870. "RGBtoHSL(): Instance is not a RGB image.",
  26871. cimg_instance);
  26872. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  26873. const ulongT whd = (ulongT)_width*_height*_depth;
  26874. cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=256))
  26875. for (ulongT N = 0; N<whd; ++N) {
  26876. const Tfloat
  26877. R = (Tfloat)p1[N],
  26878. G = (Tfloat)p2[N],
  26879. B = (Tfloat)p3[N],
  26880. m = cimg::min(R,G,B),
  26881. M = cimg::max(R,G,B),
  26882. L = (m + M)/(2*255);
  26883. Tfloat H = 0, S = 0;
  26884. if (M!=m) {
  26885. const Tfloat
  26886. f = R==m?G - B:G==m?B - R:R - G,
  26887. i = R==m?3:G==m?5:1;
  26888. H = i - f/(M - m);
  26889. if (H>=6) H-=6;
  26890. H*=60;
  26891. S = 2*L<=1?(M - m)/(M + m):(M - m)/(2*255 - M - m);
  26892. }
  26893. p1[N] = (T)cimg::cut(H,0,360);
  26894. p2[N] = (T)cimg::cut(S,0,1);
  26895. p3[N] = (T)cimg::cut(L,0,1);
  26896. }
  26897. return *this;
  26898. }
  26899. //! Convert pixel values from RGB to HSL color spaces \newinstance.
  26900. CImg<Tfloat> get_RGBtoHSL() const {
  26901. return CImg<Tfloat>(*this,false).RGBtoHSL();
  26902. }
  26903. //! Convert pixel values from HSL to RGB color spaces.
  26904. CImg<T>& HSLtoRGB() {
  26905. if (_spectrum!=3)
  26906. throw CImgInstanceException(_cimg_instance
  26907. "HSLtoRGB(): Instance is not a HSL image.",
  26908. cimg_instance);
  26909. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  26910. const ulongT whd = (ulongT)_width*_height*_depth;
  26911. cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=256))
  26912. for (ulongT N = 0; N<whd; ++N) {
  26913. const Tfloat
  26914. H = cimg::mod((Tfloat)p1[N],(Tfloat)360),
  26915. S = (Tfloat)p2[N],
  26916. L = (Tfloat)p3[N],
  26917. q = 2*L<1?L*(1 + S):L + S - L*S,
  26918. p = 2*L - q,
  26919. h = H/360,
  26920. tr = h + (Tfloat)1/3,
  26921. tg = h,
  26922. tb = h - (Tfloat)1/3,
  26923. ntr = tr<0?tr + 1:tr>1?tr - 1:(Tfloat)tr,
  26924. ntg = tg<0?tg + 1:tg>1?tg - 1:(Tfloat)tg,
  26925. ntb = tb<0?tb + 1:tb>1?tb - 1:(Tfloat)tb,
  26926. R = 6*ntr<1?p + (q - p)*6*ntr:2*ntr<1?q:3*ntr<2?p + (q - p)*6*(2.0f/3 - ntr):p,
  26927. G = 6*ntg<1?p + (q - p)*6*ntg:2*ntg<1?q:3*ntg<2?p + (q - p)*6*(2.0f/3 - ntg):p,
  26928. B = 6*ntb<1?p + (q - p)*6*ntb:2*ntb<1?q:3*ntb<2?p + (q - p)*6*(2.0f/3 - ntb):p;
  26929. p1[N] = (T)cimg::cut(255*R,0,255);
  26930. p2[N] = (T)cimg::cut(255*G,0,255);
  26931. p3[N] = (T)cimg::cut(255*B,0,255);
  26932. }
  26933. return *this;
  26934. }
  26935. //! Convert pixel values from HSL to RGB color spaces \newinstance.
  26936. CImg<Tuchar> get_HSLtoRGB() const {
  26937. return CImg<Tuchar>(*this,false).HSLtoRGB();
  26938. }
  26939. //! Convert pixel values from RGB to HSV color spaces.
  26940. CImg<T>& RGBtoHSV() {
  26941. if (_spectrum!=3)
  26942. throw CImgInstanceException(_cimg_instance
  26943. "RGBtoHSV(): Instance is not a RGB image.",
  26944. cimg_instance);
  26945. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  26946. const ulongT whd = (ulongT)_width*_height*_depth;
  26947. cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=256))
  26948. for (ulongT N = 0; N<whd; ++N) {
  26949. const Tfloat
  26950. R = (Tfloat)p1[N],
  26951. G = (Tfloat)p2[N],
  26952. B = (Tfloat)p3[N],
  26953. m = cimg::min(R,G,B),
  26954. M = cimg::max(R,G,B);
  26955. Tfloat H = 0, S = 0;
  26956. if (M!=m) {
  26957. const Tfloat
  26958. f = R==m?G - B:G==m?B - R:R - G,
  26959. i = R==m?3:G==m?5:1;
  26960. H = i - f/(M - m);
  26961. if (H>=6) H-=6;
  26962. H*=60;
  26963. S = (M - m)/M;
  26964. }
  26965. p1[N] = (T)cimg::cut(H,0,360);
  26966. p2[N] = (T)cimg::cut(S,0,1);
  26967. p3[N] = (T)cimg::cut(M/255,0,1);
  26968. }
  26969. return *this;
  26970. }
  26971. //! Convert pixel values from RGB to HSV color spaces \newinstance.
  26972. CImg<Tfloat> get_RGBtoHSV() const {
  26973. return CImg<Tfloat>(*this,false).RGBtoHSV();
  26974. }
  26975. //! Convert pixel values from HSV to RGB color spaces.
  26976. CImg<T>& HSVtoRGB() {
  26977. if (_spectrum!=3)
  26978. throw CImgInstanceException(_cimg_instance
  26979. "HSVtoRGB(): Instance is not a HSV image.",
  26980. cimg_instance);
  26981. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  26982. const ulongT whd = (ulongT)_width*_height*_depth;
  26983. cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=256))
  26984. for (ulongT N = 0; N<whd; ++N) {
  26985. Tfloat
  26986. H = cimg::mod((Tfloat)p1[N],(Tfloat)360),
  26987. S = (Tfloat)p2[N],
  26988. V = (Tfloat)p3[N],
  26989. R = 0, G = 0, B = 0;
  26990. if (H==0 && S==0) R = G = B = V;
  26991. else {
  26992. H/=60;
  26993. const int i = (int)std::floor(H);
  26994. const Tfloat
  26995. f = (i&1)?H - i:1 - H + i,
  26996. m = V*(1 - S),
  26997. n = V*(1 - S*f);
  26998. switch (i) {
  26999. case 6 :
  27000. case 0 : R = V; G = n; B = m; break;
  27001. case 1 : R = n; G = V; B = m; break;
  27002. case 2 : R = m; G = V; B = n; break;
  27003. case 3 : R = m; G = n; B = V; break;
  27004. case 4 : R = n; G = m; B = V; break;
  27005. case 5 : R = V; G = m; B = n; break;
  27006. }
  27007. }
  27008. p1[N] = (T)cimg::cut(R*255,0,255);
  27009. p2[N] = (T)cimg::cut(G*255,0,255);
  27010. p3[N] = (T)cimg::cut(B*255,0,255);
  27011. }
  27012. return *this;
  27013. }
  27014. //! Convert pixel values from HSV to RGB color spaces \newinstance.
  27015. CImg<Tuchar> get_HSVtoRGB() const {
  27016. return CImg<Tuchar>(*this,false).HSVtoRGB();
  27017. }
  27018. //! Convert pixel values from RGB to YCbCr color spaces.
  27019. CImg<T>& RGBtoYCbCr() {
  27020. if (_spectrum!=3)
  27021. throw CImgInstanceException(_cimg_instance
  27022. "RGBtoYCbCr(): Instance is not a RGB image.",
  27023. cimg_instance);
  27024. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  27025. const ulongT whd = (ulongT)_width*_height*_depth;
  27026. cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=512))
  27027. for (ulongT N = 0; N<whd; ++N) {
  27028. const Tfloat
  27029. R = (Tfloat)p1[N],
  27030. G = (Tfloat)p2[N],
  27031. B = (Tfloat)p3[N],
  27032. Y = (66*R + 129*G + 25*B + 128)/256 + 16,
  27033. Cb = (-38*R - 74*G + 112*B + 128)/256 + 128,
  27034. Cr = (112*R - 94*G - 18*B + 128)/256 + 128;
  27035. p1[N] = (T)cimg::cut(Y,0,255),
  27036. p2[N] = (T)cimg::cut(Cb,0,255),
  27037. p3[N] = (T)cimg::cut(Cr,0,255);
  27038. }
  27039. return *this;
  27040. }
  27041. //! Convert pixel values from RGB to YCbCr color spaces \newinstance.
  27042. CImg<Tuchar> get_RGBtoYCbCr() const {
  27043. return CImg<Tuchar>(*this,false).RGBtoYCbCr();
  27044. }
  27045. //! Convert pixel values from RGB to YCbCr color spaces.
  27046. CImg<T>& YCbCrtoRGB() {
  27047. if (_spectrum!=3)
  27048. throw CImgInstanceException(_cimg_instance
  27049. "YCbCrtoRGB(): Instance is not a YCbCr image.",
  27050. cimg_instance);
  27051. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  27052. const ulongT whd = (ulongT)_width*_height*_depth;
  27053. cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=512))
  27054. for (ulongT N = 0; N<whd; ++N) {
  27055. const Tfloat
  27056. Y = (Tfloat)p1[N] - 16,
  27057. Cb = (Tfloat)p2[N] - 128,
  27058. Cr = (Tfloat)p3[N] - 128,
  27059. R = (298*Y + 409*Cr + 128)/256,
  27060. G = (298*Y - 100*Cb - 208*Cr + 128)/256,
  27061. B = (298*Y + 516*Cb + 128)/256;
  27062. p1[N] = (T)cimg::cut(R,0,255),
  27063. p2[N] = (T)cimg::cut(G,0,255),
  27064. p3[N] = (T)cimg::cut(B,0,255);
  27065. }
  27066. return *this;
  27067. }
  27068. //! Convert pixel values from RGB to YCbCr color spaces \newinstance.
  27069. CImg<Tuchar> get_YCbCrtoRGB() const {
  27070. return CImg<Tuchar>(*this,false).YCbCrtoRGB();
  27071. }
  27072. //! Convert pixel values from RGB to YUV color spaces.
  27073. CImg<T>& RGBtoYUV() {
  27074. if (_spectrum!=3)
  27075. throw CImgInstanceException(_cimg_instance
  27076. "RGBtoYUV(): Instance is not a RGB image.",
  27077. cimg_instance);
  27078. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  27079. const ulongT whd = (ulongT)_width*_height*_depth;
  27080. cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=16384))
  27081. for (ulongT N = 0; N<whd; ++N) {
  27082. const Tfloat
  27083. R = (Tfloat)p1[N]/255,
  27084. G = (Tfloat)p2[N]/255,
  27085. B = (Tfloat)p3[N]/255,
  27086. Y = 0.299f*R + 0.587f*G + 0.114f*B;
  27087. p1[N] = (T)Y;
  27088. p2[N] = (T)(0.492f*(B - Y));
  27089. p3[N] = (T)(0.877*(R - Y));
  27090. }
  27091. return *this;
  27092. }
  27093. //! Convert pixel values from RGB to YUV color spaces \newinstance.
  27094. CImg<Tfloat> get_RGBtoYUV() const {
  27095. return CImg<Tfloat>(*this,false).RGBtoYUV();
  27096. }
  27097. //! Convert pixel values from YUV to RGB color spaces.
  27098. CImg<T>& YUVtoRGB() {
  27099. if (_spectrum!=3)
  27100. throw CImgInstanceException(_cimg_instance
  27101. "YUVtoRGB(): Instance is not a YUV image.",
  27102. cimg_instance);
  27103. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  27104. const ulongT whd = (ulongT)_width*_height*_depth;
  27105. cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=16384))
  27106. for (ulongT N = 0; N<whd; ++N) {
  27107. const Tfloat
  27108. Y = (Tfloat)p1[N],
  27109. U = (Tfloat)p2[N],
  27110. V = (Tfloat)p3[N],
  27111. R = (Y + 1.140f*V)*255,
  27112. G = (Y - 0.395f*U - 0.581f*V)*255,
  27113. B = (Y + 2.032f*U)*255;
  27114. p1[N] = (T)cimg::cut(R,0,255),
  27115. p2[N] = (T)cimg::cut(G,0,255),
  27116. p3[N] = (T)cimg::cut(B,0,255);
  27117. }
  27118. return *this;
  27119. }
  27120. //! Convert pixel values from YUV to RGB color spaces \newinstance.
  27121. CImg<Tuchar> get_YUVtoRGB() const {
  27122. return CImg< Tuchar>(*this,false).YUVtoRGB();
  27123. }
  27124. //! Convert pixel values from RGB to CMY color spaces.
  27125. CImg<T>& RGBtoCMY() {
  27126. if (_spectrum!=3)
  27127. throw CImgInstanceException(_cimg_instance
  27128. "RGBtoCMY(): Instance is not a RGB image.",
  27129. cimg_instance);
  27130. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  27131. const ulongT whd = (ulongT)_width*_height*_depth;
  27132. cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=2048))
  27133. for (ulongT N = 0; N<whd; ++N) {
  27134. const Tfloat
  27135. R = (Tfloat)p1[N],
  27136. G = (Tfloat)p2[N],
  27137. B = (Tfloat)p3[N],
  27138. C = 255 - R,
  27139. M = 255 - G,
  27140. Y = 255 - B;
  27141. p1[N] = (T)cimg::cut(C,0,255),
  27142. p2[N] = (T)cimg::cut(M,0,255),
  27143. p3[N] = (T)cimg::cut(Y,0,255);
  27144. }
  27145. return *this;
  27146. }
  27147. //! Convert pixel values from RGB to CMY color spaces \newinstance.
  27148. CImg<Tuchar> get_RGBtoCMY() const {
  27149. return CImg<Tfloat>(*this,false).RGBtoCMY();
  27150. }
  27151. //! Convert pixel values from CMY to RGB color spaces.
  27152. CImg<T>& CMYtoRGB() {
  27153. if (_spectrum!=3)
  27154. throw CImgInstanceException(_cimg_instance
  27155. "CMYtoRGB(): Instance is not a CMY image.",
  27156. cimg_instance);
  27157. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  27158. const ulongT whd = (ulongT)_width*_height*_depth;
  27159. cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=2048))
  27160. for (ulongT N = 0; N<whd; ++N) {
  27161. const Tfloat
  27162. C = (Tfloat)p1[N],
  27163. M = (Tfloat)p2[N],
  27164. Y = (Tfloat)p3[N],
  27165. R = 255 - C,
  27166. G = 255 - M,
  27167. B = 255 - Y;
  27168. p1[N] = (T)cimg::cut(R,0,255),
  27169. p2[N] = (T)cimg::cut(G,0,255),
  27170. p3[N] = (T)cimg::cut(B,0,255);
  27171. }
  27172. return *this;
  27173. }
  27174. //! Convert pixel values from CMY to RGB color spaces \newinstance.
  27175. CImg<Tuchar> get_CMYtoRGB() const {
  27176. return CImg<Tuchar>(*this,false).CMYtoRGB();
  27177. }
  27178. //! Convert pixel values from CMY to CMYK color spaces.
  27179. CImg<T>& CMYtoCMYK() {
  27180. return get_CMYtoCMYK().move_to(*this);
  27181. }
  27182. //! Convert pixel values from CMY to CMYK color spaces \newinstance.
  27183. CImg<Tuchar> get_CMYtoCMYK() const {
  27184. if (_spectrum!=3)
  27185. throw CImgInstanceException(_cimg_instance
  27186. "CMYtoCMYK(): Instance is not a CMY image.",
  27187. cimg_instance);
  27188. CImg<Tfloat> res(_width,_height,_depth,4);
  27189. const T *ps1 = data(0,0,0,0), *ps2 = data(0,0,0,1), *ps3 = data(0,0,0,2);
  27190. 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);
  27191. const ulongT whd = (ulongT)_width*_height*_depth;
  27192. cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=1024))
  27193. for (ulongT N = 0; N<whd; ++N) {
  27194. Tfloat
  27195. C = (Tfloat)ps1[N],
  27196. M = (Tfloat)ps2[N],
  27197. Y = (Tfloat)ps3[N],
  27198. K = cimg::min(C,M,Y);
  27199. if (K>=255) C = M = Y = 0;
  27200. else { const Tfloat K1 = 255 - K; C = 255*(C - K)/K1; M = 255*(M - K)/K1; Y = 255*(Y - K)/K1; }
  27201. pd1[N] = (Tfloat)cimg::cut(C,0,255),
  27202. pd2[N] = (Tfloat)cimg::cut(M,0,255),
  27203. pd3[N] = (Tfloat)cimg::cut(Y,0,255),
  27204. pd4[N] = (Tfloat)cimg::cut(K,0,255);
  27205. }
  27206. return res;
  27207. }
  27208. //! Convert pixel values from CMYK to CMY color spaces.
  27209. CImg<T>& CMYKtoCMY() {
  27210. return get_CMYKtoCMY().move_to(*this);
  27211. }
  27212. //! Convert pixel values from CMYK to CMY color spaces \newinstance.
  27213. CImg<Tfloat> get_CMYKtoCMY() const {
  27214. if (_spectrum!=4)
  27215. throw CImgInstanceException(_cimg_instance
  27216. "CMYKtoCMY(): Instance is not a CMYK image.",
  27217. cimg_instance);
  27218. CImg<Tfloat> res(_width,_height,_depth,3);
  27219. 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);
  27220. Tfloat *pd1 = res.data(0,0,0,0), *pd2 = res.data(0,0,0,1), *pd3 = res.data(0,0,0,2);
  27221. const ulongT whd = (ulongT)_width*_height*_depth;
  27222. cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=1024))
  27223. for (ulongT N = 0; N<whd; ++N) {
  27224. const Tfloat
  27225. C = (Tfloat)ps1[N],
  27226. M = (Tfloat)ps2[N],
  27227. Y = (Tfloat)ps3[N],
  27228. K = (Tfloat)ps4[N],
  27229. K1 = 1 - K/255,
  27230. nC = C*K1 + K,
  27231. nM = M*K1 + K,
  27232. nY = Y*K1 + K;
  27233. pd1[N] = (Tfloat)cimg::cut(nC,0,255),
  27234. pd2[N] = (Tfloat)cimg::cut(nM,0,255),
  27235. pd3[N] = (Tfloat)cimg::cut(nY,0,255);
  27236. }
  27237. return res;
  27238. }
  27239. //! Convert pixel values from RGB to XYZ color spaces.
  27240. /**
  27241. \param use_D65 Tell to use the D65 illuminant (D50 otherwise).
  27242. **/
  27243. CImg<T>& RGBtoXYZ(const bool use_D65=true) {
  27244. if (_spectrum!=3)
  27245. throw CImgInstanceException(_cimg_instance
  27246. "RGBtoXYZ(): Instance is not a RGB image.",
  27247. cimg_instance);
  27248. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  27249. const ulongT whd = (ulongT)_width*_height*_depth;
  27250. cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=2048))
  27251. for (ulongT N = 0; N<whd; ++N) {
  27252. const Tfloat
  27253. R = (Tfloat)p1[N]/255,
  27254. G = (Tfloat)p2[N]/255,
  27255. B = (Tfloat)p3[N]/255;
  27256. if (use_D65) { // D65
  27257. p1[N] = (T)(0.4124564*R + 0.3575761*G + 0.1804375*B);
  27258. p2[N] = (T)(0.2126729*R + 0.7151522*G + 0.0721750*B);
  27259. p3[N] = (T)(0.0193339*R + 0.1191920*G + 0.9503041*B);
  27260. } else { // D50
  27261. p1[N] = (T)(0.43603516*R + 0.38511658*G + 0.14305115*B);
  27262. p2[N] = (T)(0.22248840*R + 0.71690369*G + 0.06060791*B);
  27263. p3[N] = (T)(0.01391602*R + 0.09706116*G + 0.71392822*B);
  27264. }
  27265. }
  27266. return *this;
  27267. }
  27268. //! Convert pixel values from RGB to XYZ color spaces \newinstance.
  27269. CImg<Tfloat> get_RGBtoXYZ(const bool use_D65=true) const {
  27270. return CImg<Tfloat>(*this,false).RGBtoXYZ(use_D65);
  27271. }
  27272. //! Convert pixel values from XYZ to RGB color spaces.
  27273. /**
  27274. \param use_D65 Tell to use the D65 illuminant (D50 otherwise).
  27275. **/
  27276. CImg<T>& XYZtoRGB(const bool use_D65=true) {
  27277. if (_spectrum!=3)
  27278. throw CImgInstanceException(_cimg_instance
  27279. "XYZtoRGB(): Instance is not a XYZ image.",
  27280. cimg_instance);
  27281. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  27282. const ulongT whd = (ulongT)_width*_height*_depth;
  27283. cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=2048))
  27284. for (ulongT N = 0; N<whd; ++N) {
  27285. const Tfloat
  27286. X = (Tfloat)p1[N]*255,
  27287. Y = (Tfloat)p2[N]*255,
  27288. Z = (Tfloat)p3[N]*255;
  27289. if (use_D65) {
  27290. p1[N] = (T)cimg::cut(3.2404542*X - 1.5371385*Y - 0.4985314*Z,0,255);
  27291. p2[N] = (T)cimg::cut(-0.9692660*X + 1.8760108*Y + 0.0415560*Z,0,255);
  27292. p3[N] = (T)cimg::cut(0.0556434*X - 0.2040259*Y + 1.0572252*Z,0,255);
  27293. } else {
  27294. p1[N] = (T)cimg::cut(3.134274799724*X - 1.617275708956*Y - 0.490724283042*Z,0,255);
  27295. p2[N] = (T)cimg::cut(-0.978795575994*X + 1.916161689117*Y + 0.033453331711*Z,0,255);
  27296. p3[N] = (T)cimg::cut(0.071976988401*X - 0.228984974402*Y + 1.405718224383*Z,0,255);
  27297. }
  27298. }
  27299. return *this;
  27300. }
  27301. //! Convert pixel values from XYZ to RGB color spaces \newinstance.
  27302. CImg<Tuchar> get_XYZtoRGB(const bool use_D65=true) const {
  27303. return CImg<Tuchar>(*this,false).XYZtoRGB(use_D65);
  27304. }
  27305. //! Convert pixel values from XYZ to Lab color spaces.
  27306. CImg<T>& XYZtoLab(const bool use_D65=true) {
  27307. #define _cimg_Labf(x) (24389*(x)>216?cimg::cbrt(x):(24389*(x)/27 + 16)/116)
  27308. if (_spectrum!=3)
  27309. throw CImgInstanceException(_cimg_instance
  27310. "XYZtoLab(): Instance is not a XYZ image.",
  27311. cimg_instance);
  27312. const CImg<Tfloat> white = CImg<Tfloat>(1,1,1,3,255).RGBtoXYZ(use_D65);
  27313. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  27314. const ulongT whd = (ulongT)_width*_height*_depth;
  27315. cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=128))
  27316. for (ulongT N = 0; N<whd; ++N) {
  27317. const Tfloat
  27318. X = (Tfloat)(p1[N]/white[0]),
  27319. Y = (Tfloat)(p2[N]/white[1]),
  27320. Z = (Tfloat)(p3[N]/white[2]),
  27321. fX = (Tfloat)_cimg_Labf(X),
  27322. fY = (Tfloat)_cimg_Labf(Y),
  27323. fZ = (Tfloat)_cimg_Labf(Z);
  27324. p1[N] = (T)cimg::cut(116*fY - 16,0,100);
  27325. p2[N] = (T)(500*(fX - fY));
  27326. p3[N] = (T)(200*(fY - fZ));
  27327. }
  27328. return *this;
  27329. }
  27330. //! Convert pixel values from XYZ to Lab color spaces \newinstance.
  27331. CImg<Tfloat> get_XYZtoLab(const bool use_D65=true) const {
  27332. return CImg<Tfloat>(*this,false).XYZtoLab(use_D65);
  27333. }
  27334. //! Convert pixel values from Lab to XYZ color spaces.
  27335. CImg<T>& LabtoXYZ(const bool use_D65=true) {
  27336. if (_spectrum!=3)
  27337. throw CImgInstanceException(_cimg_instance
  27338. "LabtoXYZ(): Instance is not a Lab image.",
  27339. cimg_instance);
  27340. const CImg<Tfloat> white = CImg<Tfloat>(1,1,1,3,255).RGBtoXYZ(use_D65);
  27341. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  27342. const ulongT whd = (ulongT)_width*_height*_depth;
  27343. cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=128))
  27344. for (ulongT N = 0; N<whd; ++N) {
  27345. const Tfloat
  27346. L = (Tfloat)p1[N],
  27347. a = (Tfloat)p2[N],
  27348. b = (Tfloat)p3[N],
  27349. cY = (L + 16)/116,
  27350. cZ = cY - b/200,
  27351. cX = a/500 + cY,
  27352. X = (Tfloat)(24389*cX>216?cX*cX*cX:(116*cX - 16)*27/24389),
  27353. Y = (Tfloat)(27*L>216?cY*cY*cY:27*L/24389),
  27354. Z = (Tfloat)(24389*cZ>216?cZ*cZ*cZ:(116*cZ - 16)*27/24389);
  27355. p1[N] = (T)(X*white[0]);
  27356. p2[N] = (T)(Y*white[1]);
  27357. p3[N] = (T)(Z*white[2]);
  27358. }
  27359. return *this;
  27360. }
  27361. //! Convert pixel values from Lab to XYZ color spaces \newinstance.
  27362. CImg<Tfloat> get_LabtoXYZ(const bool use_D65=true) const {
  27363. return CImg<Tfloat>(*this,false).LabtoXYZ(use_D65);
  27364. }
  27365. //! Convert pixel values from XYZ to xyY color spaces.
  27366. CImg<T>& XYZtoxyY() {
  27367. if (_spectrum!=3)
  27368. throw CImgInstanceException(_cimg_instance
  27369. "XYZtoxyY(): Instance is not a XYZ image.",
  27370. cimg_instance);
  27371. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  27372. const ulongT whd = (ulongT)_width*_height*_depth;
  27373. cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=4096))
  27374. for (ulongT N = 0; N<whd; ++N) {
  27375. const Tfloat
  27376. X = (Tfloat)p1[N],
  27377. Y = (Tfloat)p2[N],
  27378. Z = (Tfloat)p3[N],
  27379. sum = X + Y + Z,
  27380. nsum = sum>0?sum:1;
  27381. p1[N] = (T)(X/nsum);
  27382. p2[N] = (T)(Y/nsum);
  27383. p3[N] = (T)Y;
  27384. }
  27385. return *this;
  27386. }
  27387. //! Convert pixel values from XYZ to xyY color spaces \newinstance.
  27388. CImg<Tfloat> get_XYZtoxyY() const {
  27389. return CImg<Tfloat>(*this,false).XYZtoxyY();
  27390. }
  27391. //! Convert pixel values from xyY pixels to XYZ color spaces.
  27392. CImg<T>& xyYtoXYZ() {
  27393. if (_spectrum!=3)
  27394. throw CImgInstanceException(_cimg_instance
  27395. "xyYtoXYZ(): Instance is not a xyY image.",
  27396. cimg_instance);
  27397. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  27398. const ulongT whd = (ulongT)_width*_height*_depth;
  27399. cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=4096))
  27400. for (ulongT N = 0; N<whd; ++N) {
  27401. const Tfloat
  27402. px = (Tfloat)p1[N],
  27403. py = (Tfloat)p2[N],
  27404. Y = (Tfloat)p3[N],
  27405. ny = py>0?py:1;
  27406. p1[N] = (T)(px*Y/ny);
  27407. p2[N] = (T)Y;
  27408. p3[N] = (T)((1 - px - py)*Y/ny);
  27409. }
  27410. return *this;
  27411. }
  27412. //! Convert pixel values from xyY pixels to XYZ color spaces \newinstance.
  27413. CImg<Tfloat> get_xyYtoXYZ() const {
  27414. return CImg<Tfloat>(*this,false).xyYtoXYZ();
  27415. }
  27416. //! Convert pixel values from RGB to Lab color spaces.
  27417. CImg<T>& RGBtoLab(const bool use_D65=true) {
  27418. return RGBtoXYZ(use_D65).XYZtoLab(use_D65);
  27419. }
  27420. //! Convert pixel values from RGB to Lab color spaces \newinstance.
  27421. CImg<Tfloat> get_RGBtoLab(const bool use_D65=true) const {
  27422. return CImg<Tfloat>(*this,false).RGBtoLab(use_D65);
  27423. }
  27424. //! Convert pixel values from Lab to RGB color spaces.
  27425. CImg<T>& LabtoRGB(const bool use_D65=true) {
  27426. return LabtoXYZ().XYZtoRGB(use_D65);
  27427. }
  27428. //! Convert pixel values from Lab to RGB color spaces \newinstance.
  27429. CImg<Tuchar> get_LabtoRGB(const bool use_D65=true) const {
  27430. return CImg<Tuchar>(*this,false).LabtoRGB(use_D65);
  27431. }
  27432. //! Convert pixel values from RGB to xyY color spaces.
  27433. CImg<T>& RGBtoxyY(const bool use_D65=true) {
  27434. return RGBtoXYZ(use_D65).XYZtoxyY();
  27435. }
  27436. //! Convert pixel values from RGB to xyY color spaces \newinstance.
  27437. CImg<Tfloat> get_RGBtoxyY(const bool use_D65=true) const {
  27438. return CImg<Tfloat>(*this,false).RGBtoxyY(use_D65);
  27439. }
  27440. //! Convert pixel values from xyY to RGB color spaces.
  27441. CImg<T>& xyYtoRGB(const bool use_D65=true) {
  27442. return xyYtoXYZ().XYZtoRGB(use_D65);
  27443. }
  27444. //! Convert pixel values from xyY to RGB color spaces \newinstance.
  27445. CImg<Tuchar> get_xyYtoRGB(const bool use_D65=true) const {
  27446. return CImg<Tuchar>(*this,false).xyYtoRGB(use_D65);
  27447. }
  27448. //! Convert pixel values from RGB to CMYK color spaces.
  27449. CImg<T>& RGBtoCMYK() {
  27450. return RGBtoCMY().CMYtoCMYK();
  27451. }
  27452. //! Convert pixel values from RGB to CMYK color spaces \newinstance.
  27453. CImg<Tfloat> get_RGBtoCMYK() const {
  27454. return CImg<Tfloat>(*this,false).RGBtoCMYK();
  27455. }
  27456. //! Convert pixel values from CMYK to RGB color spaces.
  27457. CImg<T>& CMYKtoRGB() {
  27458. return CMYKtoCMY().CMYtoRGB();
  27459. }
  27460. //! Convert pixel values from CMYK to RGB color spaces \newinstance.
  27461. CImg<Tuchar> get_CMYKtoRGB() const {
  27462. return CImg<Tuchar>(*this,false).CMYKtoRGB();
  27463. }
  27464. //@}
  27465. //------------------------------------------
  27466. //
  27467. //! \name Geometric / Spatial Manipulation
  27468. //@{
  27469. //------------------------------------------
  27470. static float _cimg_lanczos(const float x) {
  27471. if (x<=-2 || x>=2) return 0;
  27472. const float a = (float)cimg::PI*x, b = 0.5f*a;
  27473. return (float)(x?std::sin(a)*std::sin(b)/(a*b):1);
  27474. }
  27475. //! Resize image to new dimensions.
  27476. /**
  27477. \param size_x Number of columns (new size along the X-axis).
  27478. \param size_y Number of rows (new size along the Y-axis).
  27479. \param size_z Number of slices (new size along the Z-axis).
  27480. \param size_c Number of vector-channels (new size along the C-axis).
  27481. \param interpolation_type Method of interpolation:
  27482. - -1 = no interpolation: raw memory resizing.
  27483. - 0 = no interpolation: additional space is filled according to \p boundary_conditions.
  27484. - 1 = nearest-neighbor interpolation.
  27485. - 2 = moving average interpolation.
  27486. - 3 = linear interpolation.
  27487. - 4 = grid interpolation.
  27488. - 5 = cubic interpolation.
  27489. - 6 = lanczos interpolation.
  27490. \param boundary_conditions Type of boundary conditions used if necessary.
  27491. \param centering_x Set centering type (only if \p interpolation_type=0).
  27492. \param centering_y Set centering type (only if \p interpolation_type=0).
  27493. \param centering_z Set centering type (only if \p interpolation_type=0).
  27494. \param centering_c Set centering type (only if \p interpolation_type=0).
  27495. \note If pd[x,y,z,v]<0, it corresponds to a percentage of the original size (the default value is -100).
  27496. **/
  27497. CImg<T>& resize(const int size_x, const int size_y=-100,
  27498. const int size_z=-100, const int size_c=-100,
  27499. const int interpolation_type=1, const unsigned int boundary_conditions=0,
  27500. const float centering_x = 0, const float centering_y = 0,
  27501. const float centering_z = 0, const float centering_c = 0) {
  27502. if (!size_x || !size_y || !size_z || !size_c) return assign();
  27503. const unsigned int
  27504. _sx = (unsigned int)(size_x<0?-size_x*width()/100:size_x),
  27505. _sy = (unsigned int)(size_y<0?-size_y*height()/100:size_y),
  27506. _sz = (unsigned int)(size_z<0?-size_z*depth()/100:size_z),
  27507. _sc = (unsigned int)(size_c<0?-size_c*spectrum()/100:size_c),
  27508. sx = _sx?_sx:1, sy = _sy?_sy:1, sz = _sz?_sz:1, sc = _sc?_sc:1;
  27509. if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return *this;
  27510. if (is_empty()) return assign(sx,sy,sz,sc,(T)0);
  27511. if (interpolation_type==-1 && sx*sy*sz*sc==size()) {
  27512. _width = sx; _height = sy; _depth = sz; _spectrum = sc;
  27513. return *this;
  27514. }
  27515. return get_resize(sx,sy,sz,sc,interpolation_type,boundary_conditions,
  27516. centering_x,centering_y,centering_z,centering_c).move_to(*this);
  27517. }
  27518. //! Resize image to new dimensions \newinstance.
  27519. CImg<T> get_resize(const int size_x, const int size_y = -100,
  27520. const int size_z = -100, const int size_c = -100,
  27521. const int interpolation_type=1, const unsigned int boundary_conditions=0,
  27522. const float centering_x = 0, const float centering_y = 0,
  27523. const float centering_z = 0, const float centering_c = 0) const {
  27524. if (centering_x<0 || centering_x>1 || centering_y<0 || centering_y>1 ||
  27525. centering_z<0 || centering_z>1 || centering_c<0 || centering_c>1)
  27526. throw CImgArgumentException(_cimg_instance
  27527. "resize(): Specified centering arguments (%g,%g,%g,%g) are outside range [0,1].",
  27528. cimg_instance,
  27529. centering_x,centering_y,centering_z,centering_c);
  27530. if (!size_x || !size_y || !size_z || !size_c) return CImg<T>();
  27531. const unsigned int
  27532. sx = std::max(1U,(unsigned int)(size_x>=0?size_x:-size_x*width()/100)),
  27533. sy = std::max(1U,(unsigned int)(size_y>=0?size_y:-size_y*height()/100)),
  27534. sz = std::max(1U,(unsigned int)(size_z>=0?size_z:-size_z*depth()/100)),
  27535. sc = std::max(1U,(unsigned int)(size_c>=0?size_c:-size_c*spectrum()/100));
  27536. if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return +*this;
  27537. if (is_empty()) return CImg<T>(sx,sy,sz,sc,(T)0);
  27538. CImg<T> res;
  27539. switch (interpolation_type) {
  27540. // Raw resizing.
  27541. //
  27542. case -1 :
  27543. std::memcpy(res.assign(sx,sy,sz,sc,(T)0)._data,_data,sizeof(T)*std::min(size(),(ulongT)sx*sy*sz*sc));
  27544. break;
  27545. // No interpolation.
  27546. //
  27547. case 0 : {
  27548. const int
  27549. xc = (int)(centering_x*((int)sx - width())),
  27550. yc = (int)(centering_y*((int)sy - height())),
  27551. zc = (int)(centering_z*((int)sz - depth())),
  27552. cc = (int)(centering_c*((int)sc - spectrum()));
  27553. switch (boundary_conditions) {
  27554. case 3 : { // Mirror
  27555. res.assign(sx,sy,sz,sc);
  27556. const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth(), s2 = 2*spectrum();
  27557. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=65536))
  27558. cimg_forXYZC(res,x,y,z,c) {
  27559. const int
  27560. mx = cimg::mod(x - xc,w2), my = cimg::mod(y - yc,h2),
  27561. mz = cimg::mod(z - zc,d2), mc = cimg::mod(c - cc,s2);
  27562. res(x,y,z,c) = (*this)(mx<width()?mx:w2 - mx - 1,
  27563. my<height()?my:h2 - my - 1,
  27564. mz<depth()?mz:d2 - mz - 1,
  27565. mc<spectrum()?mc:s2 - mc - 1);
  27566. }
  27567. } break;
  27568. case 2 : { // Periodic
  27569. res.assign(sx,sy,sz,sc);
  27570. const int
  27571. x0 = ((int)xc%width()) - width(),
  27572. y0 = ((int)yc%height()) - height(),
  27573. z0 = ((int)zc%depth()) - depth(),
  27574. c0 = ((int)cc%spectrum()) - spectrum(),
  27575. dx = width(), dy = height(), dz = depth(), dc = spectrum();
  27576. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=65536))
  27577. for (int c = c0; c<(int)sc; c+=dc)
  27578. for (int z = z0; z<(int)sz; z+=dz)
  27579. for (int y = y0; y<(int)sy; y+=dy)
  27580. for (int x = x0; x<(int)sx; x+=dx)
  27581. res.draw_image(x,y,z,c,*this);
  27582. } break;
  27583. case 1 : { // Neumann
  27584. res.assign(sx,sy,sz,sc).draw_image(xc,yc,zc,cc,*this);
  27585. CImg<T> sprite;
  27586. if (xc>0) { // X-backward
  27587. res.get_crop(xc,yc,zc,cc,xc,yc + height() - 1,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
  27588. for (int x = xc - 1; x>=0; --x) res.draw_image(x,yc,zc,cc,sprite);
  27589. }
  27590. if (xc + width()<(int)sx) { // X-forward
  27591. res.get_crop(xc + width() - 1,yc,zc,cc,xc + width() - 1,yc + height() - 1,
  27592. zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
  27593. for (int x = xc + width(); x<(int)sx; ++x) res.draw_image(x,yc,zc,cc,sprite);
  27594. }
  27595. if (yc>0) { // Y-backward
  27596. res.get_crop(0,yc,zc,cc,sx - 1,yc,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
  27597. for (int y = yc - 1; y>=0; --y) res.draw_image(0,y,zc,cc,sprite);
  27598. }
  27599. if (yc + height()<(int)sy) { // Y-forward
  27600. res.get_crop(0,yc + height() - 1,zc,cc,sx - 1,yc + height() - 1,
  27601. zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
  27602. for (int y = yc + height(); y<(int)sy; ++y) res.draw_image(0,y,zc,cc,sprite);
  27603. }
  27604. if (zc>0) { // Z-backward
  27605. res.get_crop(0,0,zc,cc,sx - 1,sy - 1,zc,cc + spectrum() - 1).move_to(sprite);
  27606. for (int z = zc - 1; z>=0; --z) res.draw_image(0,0,z,cc,sprite);
  27607. }
  27608. if (zc + depth()<(int)sz) { // Z-forward
  27609. res.get_crop(0,0,zc +depth() - 1,cc,sx - 1,sy - 1,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
  27610. for (int z = zc + depth(); z<(int)sz; ++z) res.draw_image(0,0,z,cc,sprite);
  27611. }
  27612. if (cc>0) { // C-backward
  27613. res.get_crop(0,0,0,cc,sx - 1,sy - 1,sz - 1,cc).move_to(sprite);
  27614. for (int c = cc - 1; c>=0; --c) res.draw_image(0,0,0,c,sprite);
  27615. }
  27616. if (cc + spectrum()<(int)sc) { // C-forward
  27617. res.get_crop(0,0,0,cc + spectrum() - 1,sx - 1,sy - 1,sz - 1,cc + spectrum() - 1).move_to(sprite);
  27618. for (int c = cc + spectrum(); c<(int)sc; ++c) res.draw_image(0,0,0,c,sprite);
  27619. }
  27620. } break;
  27621. default : // Dirichlet
  27622. res.assign(sx,sy,sz,sc,(T)0).draw_image(xc,yc,zc,cc,*this);
  27623. }
  27624. break;
  27625. } break;
  27626. // Nearest neighbor interpolation.
  27627. //
  27628. case 1 : {
  27629. res.assign(sx,sy,sz,sc);
  27630. CImg<ulongT> off_x(sx), off_y(sy + 1), off_z(sz + 1), off_c(sc + 1);
  27631. const ulongT
  27632. wh = (ulongT)_width*_height,
  27633. whd = (ulongT)_width*_height*_depth,
  27634. sxy = (ulongT)sx*sy,
  27635. sxyz = (ulongT)sx*sy*sz;
  27636. if (sx==_width) off_x.fill(1);
  27637. else {
  27638. ulongT *poff_x = off_x._data, curr = 0;
  27639. cimg_forX(res,x) {
  27640. const ulongT old = curr;
  27641. curr = (ulongT)((x + 1.0)*_width/sx);
  27642. *(poff_x++) = curr - old;
  27643. }
  27644. }
  27645. if (sy==_height) off_y.fill(_width);
  27646. else {
  27647. ulongT *poff_y = off_y._data, curr = 0;
  27648. cimg_forY(res,y) {
  27649. const ulongT old = curr;
  27650. curr = (ulongT)((y + 1.0)*_height/sy);
  27651. *(poff_y++) = _width*(curr - old);
  27652. }
  27653. *poff_y = 0;
  27654. }
  27655. if (sz==_depth) off_z.fill(wh);
  27656. else {
  27657. ulongT *poff_z = off_z._data, curr = 0;
  27658. cimg_forZ(res,z) {
  27659. const ulongT old = curr;
  27660. curr = (ulongT)((z + 1.0)*_depth/sz);
  27661. *(poff_z++) = wh*(curr - old);
  27662. }
  27663. *poff_z = 0;
  27664. }
  27665. if (sc==_spectrum) off_c.fill(whd);
  27666. else {
  27667. ulongT *poff_c = off_c._data, curr = 0;
  27668. cimg_forC(res,c) {
  27669. const ulongT old = curr;
  27670. curr = (ulongT)((c + 1.0)*_spectrum/sc);
  27671. *(poff_c++) = whd*(curr - old);
  27672. }
  27673. *poff_c = 0;
  27674. }
  27675. T *ptrd = res._data;
  27676. const T* ptrc = _data;
  27677. const ulongT *poff_c = off_c._data;
  27678. for (unsigned int c = 0; c<sc; ) {
  27679. const T *ptrz = ptrc;
  27680. const ulongT *poff_z = off_z._data;
  27681. for (unsigned int z = 0; z<sz; ) {
  27682. const T *ptry = ptrz;
  27683. const ulongT *poff_y = off_y._data;
  27684. for (unsigned int y = 0; y<sy; ) {
  27685. const T *ptrx = ptry;
  27686. const ulongT *poff_x = off_x._data;
  27687. cimg_forX(res,x) { *(ptrd++) = *ptrx; ptrx+=*(poff_x++); }
  27688. ++y;
  27689. ulongT dy = *(poff_y++);
  27690. for ( ; !dy && y<dy; std::memcpy(ptrd,ptrd - sx,sizeof(T)*sx), ++y, ptrd+=sx, dy = *(poff_y++)) {}
  27691. ptry+=dy;
  27692. }
  27693. ++z;
  27694. ulongT dz = *(poff_z++);
  27695. for ( ; !dz && z<dz; std::memcpy(ptrd,ptrd-sxy,sizeof(T)*sxy), ++z, ptrd+=sxy, dz = *(poff_z++)) {}
  27696. ptrz+=dz;
  27697. }
  27698. ++c;
  27699. ulongT dc = *(poff_c++);
  27700. for ( ; !dc && c<dc; std::memcpy(ptrd,ptrd-sxyz,sizeof(T)*sxyz), ++c, ptrd+=sxyz, dc = *(poff_c++)) {}
  27701. ptrc+=dc;
  27702. }
  27703. } break;
  27704. // Moving average.
  27705. //
  27706. case 2 : {
  27707. bool instance_first = true;
  27708. if (sx!=_width) {
  27709. CImg<Tfloat> tmp(sx,_height,_depth,_spectrum,0);
  27710. for (unsigned int a = _width*sx, b = _width, c = sx, s = 0, t = 0; a; ) {
  27711. const unsigned int d = std::min(b,c);
  27712. a-=d; b-=d; c-=d;
  27713. cimg_forYZC(tmp,y,z,v) tmp(t,y,z,v)+=(Tfloat)(*this)(s,y,z,v)*d;
  27714. if (!b) {
  27715. cimg_forYZC(tmp,y,z,v) tmp(t,y,z,v)/=_width;
  27716. ++t;
  27717. b = _width;
  27718. }
  27719. if (!c) { ++s; c = sx; }
  27720. }
  27721. tmp.move_to(res);
  27722. instance_first = false;
  27723. }
  27724. if (sy!=_height) {
  27725. CImg<Tfloat> tmp(sx,sy,_depth,_spectrum,0);
  27726. for (unsigned int a = _height*sy, b = _height, c = sy, s = 0, t = 0; a; ) {
  27727. const unsigned int d = std::min(b,c);
  27728. a-=d; b-=d; c-=d;
  27729. if (instance_first)
  27730. cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)+=(Tfloat)(*this)(x,s,z,v)*d;
  27731. else
  27732. cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)+=(Tfloat)res(x,s,z,v)*d;
  27733. if (!b) {
  27734. cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)/=_height;
  27735. ++t;
  27736. b = _height;
  27737. }
  27738. if (!c) { ++s; c = sy; }
  27739. }
  27740. tmp.move_to(res);
  27741. instance_first = false;
  27742. }
  27743. if (sz!=_depth) {
  27744. CImg<Tfloat> tmp(sx,sy,sz,_spectrum,0);
  27745. for (unsigned int a = _depth*sz, b = _depth, c = sz, s = 0, t = 0; a; ) {
  27746. const unsigned int d = std::min(b,c);
  27747. a-=d; b-=d; c-=d;
  27748. if (instance_first)
  27749. cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)+=(Tfloat)(*this)(x,y,s,v)*d;
  27750. else
  27751. cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)+=(Tfloat)res(x,y,s,v)*d;
  27752. if (!b) {
  27753. cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)/=_depth;
  27754. ++t;
  27755. b = _depth;
  27756. }
  27757. if (!c) { ++s; c = sz; }
  27758. }
  27759. tmp.move_to(res);
  27760. instance_first = false;
  27761. }
  27762. if (sc!=_spectrum) {
  27763. CImg<Tfloat> tmp(sx,sy,sz,sc,0);
  27764. for (unsigned int a = _spectrum*sc, b = _spectrum, c = sc, s = 0, t = 0; a; ) {
  27765. const unsigned int d = std::min(b,c);
  27766. a-=d; b-=d; c-=d;
  27767. if (instance_first)
  27768. cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)+=(Tfloat)(*this)(x,y,z,s)*d;
  27769. else
  27770. cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)+=(Tfloat)res(x,y,z,s)*d;
  27771. if (!b) {
  27772. cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)/=_spectrum;
  27773. ++t;
  27774. b = _spectrum;
  27775. }
  27776. if (!c) { ++s; c = sc; }
  27777. }
  27778. tmp.move_to(res);
  27779. instance_first = false;
  27780. }
  27781. } break;
  27782. // Linear interpolation.
  27783. //
  27784. case 3 : {
  27785. CImg<uintT> off(cimg::max(sx,sy,sz,sc));
  27786. CImg<doubleT> foff(off._width);
  27787. CImg<T> resx, resy, resz, resc;
  27788. double curr, old;
  27789. if (sx!=_width) {
  27790. if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
  27791. else if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx);
  27792. else {
  27793. const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.0)/(sx - 1):0):
  27794. (double)_width/sx;
  27795. resx.assign(sx,_height,_depth,_spectrum);
  27796. curr = old = 0;
  27797. unsigned int *poff = off._data;
  27798. double *pfoff = foff._data;
  27799. cimg_forX(resx,x) {
  27800. *(pfoff++) = curr - (unsigned int)curr;
  27801. old = curr;
  27802. curr = std::min(width() - 1.0,curr + fx);
  27803. *(poff++) = (unsigned int)curr - (unsigned int)old;
  27804. }
  27805. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resx.size()>=65536))
  27806. cimg_forYZC(resx,y,z,c) {
  27807. const T *ptrs = data(0,y,z,c), *const ptrsmax = ptrs + _width - 1;
  27808. T *ptrd = resx.data(0,y,z,c);
  27809. const unsigned int *poff = off._data;
  27810. const double *pfoff = foff._data;
  27811. cimg_forX(resx,x) {
  27812. const double alpha = *(pfoff++);
  27813. const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + 1):val1;
  27814. *(ptrd++) = (T)((1 - alpha)*val1 + alpha*val2);
  27815. ptrs+=*(poff++);
  27816. }
  27817. }
  27818. }
  27819. } else resx.assign(*this,true);
  27820. if (sy!=_height) {
  27821. if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
  27822. else {
  27823. if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy);
  27824. else {
  27825. const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.0)/(sy - 1):0):
  27826. (double)_height/sy;
  27827. resy.assign(sx,sy,_depth,_spectrum);
  27828. curr = old = 0;
  27829. unsigned int *poff = off._data;
  27830. double *pfoff = foff._data;
  27831. cimg_forY(resy,y) {
  27832. *(pfoff++) = curr - (unsigned int)curr;
  27833. old = curr;
  27834. curr = std::min(height() - 1.0,curr + fy);
  27835. *(poff++) = sx*((unsigned int)curr - (unsigned int)old);
  27836. }
  27837. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resy.size()>=65536))
  27838. cimg_forXZC(resy,x,z,c) {
  27839. const T *ptrs = resx.data(x,0,z,c), *const ptrsmax = ptrs + (_height - 1)*sx;
  27840. T *ptrd = resy.data(x,0,z,c);
  27841. const unsigned int *poff = off._data;
  27842. const double *pfoff = foff._data;
  27843. cimg_forY(resy,y) {
  27844. const double alpha = *(pfoff++);
  27845. const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + sx):val1;
  27846. *ptrd = (T)((1 - alpha)*val1 + alpha*val2);
  27847. ptrd+=sx;
  27848. ptrs+=*(poff++);
  27849. }
  27850. }
  27851. }
  27852. }
  27853. resx.assign();
  27854. } else resy.assign(resx,true);
  27855. if (sz!=_depth) {
  27856. if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
  27857. else {
  27858. if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz);
  27859. else {
  27860. const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.0)/(sz - 1):0):
  27861. (double)_depth/sz;
  27862. const unsigned int sxy = sx*sy;
  27863. resz.assign(sx,sy,sz,_spectrum);
  27864. curr = old = 0;
  27865. unsigned int *poff = off._data;
  27866. double *pfoff = foff._data;
  27867. cimg_forZ(resz,z) {
  27868. *(pfoff++) = curr - (unsigned int)curr;
  27869. old = curr;
  27870. curr = std::min(depth() - 1.0,curr + fz);
  27871. *(poff++) = sxy*((unsigned int)curr - (unsigned int)old);
  27872. }
  27873. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resz.size()>=65536))
  27874. cimg_forXYC(resz,x,y,c) {
  27875. const T *ptrs = resy.data(x,y,0,c), *const ptrsmax = ptrs + (_depth - 1)*sxy;
  27876. T *ptrd = resz.data(x,y,0,c);
  27877. const unsigned int *poff = off._data;
  27878. const double *pfoff = foff._data;
  27879. cimg_forZ(resz,z) {
  27880. const double alpha = *(pfoff++);
  27881. const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + sxy):val1;
  27882. *ptrd = (T)((1 - alpha)*val1 + alpha*val2);
  27883. ptrd+=sxy;
  27884. ptrs+=*(poff++);
  27885. }
  27886. }
  27887. }
  27888. }
  27889. resy.assign();
  27890. } else resz.assign(resy,true);
  27891. if (sc!=_spectrum) {
  27892. if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
  27893. else {
  27894. if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc);
  27895. else {
  27896. const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.0)/(sc - 1):0):
  27897. (double)_spectrum/sc;
  27898. const unsigned int sxyz = sx*sy*sz;
  27899. resc.assign(sx,sy,sz,sc);
  27900. curr = old = 0;
  27901. unsigned int *poff = off._data;
  27902. double *pfoff = foff._data;
  27903. cimg_forC(resc,c) {
  27904. *(pfoff++) = curr - (unsigned int)curr;
  27905. old = curr;
  27906. curr = std::min(spectrum() - 1.0,curr + fc);
  27907. *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old);
  27908. }
  27909. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resc.size()>=65536))
  27910. cimg_forXYZ(resc,x,y,z) {
  27911. const T *ptrs = resz.data(x,y,z,0), *const ptrsmax = ptrs + (_spectrum - 1)*sxyz;
  27912. T *ptrd = resc.data(x,y,z,0);
  27913. const unsigned int *poff = off._data;
  27914. const double *pfoff = foff._data;
  27915. cimg_forC(resc,c) {
  27916. const double alpha = *(pfoff++);
  27917. const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + sxyz):val1;
  27918. *ptrd = (T)((1 - alpha)*val1 + alpha*val2);
  27919. ptrd+=sxyz;
  27920. ptrs+=*(poff++);
  27921. }
  27922. }
  27923. }
  27924. }
  27925. resz.assign();
  27926. } else resc.assign(resz,true);
  27927. return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
  27928. } break;
  27929. // Grid interpolation.
  27930. //
  27931. case 4 : {
  27932. CImg<T> resx, resy, resz, resc;
  27933. if (sx!=_width) {
  27934. if (sx<_width) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
  27935. else {
  27936. resx.assign(sx,_height,_depth,_spectrum,(T)0);
  27937. const int dx = (int)(2*sx), dy = 2*width();
  27938. int err = (int)(dy + centering_x*(sx*dy/width() - dy)), xs = 0;
  27939. cimg_forX(resx,x) if ((err-=dy)<=0) {
  27940. cimg_forYZC(resx,y,z,c) resx(x,y,z,c) = (*this)(xs,y,z,c);
  27941. ++xs;
  27942. err+=dx;
  27943. }
  27944. }
  27945. } else resx.assign(*this,true);
  27946. if (sy!=_height) {
  27947. if (sy<_height) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
  27948. else {
  27949. resy.assign(sx,sy,_depth,_spectrum,(T)0);
  27950. const int dx = (int)(2*sy), dy = 2*height();
  27951. int err = (int)(dy + centering_y*(sy*dy/height() - dy)), ys = 0;
  27952. cimg_forY(resy,y) if ((err-=dy)<=0) {
  27953. cimg_forXZC(resy,x,z,c) resy(x,y,z,c) = resx(x,ys,z,c);
  27954. ++ys;
  27955. err+=dx;
  27956. }
  27957. }
  27958. resx.assign();
  27959. } else resy.assign(resx,true);
  27960. if (sz!=_depth) {
  27961. if (sz<_depth) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
  27962. else {
  27963. resz.assign(sx,sy,sz,_spectrum,(T)0);
  27964. const int dx = (int)(2*sz), dy = 2*depth();
  27965. int err = (int)(dy + centering_z*(sz*dy/depth() - dy)), zs = 0;
  27966. cimg_forZ(resz,z) if ((err-=dy)<=0) {
  27967. cimg_forXYC(resz,x,y,c) resz(x,y,z,c) = resy(x,y,zs,c);
  27968. ++zs;
  27969. err+=dx;
  27970. }
  27971. }
  27972. resy.assign();
  27973. } else resz.assign(resy,true);
  27974. if (sc!=_spectrum) {
  27975. if (sc<_spectrum) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
  27976. else {
  27977. resc.assign(sx,sy,sz,sc,(T)0);
  27978. const int dx = (int)(2*sc), dy = 2*spectrum();
  27979. int err = (int)(dy + centering_c*(sc*dy/spectrum() - dy)), cs = 0;
  27980. cimg_forC(resc,c) if ((err-=dy)<=0) {
  27981. cimg_forXYZ(resc,x,y,z) resc(x,y,z,c) = resz(x,y,z,cs);
  27982. ++cs;
  27983. err+=dx;
  27984. }
  27985. }
  27986. resz.assign();
  27987. } else resc.assign(resz,true);
  27988. return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
  27989. } break;
  27990. // Cubic interpolation.
  27991. //
  27992. case 5 : {
  27993. const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
  27994. CImg<uintT> off(cimg::max(sx,sy,sz,sc));
  27995. CImg<doubleT> foff(off._width);
  27996. CImg<T> resx, resy, resz, resc;
  27997. double curr, old;
  27998. if (sx!=_width) {
  27999. if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
  28000. else {
  28001. if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx);
  28002. else {
  28003. const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.0)/(sx - 1):0):
  28004. (double)_width/sx;
  28005. resx.assign(sx,_height,_depth,_spectrum);
  28006. curr = old = 0;
  28007. unsigned int *poff = off._data;
  28008. double *pfoff = foff._data;
  28009. cimg_forX(resx,x) {
  28010. *(pfoff++) = curr - (unsigned int)curr;
  28011. old = curr;
  28012. curr = std::min(width() - 1.0,curr + fx);
  28013. *(poff++) = (unsigned int)curr - (unsigned int)old;
  28014. }
  28015. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resx.size()>=65536))
  28016. cimg_forYZC(resx,y,z,c) {
  28017. const T *const ptrs0 = data(0,y,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_width - 2);
  28018. T *ptrd = resx.data(0,y,z,c);
  28019. const unsigned int *poff = off._data;
  28020. const double *pfoff = foff._data;
  28021. cimg_forX(resx,x) {
  28022. const double
  28023. t = *(pfoff++),
  28024. val1 = (double)*ptrs,
  28025. val0 = ptrs>ptrs0?(double)*(ptrs - 1):val1,
  28026. val2 = ptrs<=ptrsmax?(double)*(ptrs + 1):val1,
  28027. val3 = ptrs<ptrsmax?(double)*(ptrs + 2):val2,
  28028. val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) +
  28029. t*t*t*(-val0 + 3*val1 - 3*val2 + val3));
  28030. *(ptrd++) = (T)(val<vmin?vmin:val>vmax?vmax:val);
  28031. ptrs+=*(poff++);
  28032. }
  28033. }
  28034. }
  28035. }
  28036. } else resx.assign(*this,true);
  28037. if (sy!=_height) {
  28038. if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
  28039. else {
  28040. if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy);
  28041. else {
  28042. const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.0)/(sy - 1):0):
  28043. (double)_height/sy;
  28044. resy.assign(sx,sy,_depth,_spectrum);
  28045. curr = old = 0;
  28046. unsigned int *poff = off._data;
  28047. double *pfoff = foff._data;
  28048. cimg_forY(resy,y) {
  28049. *(pfoff++) = curr - (unsigned int)curr;
  28050. old = curr;
  28051. curr = std::min(height() - 1.0,curr + fy);
  28052. *(poff++) = sx*((unsigned int)curr - (unsigned int)old);
  28053. }
  28054. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resy.size()>=65536))
  28055. cimg_forXZC(resy,x,z,c) {
  28056. const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_height - 2)*sx;
  28057. T *ptrd = resy.data(x,0,z,c);
  28058. const unsigned int *poff = off._data;
  28059. const double *pfoff = foff._data;
  28060. cimg_forY(resy,y) {
  28061. const double
  28062. t = *(pfoff++),
  28063. val1 = (double)*ptrs,
  28064. val0 = ptrs>ptrs0?(double)*(ptrs - sx):val1,
  28065. val2 = ptrs<=ptrsmax?(double)*(ptrs + sx):val1,
  28066. val3 = ptrs<ptrsmax?(double)*(ptrs + 2*sx):val2,
  28067. val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) +
  28068. t*t*t*(-val0 + 3*val1 - 3*val2 + val3));
  28069. *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
  28070. ptrd+=sx;
  28071. ptrs+=*(poff++);
  28072. }
  28073. }
  28074. }
  28075. }
  28076. resx.assign();
  28077. } else resy.assign(resx,true);
  28078. if (sz!=_depth) {
  28079. if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
  28080. else {
  28081. if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz);
  28082. else {
  28083. const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.0)/(sz - 1):0):
  28084. (double)_depth/sz;
  28085. const unsigned int sxy = sx*sy;
  28086. resz.assign(sx,sy,sz,_spectrum);
  28087. curr = old = 0;
  28088. unsigned int *poff = off._data;
  28089. double *pfoff = foff._data;
  28090. cimg_forZ(resz,z) {
  28091. *(pfoff++) = curr - (unsigned int)curr;
  28092. old = curr;
  28093. curr = std::min(depth() - 1.0,curr + fz);
  28094. *(poff++) = sxy*((unsigned int)curr - (unsigned int)old);
  28095. }
  28096. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resz.size()>=65536))
  28097. cimg_forXYC(resz,x,y,c) {
  28098. const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_depth - 2)*sxy;
  28099. T *ptrd = resz.data(x,y,0,c);
  28100. const unsigned int *poff = off._data;
  28101. const double *pfoff = foff._data;
  28102. cimg_forZ(resz,z) {
  28103. const double
  28104. t = *(pfoff++),
  28105. val1 = (double)*ptrs,
  28106. val0 = ptrs>ptrs0?(double)*(ptrs - sxy):val1,
  28107. val2 = ptrs<=ptrsmax?(double)*(ptrs + sxy):val1,
  28108. val3 = ptrs<ptrsmax?(double)*(ptrs + 2*sxy):val2,
  28109. val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) +
  28110. t*t*t*(-val0 + 3*val1 - 3*val2 + val3));
  28111. *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
  28112. ptrd+=sxy;
  28113. ptrs+=*(poff++);
  28114. }
  28115. }
  28116. }
  28117. }
  28118. resy.assign();
  28119. } else resz.assign(resy,true);
  28120. if (sc!=_spectrum) {
  28121. if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
  28122. else {
  28123. if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc);
  28124. else {
  28125. const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.0)/(sc - 1):0):
  28126. (double)_spectrum/sc;
  28127. const unsigned int sxyz = sx*sy*sz;
  28128. resc.assign(sx,sy,sz,sc);
  28129. curr = old = 0;
  28130. unsigned int *poff = off._data;
  28131. double *pfoff = foff._data;
  28132. cimg_forC(resc,c) {
  28133. *(pfoff++) = curr - (unsigned int)curr;
  28134. old = curr;
  28135. curr = std::min(spectrum() - 1.0,curr + fc);
  28136. *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old);
  28137. }
  28138. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resc.size()>=65536))
  28139. cimg_forXYZ(resc,x,y,z) {
  28140. const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmax = ptrs + (_spectrum - 2)*sxyz;
  28141. T *ptrd = resc.data(x,y,z,0);
  28142. const unsigned int *poff = off._data;
  28143. const double *pfoff = foff._data;
  28144. cimg_forC(resc,c) {
  28145. const double
  28146. t = *(pfoff++),
  28147. val1 = (double)*ptrs,
  28148. val0 = ptrs>ptrs0?(double)*(ptrs - sxyz):val1,
  28149. val2 = ptrs<=ptrsmax?(double)*(ptrs + sxyz):val1,
  28150. val3 = ptrs<ptrsmax?(double)*(ptrs + 2*sxyz):val2,
  28151. val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) +
  28152. t*t*t*(-val0 + 3*val1 - 3*val2 + val3));
  28153. *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
  28154. ptrd+=sxyz;
  28155. ptrs+=*(poff++);
  28156. }
  28157. }
  28158. }
  28159. }
  28160. resz.assign();
  28161. } else resc.assign(resz,true);
  28162. return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
  28163. } break;
  28164. // Lanczos interpolation.
  28165. //
  28166. case 6 : {
  28167. const double vmin = (double)cimg::type<T>::min(), vmax = (double)cimg::type<T>::max();
  28168. CImg<uintT> off(cimg::max(sx,sy,sz,sc));
  28169. CImg<doubleT> foff(off._width);
  28170. CImg<T> resx, resy, resz, resc;
  28171. double curr, old;
  28172. if (sx!=_width) {
  28173. if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
  28174. else {
  28175. if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx);
  28176. else {
  28177. const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.0)/(sx - 1):0):
  28178. (double)_width/sx;
  28179. resx.assign(sx,_height,_depth,_spectrum);
  28180. curr = old = 0;
  28181. unsigned int *poff = off._data;
  28182. double *pfoff = foff._data;
  28183. cimg_forX(resx,x) {
  28184. *(pfoff++) = curr - (unsigned int)curr;
  28185. old = curr;
  28186. curr = std::min(width() - 1.0,curr + fx);
  28187. *(poff++) = (unsigned int)curr - (unsigned int)old;
  28188. }
  28189. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resx.size()>=65536))
  28190. cimg_forYZC(resx,y,z,c) {
  28191. const T *const ptrs0 = data(0,y,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + 1,
  28192. *const ptrsmax = ptrs0 + (_width - 2);
  28193. T *ptrd = resx.data(0,y,z,c);
  28194. const unsigned int *poff = off._data;
  28195. const double *pfoff = foff._data;
  28196. cimg_forX(resx,x) {
  28197. const double
  28198. t = *(pfoff++),
  28199. w0 = _cimg_lanczos(t + 2),
  28200. w1 = _cimg_lanczos(t + 1),
  28201. w2 = _cimg_lanczos(t),
  28202. w3 = _cimg_lanczos(t - 1),
  28203. w4 = _cimg_lanczos(t - 2),
  28204. val2 = (double)*ptrs,
  28205. val1 = ptrs>=ptrsmin?(double)*(ptrs - 1):val2,
  28206. val0 = ptrs>ptrsmin?(double)*(ptrs - 2):val1,
  28207. val3 = ptrs<=ptrsmax?(double)*(ptrs + 1):val2,
  28208. val4 = ptrs<ptrsmax?(double)*(ptrs + 2):val3,
  28209. val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
  28210. *(ptrd++) = (T)(val<vmin?vmin:val>vmax?vmax:val);
  28211. ptrs+=*(poff++);
  28212. }
  28213. }
  28214. }
  28215. }
  28216. } else resx.assign(*this,true);
  28217. if (sy!=_height) {
  28218. if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
  28219. else {
  28220. if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy);
  28221. else {
  28222. const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.0)/(sy - 1):0):
  28223. (double)_height/sy;
  28224. resy.assign(sx,sy,_depth,_spectrum);
  28225. curr = old = 0;
  28226. unsigned int *poff = off._data;
  28227. double *pfoff = foff._data;
  28228. cimg_forY(resy,y) {
  28229. *(pfoff++) = curr - (unsigned int)curr;
  28230. old = curr;
  28231. curr = std::min(height() - 1.0,curr + fy);
  28232. *(poff++) = sx*((unsigned int)curr - (unsigned int)old);
  28233. }
  28234. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resy.size()>=65536))
  28235. cimg_forXZC(resy,x,z,c) {
  28236. const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sx,
  28237. *const ptrsmax = ptrs0 + (_height - 2)*sx;
  28238. T *ptrd = resy.data(x,0,z,c);
  28239. const unsigned int *poff = off._data;
  28240. const double *pfoff = foff._data;
  28241. cimg_forY(resy,y) {
  28242. const double
  28243. t = *(pfoff++),
  28244. w0 = _cimg_lanczos(t + 2),
  28245. w1 = _cimg_lanczos(t + 1),
  28246. w2 = _cimg_lanczos(t),
  28247. w3 = _cimg_lanczos(t - 1),
  28248. w4 = _cimg_lanczos(t - 2),
  28249. val2 = (double)*ptrs,
  28250. val1 = ptrs>=ptrsmin?(double)*(ptrs - sx):val2,
  28251. val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sx):val1,
  28252. val3 = ptrs<=ptrsmax?(double)*(ptrs + sx):val2,
  28253. val4 = ptrs<ptrsmax?(double)*(ptrs + 2*sx):val3,
  28254. val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
  28255. *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
  28256. ptrd+=sx;
  28257. ptrs+=*(poff++);
  28258. }
  28259. }
  28260. }
  28261. }
  28262. resx.assign();
  28263. } else resy.assign(resx,true);
  28264. if (sz!=_depth) {
  28265. if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
  28266. else {
  28267. if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz);
  28268. else {
  28269. const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.0)/(sz - 1):0):
  28270. (double)_depth/sz;
  28271. const unsigned int sxy = sx*sy;
  28272. resz.assign(sx,sy,sz,_spectrum);
  28273. curr = old = 0;
  28274. unsigned int *poff = off._data;
  28275. double *pfoff = foff._data;
  28276. cimg_forZ(resz,z) {
  28277. *(pfoff++) = curr - (unsigned int)curr;
  28278. old = curr;
  28279. curr = std::min(depth() - 1.0,curr + fz);
  28280. *(poff++) = sxy*((unsigned int)curr - (unsigned int)old);
  28281. }
  28282. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resz.size()>=65536))
  28283. cimg_forXYC(resz,x,y,c) {
  28284. const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxy,
  28285. *const ptrsmax = ptrs0 + (_depth - 2)*sxy;
  28286. T *ptrd = resz.data(x,y,0,c);
  28287. const unsigned int *poff = off._data;
  28288. const double *pfoff = foff._data;
  28289. cimg_forZ(resz,z) {
  28290. const double
  28291. t = *(pfoff++),
  28292. w0 = _cimg_lanczos(t + 2),
  28293. w1 = _cimg_lanczos(t + 1),
  28294. w2 = _cimg_lanczos(t),
  28295. w3 = _cimg_lanczos(t - 1),
  28296. w4 = _cimg_lanczos(t - 2),
  28297. val2 = (double)*ptrs,
  28298. val1 = ptrs>=ptrsmin?(double)*(ptrs - sxy):val2,
  28299. val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sxy):val1,
  28300. val3 = ptrs<=ptrsmax?(double)*(ptrs + sxy):val2,
  28301. val4 = ptrs<ptrsmax?(double)*(ptrs + 2*sxy):val3,
  28302. val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
  28303. *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
  28304. ptrd+=sxy;
  28305. ptrs+=*(poff++);
  28306. }
  28307. }
  28308. }
  28309. }
  28310. resy.assign();
  28311. } else resz.assign(resy,true);
  28312. if (sc!=_spectrum) {
  28313. if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
  28314. else {
  28315. if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc);
  28316. else {
  28317. const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.0)/(sc - 1):0):
  28318. (double)_spectrum/sc;
  28319. const unsigned int sxyz = sx*sy*sz;
  28320. resc.assign(sx,sy,sz,sc);
  28321. curr = old = 0;
  28322. unsigned int *poff = off._data;
  28323. double *pfoff = foff._data;
  28324. cimg_forC(resc,c) {
  28325. *(pfoff++) = curr - (unsigned int)curr;
  28326. old = curr;
  28327. curr = std::min(spectrum() - 1.0,curr + fc);
  28328. *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old);
  28329. }
  28330. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resc.size()>=65536))
  28331. cimg_forXYZ(resc,x,y,z) {
  28332. const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxyz,
  28333. *const ptrsmax = ptrs + (_spectrum - 2)*sxyz;
  28334. T *ptrd = resc.data(x,y,z,0);
  28335. const unsigned int *poff = off._data;
  28336. const double *pfoff = foff._data;
  28337. cimg_forC(resc,c) {
  28338. const double
  28339. t = *(pfoff++),
  28340. w0 = _cimg_lanczos(t + 2),
  28341. w1 = _cimg_lanczos(t + 1),
  28342. w2 = _cimg_lanczos(t),
  28343. w3 = _cimg_lanczos(t - 1),
  28344. w4 = _cimg_lanczos(t - 2),
  28345. val2 = (double)*ptrs,
  28346. val1 = ptrs>=ptrsmin?(double)*(ptrs - sxyz):val2,
  28347. val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sxyz):val1,
  28348. val3 = ptrs<=ptrsmax?(double)*(ptrs + sxyz):val2,
  28349. val4 = ptrs<ptrsmax?(double)*(ptrs + 2*sxyz):val3,
  28350. val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
  28351. *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
  28352. ptrd+=sxyz;
  28353. ptrs+=*(poff++);
  28354. }
  28355. }
  28356. }
  28357. }
  28358. resz.assign();
  28359. } else resc.assign(resz,true);
  28360. return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
  28361. } break;
  28362. // Unknow interpolation.
  28363. //
  28364. default :
  28365. throw CImgArgumentException(_cimg_instance
  28366. "resize(): Invalid specified interpolation %d "
  28367. "(should be { -1=raw | 0=none | 1=nearest | 2=average | 3=linear | 4=grid | "
  28368. "5=cubic | 6=lanczos }).",
  28369. cimg_instance,
  28370. interpolation_type);
  28371. }
  28372. return res;
  28373. }
  28374. //! Resize image to dimensions of another image.
  28375. /**
  28376. \param src Reference image used for dimensions.
  28377. \param interpolation_type Interpolation method.
  28378. \param boundary_conditions Boundary conditions.
  28379. \param centering_x Set centering type (only if \p interpolation_type=0).
  28380. \param centering_y Set centering type (only if \p interpolation_type=0).
  28381. \param centering_z Set centering type (only if \p interpolation_type=0).
  28382. \param centering_c Set centering type (only if \p interpolation_type=0).
  28383. **/
  28384. template<typename t>
  28385. CImg<T>& resize(const CImg<t>& src,
  28386. const int interpolation_type=1, const unsigned int boundary_conditions=0,
  28387. const float centering_x = 0, const float centering_y = 0,
  28388. const float centering_z = 0, const float centering_c = 0) {
  28389. return resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,boundary_conditions,
  28390. centering_x,centering_y,centering_z,centering_c);
  28391. }
  28392. //! Resize image to dimensions of another image \newinstance.
  28393. template<typename t>
  28394. CImg<T> get_resize(const CImg<t>& src,
  28395. const int interpolation_type=1, const unsigned int boundary_conditions=0,
  28396. const float centering_x = 0, const float centering_y = 0,
  28397. const float centering_z = 0, const float centering_c = 0) const {
  28398. return get_resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,boundary_conditions,
  28399. centering_x,centering_y,centering_z,centering_c);
  28400. }
  28401. //! Resize image to dimensions of a display window.
  28402. /**
  28403. \param disp Reference display window used for dimensions.
  28404. \param interpolation_type Interpolation method.
  28405. \param boundary_conditions Boundary conditions.
  28406. \param centering_x Set centering type (only if \p interpolation_type=0).
  28407. \param centering_y Set centering type (only if \p interpolation_type=0).
  28408. \param centering_z Set centering type (only if \p interpolation_type=0).
  28409. \param centering_c Set centering type (only if \p interpolation_type=0).
  28410. **/
  28411. CImg<T>& resize(const CImgDisplay& disp,
  28412. const int interpolation_type=1, const unsigned int boundary_conditions=0,
  28413. const float centering_x = 0, const float centering_y = 0,
  28414. const float centering_z = 0, const float centering_c = 0) {
  28415. return resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,boundary_conditions,
  28416. centering_x,centering_y,centering_z,centering_c);
  28417. }
  28418. //! Resize image to dimensions of a display window \newinstance.
  28419. CImg<T> get_resize(const CImgDisplay& disp,
  28420. const int interpolation_type=1, const unsigned int boundary_conditions=0,
  28421. const float centering_x = 0, const float centering_y = 0,
  28422. const float centering_z = 0, const float centering_c = 0) const {
  28423. return get_resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,boundary_conditions,
  28424. centering_x,centering_y,centering_z,centering_c);
  28425. }
  28426. //! Resize image to half-size along XY axes, using an optimized filter.
  28427. CImg<T>& resize_halfXY() {
  28428. return get_resize_halfXY().move_to(*this);
  28429. }
  28430. //! Resize image to half-size along XY axes, using an optimized filter \newinstance.
  28431. CImg<T> get_resize_halfXY() const {
  28432. if (is_empty()) return *this;
  28433. static const Tfloat kernel[9] = { 0.07842776544f, 0.1231940459f, 0.07842776544f,
  28434. 0.1231940459f, 0.1935127547f, 0.1231940459f,
  28435. 0.07842776544f, 0.1231940459f, 0.07842776544f };
  28436. CImg<T> I(9), res(_width/2,_height/2,_depth,_spectrum);
  28437. T *ptrd = res._data;
  28438. cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,T)
  28439. if (x%2 && y%2) *(ptrd++) = (T)
  28440. (I[0]*kernel[0] + I[1]*kernel[1] + I[2]*kernel[2] +
  28441. I[3]*kernel[3] + I[4]*kernel[4] + I[5]*kernel[5] +
  28442. I[6]*kernel[6] + I[7]*kernel[7] + I[8]*kernel[8]);
  28443. return res;
  28444. }
  28445. //! Resize image to double-size, using the Scale2X algorithm.
  28446. /**
  28447. \note Use anisotropic upscaling algorithm
  28448. <a href="http://scale2x.sourceforge.net/algorithm.html">described here</a>.
  28449. **/
  28450. CImg<T>& resize_doubleXY() {
  28451. return get_resize_doubleXY().move_to(*this);
  28452. }
  28453. //! Resize image to double-size, using the Scale2X algorithm \newinstance.
  28454. CImg<T> get_resize_doubleXY() const {
  28455. #define _cimg_gs2x_for3(bound,i) \
  28456. for (int i = 0, _p1##i = 0, \
  28457. _n1##i = 1>=(bound)?(int)(bound) - 1:1; \
  28458. _n1##i<(int)(bound) || i==--_n1##i; \
  28459. _p1##i = i++, ++_n1##i, ptrd1+=(res)._width, ptrd2+=(res)._width)
  28460. #define _cimg_gs2x_for3x3(img,x,y,z,c,I,T) \
  28461. _cimg_gs2x_for3((img)._height,y) for (int x = 0, \
  28462. _p1##x = 0, \
  28463. _n1##x = (int)( \
  28464. (I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
  28465. (I[3] = I[4] = (T)(img)(0,y,z,c)), \
  28466. (I[7] = (T)(img)(0,_n1##y,z,c)), \
  28467. 1>=(img)._width?(img).width() - 1:1); \
  28468. (_n1##x<(img).width() && ( \
  28469. (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
  28470. (I[5] = (T)(img)(_n1##x,y,z,c)), \
  28471. (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
  28472. x==--_n1##x; \
  28473. I[1] = I[2], \
  28474. I[3] = I[4], I[4] = I[5], \
  28475. I[7] = I[8], \
  28476. _p1##x = x++, ++_n1##x)
  28477. if (is_empty()) return *this;
  28478. CImg<T> res(_width<<1,_height<<1,_depth,_spectrum);
  28479. CImg_3x3(I,T);
  28480. cimg_forZC(*this,z,c) {
  28481. T
  28482. *ptrd1 = res.data(0,0,z,c),
  28483. *ptrd2 = ptrd1 + res._width;
  28484. _cimg_gs2x_for3x3(*this,x,y,z,c,I,T) {
  28485. if (Icp!=Icn && Ipc!=Inc) {
  28486. *(ptrd1++) = Ipc==Icp?Ipc:Icc;
  28487. *(ptrd1++) = Icp==Inc?Inc:Icc;
  28488. *(ptrd2++) = Ipc==Icn?Ipc:Icc;
  28489. *(ptrd2++) = Icn==Inc?Inc:Icc;
  28490. } else { *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc; }
  28491. }
  28492. }
  28493. return res;
  28494. }
  28495. //! Resize image to triple-size, using the Scale3X algorithm.
  28496. /**
  28497. \note Use anisotropic upscaling algorithm
  28498. <a href="http://scale2x.sourceforge.net/algorithm.html">described here</a>.
  28499. **/
  28500. CImg<T>& resize_tripleXY() {
  28501. return get_resize_tripleXY().move_to(*this);
  28502. }
  28503. //! Resize image to triple-size, using the Scale3X algorithm \newinstance.
  28504. CImg<T> get_resize_tripleXY() const {
  28505. #define _cimg_gs3x_for3(bound,i) \
  28506. for (int i = 0, _p1##i = 0, \
  28507. _n1##i = 1>=(bound)?(int)(bound) - 1:1; \
  28508. _n1##i<(int)(bound) || i==--_n1##i; \
  28509. _p1##i = i++, ++_n1##i, ptrd1+=2*(res)._width, ptrd2+=2*(res)._width, ptrd3+=2*(res)._width)
  28510. #define _cimg_gs3x_for3x3(img,x,y,z,c,I,T) \
  28511. _cimg_gs3x_for3((img)._height,y) for (int x = 0, \
  28512. _p1##x = 0, \
  28513. _n1##x = (int)( \
  28514. (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
  28515. (I[3] = I[4] = (T)(img)(0,y,z,c)), \
  28516. (I[6] = I[7] = (T)(img)(0,_n1##y,z,c)), \
  28517. 1>=(img)._width?(img).width() - 1:1); \
  28518. (_n1##x<(img).width() && ( \
  28519. (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
  28520. (I[5] = (T)(img)(_n1##x,y,z,c)), \
  28521. (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
  28522. x==--_n1##x; \
  28523. I[0] = I[1], I[1] = I[2], \
  28524. I[3] = I[4], I[4] = I[5], \
  28525. I[6] = I[7], I[7] = I[8], \
  28526. _p1##x = x++, ++_n1##x)
  28527. if (is_empty()) return *this;
  28528. CImg<T> res(3*_width,3*_height,_depth,_spectrum);
  28529. CImg_3x3(I,T);
  28530. cimg_forZC(*this,z,c) {
  28531. T
  28532. *ptrd1 = res.data(0,0,z,c),
  28533. *ptrd2 = ptrd1 + res._width,
  28534. *ptrd3 = ptrd2 + res._width;
  28535. _cimg_gs3x_for3x3(*this,x,y,z,c,I,T) {
  28536. if (Icp != Icn && Ipc != Inc) {
  28537. *(ptrd1++) = Ipc==Icp?Ipc:Icc;
  28538. *(ptrd1++) = (Ipc==Icp && Icc!=Inp) || (Icp==Inc && Icc!=Ipp)?Icp:Icc;
  28539. *(ptrd1++) = Icp==Inc?Inc:Icc;
  28540. *(ptrd2++) = (Ipc==Icp && Icc!=Ipn) || (Ipc==Icn && Icc!=Ipp)?Ipc:Icc;
  28541. *(ptrd2++) = Icc;
  28542. *(ptrd2++) = (Icp==Inc && Icc!=Inn) || (Icn==Inc && Icc!=Inp)?Inc:Icc;
  28543. *(ptrd3++) = Ipc==Icn?Ipc:Icc;
  28544. *(ptrd3++) = (Ipc==Icn && Icc!=Inn) || (Icn==Inc && Icc!=Ipn)?Icn:Icc;
  28545. *(ptrd3++) = Icn==Inc?Inc:Icc;
  28546. } else {
  28547. *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd1++) = Icc;
  28548. *(ptrd2++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc;
  28549. *(ptrd3++) = Icc; *(ptrd3++) = Icc; *(ptrd3++) = Icc;
  28550. }
  28551. }
  28552. }
  28553. return res;
  28554. }
  28555. //! Mirror image content along specified axis.
  28556. /**
  28557. \param axis Mirror axis
  28558. **/
  28559. CImg<T>& mirror(const char axis) {
  28560. if (is_empty()) return *this;
  28561. T *pf, *pb, *buf = 0;
  28562. switch (cimg::lowercase(axis)) {
  28563. case 'x' : {
  28564. pf = _data; pb = data(_width - 1);
  28565. const unsigned int width2 = _width/2;
  28566. for (unsigned int yzv = 0; yzv<_height*_depth*_spectrum; ++yzv) {
  28567. for (unsigned int x = 0; x<width2; ++x) { const T val = *pf; *(pf++) = *pb; *(pb--) = val; }
  28568. pf+=_width - width2;
  28569. pb+=_width + width2;
  28570. }
  28571. } break;
  28572. case 'y' : {
  28573. buf = new T[_width];
  28574. pf = _data; pb = data(0,_height - 1);
  28575. const unsigned int height2 = _height/2;
  28576. for (unsigned int zv = 0; zv<_depth*_spectrum; ++zv) {
  28577. for (unsigned int y = 0; y<height2; ++y) {
  28578. std::memcpy(buf,pf,_width*sizeof(T));
  28579. std::memcpy(pf,pb,_width*sizeof(T));
  28580. std::memcpy(pb,buf,_width*sizeof(T));
  28581. pf+=_width;
  28582. pb-=_width;
  28583. }
  28584. pf+=(ulongT)_width*(_height - height2);
  28585. pb+=(ulongT)_width*(_height + height2);
  28586. }
  28587. } break;
  28588. case 'z' : {
  28589. buf = new T[(ulongT)_width*_height];
  28590. pf = _data; pb = data(0,0,_depth - 1);
  28591. const unsigned int depth2 = _depth/2;
  28592. cimg_forC(*this,c) {
  28593. for (unsigned int z = 0; z<depth2; ++z) {
  28594. std::memcpy(buf,pf,_width*_height*sizeof(T));
  28595. std::memcpy(pf,pb,_width*_height*sizeof(T));
  28596. std::memcpy(pb,buf,_width*_height*sizeof(T));
  28597. pf+=(ulongT)_width*_height;
  28598. pb-=(ulongT)_width*_height;
  28599. }
  28600. pf+=(ulongT)_width*_height*(_depth - depth2);
  28601. pb+=(ulongT)_width*_height*(_depth + depth2);
  28602. }
  28603. } break;
  28604. case 'c' : {
  28605. buf = new T[(ulongT)_width*_height*_depth];
  28606. pf = _data; pb = data(0,0,0,_spectrum - 1);
  28607. const unsigned int _spectrum2 = _spectrum/2;
  28608. for (unsigned int v = 0; v<_spectrum2; ++v) {
  28609. std::memcpy(buf,pf,_width*_height*_depth*sizeof(T));
  28610. std::memcpy(pf,pb,_width*_height*_depth*sizeof(T));
  28611. std::memcpy(pb,buf,_width*_height*_depth*sizeof(T));
  28612. pf+=(ulongT)_width*_height*_depth;
  28613. pb-=(ulongT)_width*_height*_depth;
  28614. }
  28615. } break;
  28616. default :
  28617. throw CImgArgumentException(_cimg_instance
  28618. "mirror(): Invalid specified axis '%c'.",
  28619. cimg_instance,
  28620. axis);
  28621. }
  28622. delete[] buf;
  28623. return *this;
  28624. }
  28625. //! Mirror image content along specified axis \newinstance.
  28626. CImg<T> get_mirror(const char axis) const {
  28627. return (+*this).mirror(axis);
  28628. }
  28629. //! Mirror image content along specified axes.
  28630. /**
  28631. \param axes Mirror axes, as a C-string.
  28632. \note \c axes may contains multiple characters, e.g. \c "xyz"
  28633. **/
  28634. CImg<T>& mirror(const char *const axes) {
  28635. for (const char *s = axes; *s; ++s) mirror(*s);
  28636. return *this;
  28637. }
  28638. //! Mirror image content along specified axes \newinstance.
  28639. CImg<T> get_mirror(const char *const axes) const {
  28640. return (+*this).mirror(axes);
  28641. }
  28642. //! Shift image content.
  28643. /**
  28644. \param delta_x Amount of displacement along the X-axis.
  28645. \param delta_y Amount of displacement along the Y-axis.
  28646. \param delta_z Amount of displacement along the Z-axis.
  28647. \param delta_c Amount of displacement along the C-axis.
  28648. \param boundary_conditions Border condition. Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
  28649. **/
  28650. CImg<T>& shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0,
  28651. const unsigned int boundary_conditions=0) {
  28652. if (is_empty()) return *this;
  28653. if (boundary_conditions==3)
  28654. return get_crop(-delta_x,-delta_y,-delta_z,-delta_c,
  28655. width() - delta_x - 1,
  28656. height() - delta_y - 1,
  28657. depth() - delta_z - 1,
  28658. spectrum() - delta_c - 1,3).move_to(*this);
  28659. if (delta_x) // Shift along X-axis
  28660. switch (boundary_conditions) {
  28661. case 2 : { // Periodic
  28662. const int ml = cimg::mod(-delta_x,width()), ndelta_x = (ml<=width()/2)?ml:(ml-width());
  28663. if (!ndelta_x) return *this;
  28664. CImg<T> buf(cimg::abs(ndelta_x));
  28665. if (ndelta_x>0) cimg_forYZC(*this,y,z,c) {
  28666. std::memcpy(buf,data(0,y,z,c),ndelta_x*sizeof(T));
  28667. std::memmove(data(0,y,z,c),data(ndelta_x,y,z,c),(_width-ndelta_x)*sizeof(T));
  28668. std::memcpy(data(_width-ndelta_x,y,z,c),buf,ndelta_x*sizeof(T));
  28669. } else cimg_forYZC(*this,y,z,c) {
  28670. std::memcpy(buf,data(_width + ndelta_x,y,z,c),-ndelta_x*sizeof(T));
  28671. std::memmove(data(-ndelta_x,y,z,c),data(0,y,z,c),(_width + ndelta_x)*sizeof(T));
  28672. std::memcpy(data(0,y,z,c),buf,-ndelta_x*sizeof(T));
  28673. }
  28674. } break;
  28675. case 1 : // Neumann
  28676. if (delta_x<0) {
  28677. const int ndelta_x = (-delta_x>=width())?width() - 1:-delta_x;
  28678. if (!ndelta_x) return *this;
  28679. cimg_forYZC(*this,y,z,c) {
  28680. std::memmove(data(0,y,z,c),data(ndelta_x,y,z,c),(_width-ndelta_x)*sizeof(T));
  28681. T *ptrd = data(_width - 1,y,z,c);
  28682. const T val = *ptrd;
  28683. for (int l = 0; l<ndelta_x - 1; ++l) *(--ptrd) = val;
  28684. }
  28685. } else {
  28686. const int ndelta_x = (delta_x>=width())?width() - 1:delta_x;
  28687. if (!ndelta_x) return *this;
  28688. cimg_forYZC(*this,y,z,c) {
  28689. std::memmove(data(ndelta_x,y,z,c),data(0,y,z,c),(_width-ndelta_x)*sizeof(T));
  28690. T *ptrd = data(0,y,z,c);
  28691. const T val = *ptrd;
  28692. for (int l = 0; l<ndelta_x - 1; ++l) *(++ptrd) = val;
  28693. }
  28694. }
  28695. break;
  28696. default : // Dirichlet
  28697. if (delta_x<=-width() || delta_x>=width()) return fill((T)0);
  28698. if (delta_x<0) cimg_forYZC(*this,y,z,c) {
  28699. std::memmove(data(0,y,z,c),data(-delta_x,y,z,c),(_width + delta_x)*sizeof(T));
  28700. std::memset(data(_width + delta_x,y,z,c),0,-delta_x*sizeof(T));
  28701. } else cimg_forYZC(*this,y,z,c) {
  28702. std::memmove(data(delta_x,y,z,c),data(0,y,z,c),(_width-delta_x)*sizeof(T));
  28703. std::memset(data(0,y,z,c),0,delta_x*sizeof(T));
  28704. }
  28705. }
  28706. if (delta_y) // Shift along Y-axis
  28707. switch (boundary_conditions) {
  28708. case 2 : { // Periodic
  28709. const int ml = cimg::mod(-delta_y,height()), ndelta_y = (ml<=height()/2)?ml:(ml-height());
  28710. if (!ndelta_y) return *this;
  28711. CImg<T> buf(width(),cimg::abs(ndelta_y));
  28712. if (ndelta_y>0) cimg_forZC(*this,z,c) {
  28713. std::memcpy(buf,data(0,0,z,c),_width*ndelta_y*sizeof(T));
  28714. std::memmove(data(0,0,z,c),data(0,ndelta_y,z,c),_width*(_height-ndelta_y)*sizeof(T));
  28715. std::memcpy(data(0,_height-ndelta_y,z,c),buf,_width*ndelta_y*sizeof(T));
  28716. } else cimg_forZC(*this,z,c) {
  28717. std::memcpy(buf,data(0,_height + ndelta_y,z,c),-ndelta_y*_width*sizeof(T));
  28718. std::memmove(data(0,-ndelta_y,z,c),data(0,0,z,c),_width*(_height + ndelta_y)*sizeof(T));
  28719. std::memcpy(data(0,0,z,c),buf,-ndelta_y*_width*sizeof(T));
  28720. }
  28721. } break;
  28722. case 1 : // Neumann
  28723. if (delta_y<0) {
  28724. const int ndelta_y = (-delta_y>=height())?height() - 1:-delta_y;
  28725. if (!ndelta_y) return *this;
  28726. cimg_forZC(*this,z,c) {
  28727. std::memmove(data(0,0,z,c),data(0,ndelta_y,z,c),_width*(_height-ndelta_y)*sizeof(T));
  28728. T *ptrd = data(0,_height-ndelta_y,z,c), *ptrs = data(0,_height - 1,z,c);
  28729. for (int l = 0; l<ndelta_y - 1; ++l) { std::memcpy(ptrd,ptrs,_width*sizeof(T)); ptrd+=_width; }
  28730. }
  28731. } else {
  28732. const int ndelta_y = (delta_y>=height())?height() - 1:delta_y;
  28733. if (!ndelta_y) return *this;
  28734. cimg_forZC(*this,z,c) {
  28735. std::memmove(data(0,ndelta_y,z,c),data(0,0,z,c),_width*(_height-ndelta_y)*sizeof(T));
  28736. T *ptrd = data(0,1,z,c), *ptrs = data(0,0,z,c);
  28737. for (int l = 0; l<ndelta_y - 1; ++l) { std::memcpy(ptrd,ptrs,_width*sizeof(T)); ptrd+=_width; }
  28738. }
  28739. }
  28740. break;
  28741. default : // Dirichlet
  28742. if (delta_y<=-height() || delta_y>=height()) return fill((T)0);
  28743. if (delta_y<0) cimg_forZC(*this,z,c) {
  28744. std::memmove(data(0,0,z,c),data(0,-delta_y,z,c),_width*(_height + delta_y)*sizeof(T));
  28745. std::memset(data(0,_height + delta_y,z,c),0,-delta_y*_width*sizeof(T));
  28746. } else cimg_forZC(*this,z,c) {
  28747. std::memmove(data(0,delta_y,z,c),data(0,0,z,c),_width*(_height-delta_y)*sizeof(T));
  28748. std::memset(data(0,0,z,c),0,delta_y*_width*sizeof(T));
  28749. }
  28750. }
  28751. if (delta_z) // Shift along Z-axis
  28752. switch (boundary_conditions) {
  28753. case 2 : { // Periodic
  28754. const int ml = cimg::mod(-delta_z,depth()), ndelta_z = (ml<=depth()/2)?ml:(ml-depth());
  28755. if (!ndelta_z) return *this;
  28756. CImg<T> buf(width(),height(),cimg::abs(ndelta_z));
  28757. if (ndelta_z>0) cimg_forC(*this,c) {
  28758. std::memcpy(buf,data(0,0,0,c),_width*_height*ndelta_z*sizeof(T));
  28759. std::memmove(data(0,0,0,c),data(0,0,ndelta_z,c),_width*_height*(_depth-ndelta_z)*sizeof(T));
  28760. std::memcpy(data(0,0,_depth-ndelta_z,c),buf,_width*_height*ndelta_z*sizeof(T));
  28761. } else cimg_forC(*this,c) {
  28762. std::memcpy(buf,data(0,0,_depth + ndelta_z,c),-ndelta_z*_width*_height*sizeof(T));
  28763. std::memmove(data(0,0,-ndelta_z,c),data(0,0,0,c),_width*_height*(_depth + ndelta_z)*sizeof(T));
  28764. std::memcpy(data(0,0,0,c),buf,-ndelta_z*_width*_height*sizeof(T));
  28765. }
  28766. } break;
  28767. case 1 : // Neumann
  28768. if (delta_z<0) {
  28769. const int ndelta_z = (-delta_z>=depth())?depth() - 1:-delta_z;
  28770. if (!ndelta_z) return *this;
  28771. cimg_forC(*this,c) {
  28772. std::memmove(data(0,0,0,c),data(0,0,ndelta_z,c),_width*_height*(_depth-ndelta_z)*sizeof(T));
  28773. T *ptrd = data(0,0,_depth-ndelta_z,c), *ptrs = data(0,0,_depth - 1,c);
  28774. for (int l = 0; l<ndelta_z - 1; ++l) {
  28775. std::memcpy(ptrd,ptrs,_width*_height*sizeof(T)); ptrd+=(ulongT)_width*_height;
  28776. }
  28777. }
  28778. } else {
  28779. const int ndelta_z = (delta_z>=depth())?depth() - 1:delta_z;
  28780. if (!ndelta_z) return *this;
  28781. cimg_forC(*this,c) {
  28782. std::memmove(data(0,0,ndelta_z,c),data(0,0,0,c),_width*_height*(_depth-ndelta_z)*sizeof(T));
  28783. T *ptrd = data(0,0,1,c), *ptrs = data(0,0,0,c);
  28784. for (int l = 0; l<ndelta_z - 1; ++l) {
  28785. std::memcpy(ptrd,ptrs,_width*_height*sizeof(T)); ptrd+=(ulongT)_width*_height;
  28786. }
  28787. }
  28788. }
  28789. break;
  28790. default : // Dirichlet
  28791. if (delta_z<=-depth() || delta_z>=depth()) return fill((T)0);
  28792. if (delta_z<0) cimg_forC(*this,c) {
  28793. std::memmove(data(0,0,0,c),data(0,0,-delta_z,c),_width*_height*(_depth + delta_z)*sizeof(T));
  28794. std::memset(data(0,0,_depth + delta_z,c),0,_width*_height*(-delta_z)*sizeof(T));
  28795. } else cimg_forC(*this,c) {
  28796. std::memmove(data(0,0,delta_z,c),data(0,0,0,c),_width*_height*(_depth-delta_z)*sizeof(T));
  28797. std::memset(data(0,0,0,c),0,delta_z*_width*_height*sizeof(T));
  28798. }
  28799. }
  28800. if (delta_c) // Shift along C-axis
  28801. switch (boundary_conditions) {
  28802. case 2 : { // Periodic
  28803. const int ml = cimg::mod(-delta_c,spectrum()), ndelta_c = (ml<=spectrum()/2)?ml:(ml-spectrum());
  28804. if (!ndelta_c) return *this;
  28805. CImg<T> buf(width(),height(),depth(),cimg::abs(ndelta_c));
  28806. if (ndelta_c>0) {
  28807. std::memcpy(buf,_data,_width*_height*_depth*ndelta_c*sizeof(T));
  28808. std::memmove(_data,data(0,0,0,ndelta_c),_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T));
  28809. std::memcpy(data(0,0,0,_spectrum-ndelta_c),buf,_width*_height*_depth*ndelta_c*sizeof(T));
  28810. } else {
  28811. std::memcpy(buf,data(0,0,0,_spectrum + ndelta_c),-ndelta_c*_width*_height*_depth*sizeof(T));
  28812. std::memmove(data(0,0,0,-ndelta_c),_data,_width*_height*_depth*(_spectrum + ndelta_c)*sizeof(T));
  28813. std::memcpy(_data,buf,-ndelta_c*_width*_height*_depth*sizeof(T));
  28814. }
  28815. } break;
  28816. case 1 : // Neumann
  28817. if (delta_c<0) {
  28818. const int ndelta_c = (-delta_c>=spectrum())?spectrum() - 1:-delta_c;
  28819. if (!ndelta_c) return *this;
  28820. std::memmove(_data,data(0,0,0,ndelta_c),_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T));
  28821. T *ptrd = data(0,0,0,_spectrum-ndelta_c), *ptrs = data(0,0,0,_spectrum - 1);
  28822. for (int l = 0; l<ndelta_c - 1; ++l) {
  28823. std::memcpy(ptrd,ptrs,_width*_height*_depth*sizeof(T)); ptrd+=(ulongT)_width*_height*_depth;
  28824. }
  28825. } else {
  28826. const int ndelta_c = (delta_c>=spectrum())?spectrum() - 1:delta_c;
  28827. if (!ndelta_c) return *this;
  28828. std::memmove(data(0,0,0,ndelta_c),_data,_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T));
  28829. T *ptrd = data(0,0,0,1);
  28830. for (int l = 0; l<ndelta_c - 1; ++l) {
  28831. std::memcpy(ptrd,_data,_width*_height*_depth*sizeof(T)); ptrd+=(ulongT)_width*_height*_depth;
  28832. }
  28833. }
  28834. break;
  28835. default : // Dirichlet
  28836. if (delta_c<=-spectrum() || delta_c>=spectrum()) return fill((T)0);
  28837. if (delta_c<0) {
  28838. std::memmove(_data,data(0,0,0,-delta_c),_width*_height*_depth*(_spectrum + delta_c)*sizeof(T));
  28839. std::memset(data(0,0,0,_spectrum + delta_c),0,_width*_height*_depth*(-delta_c)*sizeof(T));
  28840. } else {
  28841. std::memmove(data(0,0,0,delta_c),_data,_width*_height*_depth*(_spectrum-delta_c)*sizeof(T));
  28842. std::memset(_data,0,delta_c*_width*_height*_depth*sizeof(T));
  28843. }
  28844. }
  28845. return *this;
  28846. }
  28847. //! Shift image content \newinstance.
  28848. CImg<T> get_shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0,
  28849. const unsigned int boundary_conditions=0) const {
  28850. return (+*this).shift(delta_x,delta_y,delta_z,delta_c,boundary_conditions);
  28851. }
  28852. //! Permute axes order.
  28853. /**
  28854. \param order Axes permutations, as a C-string of 4 characters.
  28855. This function permutes image content regarding the specified axes permutation.
  28856. **/
  28857. CImg<T>& permute_axes(const char *const order) {
  28858. return get_permute_axes(order).move_to(*this);
  28859. }
  28860. //! Permute axes order \newinstance.
  28861. CImg<T> get_permute_axes(const char *const order) const {
  28862. const T foo = (T)0;
  28863. return _permute_axes(order,foo);
  28864. }
  28865. template<typename t>
  28866. CImg<t> _permute_axes(const char *const order, const t&) const {
  28867. if (is_empty() || !order) return CImg<t>(*this,false);
  28868. CImg<t> res;
  28869. const T* ptrs = _data;
  28870. unsigned char s_code[4] = { 0,1,2,3 }, n_code[4] = { 0 };
  28871. for (unsigned int l = 0; order[l]; ++l) {
  28872. int c = cimg::lowercase(order[l]);
  28873. if (c!='x' && c!='y' && c!='z' && c!='c') { *s_code = 4; break; }
  28874. else { ++n_code[c%=4]; s_code[l] = c; }
  28875. }
  28876. if (*order && *s_code<4 && *n_code<=1 && n_code[1]<=1 && n_code[2]<=1 && n_code[3]<=1) {
  28877. const unsigned int code = (s_code[0]<<12) | (s_code[1]<<8) | (s_code[2]<<4) | (s_code[3]);
  28878. ulongT wh, whd;
  28879. switch (code) {
  28880. case 0x0123 : // xyzc
  28881. return +*this;
  28882. case 0x0132 : // xycz
  28883. res.assign(_width,_height,_spectrum,_depth);
  28884. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  28885. cimg_forXYZC(*this,x,y,z,c) res(x,y,c,z,wh,whd) = (t)*(ptrs++);
  28886. break;
  28887. case 0x0213 : // xzyc
  28888. res.assign(_width,_depth,_height,_spectrum);
  28889. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  28890. cimg_forXYZC(*this,x,y,z,c) res(x,z,y,c,wh,whd) = (t)*(ptrs++);
  28891. break;
  28892. case 0x0231 : // xzcy
  28893. res.assign(_width,_depth,_spectrum,_height);
  28894. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  28895. cimg_forXYZC(*this,x,y,z,c) res(x,z,c,y,wh,whd) = (t)*(ptrs++);
  28896. break;
  28897. case 0x0312 : // xcyz
  28898. res.assign(_width,_spectrum,_height,_depth);
  28899. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  28900. cimg_forXYZC(*this,x,y,z,c) res(x,c,y,z,wh,whd) = (t)*(ptrs++);
  28901. break;
  28902. case 0x0321 : // xczy
  28903. res.assign(_width,_spectrum,_depth,_height);
  28904. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  28905. cimg_forXYZC(*this,x,y,z,c) res(x,c,z,y,wh,whd) = (t)*(ptrs++);
  28906. break;
  28907. case 0x1023 : // yxzc
  28908. res.assign(_height,_width,_depth,_spectrum);
  28909. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  28910. cimg_forXYZC(*this,x,y,z,c) res(y,x,z,c,wh,whd) = (t)*(ptrs++);
  28911. break;
  28912. case 0x1032 : // yxcz
  28913. res.assign(_height,_width,_spectrum,_depth);
  28914. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  28915. cimg_forXYZC(*this,x,y,z,c) res(y,x,c,z,wh,whd) = (t)*(ptrs++);
  28916. break;
  28917. case 0x1203 : // yzxc
  28918. res.assign(_height,_depth,_width,_spectrum);
  28919. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  28920. cimg_forXYZC(*this,x,y,z,c) res(y,z,x,c,wh,whd) = (t)*(ptrs++);
  28921. break;
  28922. case 0x1230 : // yzcx
  28923. res.assign(_height,_depth,_spectrum,_width);
  28924. switch (_width) {
  28925. case 1 : {
  28926. t *ptr_r = res.data(0,0,0,0);
  28927. for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
  28928. *(ptr_r++) = (t)*(ptrs++);
  28929. }
  28930. } break;
  28931. case 2 : {
  28932. t *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1);
  28933. for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
  28934. *(ptr_r++) = (t)ptrs[0];
  28935. *(ptr_g++) = (t)ptrs[1];
  28936. ptrs+=2;
  28937. }
  28938. } break;
  28939. case 3 : { // Optimization for the classical conversion from interleaved RGB to planar RGB
  28940. 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);
  28941. for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
  28942. *(ptr_r++) = (t)ptrs[0];
  28943. *(ptr_g++) = (t)ptrs[1];
  28944. *(ptr_b++) = (t)ptrs[2];
  28945. ptrs+=3;
  28946. }
  28947. } break;
  28948. case 4 : { // Optimization for the classical conversion from interleaved RGBA to planar RGBA
  28949. t
  28950. *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1),
  28951. *ptr_b = res.data(0,0,0,2), *ptr_a = res.data(0,0,0,3);
  28952. for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
  28953. *(ptr_r++) = (t)ptrs[0];
  28954. *(ptr_g++) = (t)ptrs[1];
  28955. *(ptr_b++) = (t)ptrs[2];
  28956. *(ptr_a++) = (t)ptrs[3];
  28957. ptrs+=4;
  28958. }
  28959. } break;
  28960. default : {
  28961. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  28962. cimg_forXYZC(*this,x,y,z,c) res(y,z,c,x,wh,whd) = *(ptrs++);
  28963. return res;
  28964. }
  28965. }
  28966. break;
  28967. case 0x1302 : // ycxz
  28968. res.assign(_height,_spectrum,_width,_depth);
  28969. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  28970. cimg_forXYZC(*this,x,y,z,c) res(y,c,x,z,wh,whd) = (t)*(ptrs++);
  28971. break;
  28972. case 0x1320 : // yczx
  28973. res.assign(_height,_spectrum,_depth,_width);
  28974. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  28975. cimg_forXYZC(*this,x,y,z,c) res(y,c,z,x,wh,whd) = (t)*(ptrs++);
  28976. break;
  28977. case 0x2013 : // zxyc
  28978. res.assign(_depth,_width,_height,_spectrum);
  28979. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  28980. cimg_forXYZC(*this,x,y,z,c) res(z,x,y,c,wh,whd) = (t)*(ptrs++);
  28981. break;
  28982. case 0x2031 : // zxcy
  28983. res.assign(_depth,_width,_spectrum,_height);
  28984. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  28985. cimg_forXYZC(*this,x,y,z,c) res(z,x,c,y,wh,whd) = (t)*(ptrs++);
  28986. break;
  28987. case 0x2103 : // zyxc
  28988. res.assign(_depth,_height,_width,_spectrum);
  28989. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  28990. cimg_forXYZC(*this,x,y,z,c) res(z,y,x,c,wh,whd) = (t)*(ptrs++);
  28991. break;
  28992. case 0x2130 : // zycx
  28993. res.assign(_depth,_height,_spectrum,_width);
  28994. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  28995. cimg_forXYZC(*this,x,y,z,c) res(z,y,c,x,wh,whd) = (t)*(ptrs++);
  28996. break;
  28997. case 0x2301 : // zcxy
  28998. res.assign(_depth,_spectrum,_width,_height);
  28999. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  29000. cimg_forXYZC(*this,x,y,z,c) res(z,c,x,y,wh,whd) = (t)*(ptrs++);
  29001. break;
  29002. case 0x2310 : // zcyx
  29003. res.assign(_depth,_spectrum,_height,_width);
  29004. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  29005. cimg_forXYZC(*this,x,y,z,c) res(z,c,y,x,wh,whd) = (t)*(ptrs++);
  29006. break;
  29007. case 0x3012 : // cxyz
  29008. res.assign(_spectrum,_width,_height,_depth);
  29009. switch (_spectrum) {
  29010. case 1 : {
  29011. const T *ptr_r = data(0,0,0,0);
  29012. t *ptrd = res._data;
  29013. for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) *(ptrd++) = (t)*(ptr_r++);
  29014. } break;
  29015. case 2 : {
  29016. const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1);
  29017. t *ptrd = res._data;
  29018. for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) {
  29019. ptrd[0] = (t)*(ptr_r++);
  29020. ptrd[1] = (t)*(ptr_g++);
  29021. ptrd+=2;
  29022. }
  29023. } break;
  29024. case 3 : { // Optimization for the classical conversion from planar RGB to interleaved RGB
  29025. const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
  29026. t *ptrd = res._data;
  29027. for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) {
  29028. ptrd[0] = (t)*(ptr_r++);
  29029. ptrd[1] = (t)*(ptr_g++);
  29030. ptrd[2] = (t)*(ptr_b++);
  29031. ptrd+=3;
  29032. }
  29033. } break;
  29034. case 4 : { // Optimization for the classical conversion from planar RGBA to interleaved RGBA
  29035. 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);
  29036. t *ptrd = res._data;
  29037. for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) {
  29038. ptrd[0] = (t)*(ptr_r++);
  29039. ptrd[1] = (t)*(ptr_g++);
  29040. ptrd[2] = (t)*(ptr_b++);
  29041. ptrd[3] = (t)*(ptr_a++);
  29042. ptrd+=4;
  29043. }
  29044. } break;
  29045. default : {
  29046. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  29047. cimg_forXYZC(*this,x,y,z,c) res(c,x,y,z,wh,whd) = (t)*(ptrs++);
  29048. }
  29049. }
  29050. break;
  29051. case 0x3021 : // cxzy
  29052. res.assign(_spectrum,_width,_depth,_height);
  29053. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  29054. cimg_forXYZC(*this,x,y,z,c) res(c,x,z,y,wh,whd) = (t)*(ptrs++);
  29055. break;
  29056. case 0x3102 : // cyxz
  29057. res.assign(_spectrum,_height,_width,_depth);
  29058. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  29059. cimg_forXYZC(*this,x,y,z,c) res(c,y,x,z,wh,whd) = (t)*(ptrs++);
  29060. break;
  29061. case 0x3120 : // cyzx
  29062. res.assign(_spectrum,_height,_depth,_width);
  29063. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  29064. cimg_forXYZC(*this,x,y,z,c) res(c,y,z,x,wh,whd) = (t)*(ptrs++);
  29065. break;
  29066. case 0x3201 : // czxy
  29067. res.assign(_spectrum,_depth,_width,_height);
  29068. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  29069. cimg_forXYZC(*this,x,y,z,c) res(c,z,x,y,wh,whd) = (t)*(ptrs++);
  29070. break;
  29071. case 0x3210 : // czyx
  29072. res.assign(_spectrum,_depth,_height,_width);
  29073. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  29074. cimg_forXYZC(*this,x,y,z,c) res(c,z,y,x,wh,whd) = (t)*(ptrs++);
  29075. break;
  29076. }
  29077. }
  29078. if (!res)
  29079. throw CImgArgumentException(_cimg_instance
  29080. "permute_axes(): Invalid specified permutation '%s'.",
  29081. cimg_instance,
  29082. order);
  29083. return res;
  29084. }
  29085. //! Unroll pixel values along specified axis.
  29086. /**
  29087. \param axis Unroll axis (can be \c 'x', \c 'y', \c 'z' or c 'c').
  29088. **/
  29089. CImg<T>& unroll(const char axis) {
  29090. const unsigned int siz = (unsigned int)size();
  29091. if (siz) switch (cimg::lowercase(axis)) {
  29092. case 'x' : _width = siz; _height = _depth = _spectrum = 1; break;
  29093. case 'y' : _height = siz; _width = _depth = _spectrum = 1; break;
  29094. case 'z' : _depth = siz; _width = _height = _spectrum = 1; break;
  29095. default : _spectrum = siz; _width = _height = _depth = 1;
  29096. }
  29097. return *this;
  29098. }
  29099. //! Unroll pixel values along specified axis \newinstance.
  29100. CImg<T> get_unroll(const char axis) const {
  29101. return (+*this).unroll(axis);
  29102. }
  29103. //! Rotate image with arbitrary angle.
  29104. /**
  29105. \param angle Rotation angle, in degrees.
  29106. \param interpolation Type of interpolation. Can be <tt>{ 0=nearest | 1=linear | 2=cubic }</tt>.
  29107. \param boundary_conditions Boundary conditions.
  29108. Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
  29109. \note The size of the image is modified.
  29110. **/
  29111. CImg<T>& rotate(const float angle, const unsigned int interpolation=1,
  29112. const unsigned int boundary_conditions=0) {
  29113. const float nangle = cimg::mod(angle,360.0f);
  29114. if (nangle==0.0f) return *this;
  29115. return get_rotate(nangle,interpolation,boundary_conditions).move_to(*this);
  29116. }
  29117. //! Rotate image with arbitrary angle \newinstance.
  29118. CImg<T> get_rotate(const float angle, const unsigned int interpolation=1,
  29119. const unsigned int boundary_conditions=0) const {
  29120. if (is_empty()) return *this;
  29121. CImg<T> res;
  29122. const float nangle = cimg::mod(angle,360.0f);
  29123. if (boundary_conditions!=1 && cimg::mod(nangle,90.0f)==0) { // Optimized version for orthogonal angles.
  29124. const int wm1 = width() - 1, hm1 = height() - 1;
  29125. const int iangle = (int)nangle/90;
  29126. switch (iangle) {
  29127. case 1 : { // 90 deg
  29128. res.assign(_height,_width,_depth,_spectrum);
  29129. T *ptrd = res._data;
  29130. cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(y,hm1 - x,z,c);
  29131. } break;
  29132. case 2 : { // 180 deg
  29133. res.assign(_width,_height,_depth,_spectrum);
  29134. T *ptrd = res._data;
  29135. cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1 - x,hm1 - y,z,c);
  29136. } break;
  29137. case 3 : { // 270 deg
  29138. res.assign(_height,_width,_depth,_spectrum);
  29139. T *ptrd = res._data;
  29140. cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1 - y,x,z,c);
  29141. } break;
  29142. default : // 0 deg
  29143. return *this;
  29144. }
  29145. } else { // Generic angle
  29146. const float
  29147. rad = (float)(nangle*cimg::PI/180.0),
  29148. ca = (float)std::cos(rad), sa = (float)std::sin(rad),
  29149. ux = cimg::abs((_width - 1)*ca), uy = cimg::abs((_width - 1)*sa),
  29150. vx = cimg::abs((_height - 1)*sa), vy = cimg::abs((_height - 1)*ca),
  29151. w2 = 0.5f*(_width - 1), h2 = 0.5f*(_height - 1);
  29152. res.assign((int)cimg::round(1 + ux + vx),(int)cimg::round(1 + uy + vy),_depth,_spectrum);
  29153. const float rw2 = 0.5f*(res._width - 1), rh2 = 0.5f*(res._height - 1);
  29154. _rotate(res,nangle,interpolation,boundary_conditions,w2,h2,rw2,rh2);
  29155. }
  29156. return res;
  29157. }
  29158. //! Rotate image with arbitrary angle, around a center point.
  29159. /**
  29160. \param angle Rotation angle, in degrees.
  29161. \param cx X-coordinate of the rotation center.
  29162. \param cy Y-coordinate of the rotation center.
  29163. \param interpolation Type of interpolation, <tt>{ 0=nearest | 1=linear | 2=cubic | 3=mirror }</tt>.
  29164. \param boundary_conditions Boundary conditions, <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
  29165. **/
  29166. CImg<T>& rotate(const float angle, const float cx, const float cy,
  29167. const unsigned int interpolation, const unsigned int boundary_conditions=0) {
  29168. return get_rotate(angle,cx,cy,interpolation,boundary_conditions).move_to(*this);
  29169. }
  29170. //! Rotate image with arbitrary angle, around a center point \newinstance.
  29171. CImg<T> get_rotate(const float angle, const float cx, const float cy,
  29172. const unsigned int interpolation, const unsigned int boundary_conditions=0) const {
  29173. if (is_empty()) return *this;
  29174. CImg<T> res(_width,_height,_depth,_spectrum);
  29175. _rotate(res,angle,interpolation,boundary_conditions,cx,cy,cx,cy);
  29176. return res;
  29177. }
  29178. // [internal] Perform 2d rotation with arbitrary angle.
  29179. void _rotate(CImg<T>& res, const float angle,
  29180. const unsigned int interpolation, const unsigned int boundary_conditions,
  29181. const float w2, const float h2,
  29182. const float rw2, const float rh2) const {
  29183. const float
  29184. rad = (float)(angle*cimg::PI/180.0),
  29185. ca = (float)std::cos(rad), sa = (float)std::sin(rad);
  29186. switch (boundary_conditions) {
  29187. case 3 : { // Mirror
  29188. switch (interpolation) {
  29189. case 2 : { // Cubic interpolation
  29190. const float ww = 2.0f*width(), hh = 2.0f*height();
  29191. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
  29192. cimg_forXYZC(res,x,y,z,c) {
  29193. const float xc = x - rw2, yc = y - rh2,
  29194. mx = cimg::mod(w2 + xc*ca + yc*sa,ww),
  29195. my = cimg::mod(h2 - xc*sa + yc*ca,hh);
  29196. res(x,y,z,c) = _cubic_cut_atXY(mx<width()?mx:ww - mx - 1,my<height()?my:hh - my - 1,z,c);
  29197. }
  29198. } break;
  29199. case 1 : { // Linear interpolation
  29200. const float ww = 2.0f*width(), hh = 2.0f*height();
  29201. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
  29202. cimg_forXYZC(res,x,y,z,c) {
  29203. const float xc = x - rw2, yc = y - rh2,
  29204. mx = cimg::mod(w2 + xc*ca + yc*sa,ww),
  29205. my = cimg::mod(h2 - xc*sa + yc*ca,hh);
  29206. res(x,y,z,c) = (T)_linear_atXY(mx<width()?mx:ww - mx - 1,my<height()?my:hh - my - 1,z,c);
  29207. }
  29208. } break;
  29209. default : { // Nearest-neighbor interpolation
  29210. const int ww = 2*width(), hh = 2*height();
  29211. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
  29212. cimg_forXYZC(res,x,y,z,c) {
  29213. const float xc = x - rw2, yc = y - rh2,
  29214. mx = cimg::mod((int)cimg::round(w2 + xc*ca + yc*sa),ww),
  29215. my = cimg::mod((int)cimg::round(h2 - xc*sa + yc*ca),hh);
  29216. res(x,y,z,c) = (*this)(mx<width()?mx:ww - mx - 1,my<height()?my:hh - my - 1,z,c);
  29217. }
  29218. }
  29219. }
  29220. } break;
  29221. case 2 : // Periodic
  29222. switch (interpolation) {
  29223. case 2 : { // Cubic interpolation
  29224. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
  29225. cimg_forXYZC(res,x,y,z,c) {
  29226. const float xc = x - rw2, yc = y - rh2;
  29227. res(x,y,z,c) = _cubic_cut_atXY(cimg::mod(w2 + xc*ca + yc*sa,(float)width()),
  29228. cimg::mod(h2 - xc*sa + yc*ca,(float)height()),z,c);
  29229. }
  29230. } break;
  29231. case 1 : { // Linear interpolation
  29232. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
  29233. cimg_forXYZC(res,x,y,z,c) {
  29234. const float xc = x - rw2, yc = y - rh2;
  29235. res(x,y,z,c) = (T)_linear_atXY(cimg::mod(w2 + xc*ca + yc*sa,(float)width()),
  29236. cimg::mod(h2 - xc*sa + yc*ca,(float)height()),z,c);
  29237. }
  29238. } break;
  29239. default : { // Nearest-neighbor interpolation
  29240. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
  29241. cimg_forXYZC(res,x,y,z,c) {
  29242. const float xc = x - rw2, yc = y - rh2;
  29243. res(x,y,z,c) = (*this)(cimg::mod((int)cimg::round(w2 + xc*ca + yc*sa),(float)width()),
  29244. cimg::mod((int)cimg::round(h2 - xc*sa + yc*ca),(float)height()),z,c);
  29245. }
  29246. }
  29247. } break;
  29248. case 1 : // Neumann
  29249. switch (interpolation) {
  29250. case 2 : { // Cubic interpolation
  29251. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
  29252. cimg_forXYZC(res,x,y,z,c) {
  29253. const float xc = x - rw2, yc = y - rh2;
  29254. res(x,y,z,c) = _cubic_cut_atXY(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c);
  29255. }
  29256. } break;
  29257. case 1 : { // Linear interpolation
  29258. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
  29259. cimg_forXYZC(res,x,y,z,c) {
  29260. const float xc = x - rw2, yc = y - rh2;
  29261. res(x,y,z,c) = (T)_linear_atXY(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c);
  29262. }
  29263. } break;
  29264. default : { // Nearest-neighbor interpolation
  29265. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
  29266. cimg_forXYZC(res,x,y,z,c) {
  29267. const float xc = x - rw2, yc = y - rh2;
  29268. res(x,y,z,c) = _atXY((int)cimg::round(w2 + xc*ca + yc*sa),
  29269. (int)cimg::round(h2 - xc*sa + yc*ca),z,c);
  29270. }
  29271. }
  29272. } break;
  29273. default : // Dirichlet
  29274. switch (interpolation) {
  29275. case 2 : { // Cubic interpolation
  29276. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
  29277. cimg_forXYZC(res,x,y,z,c) {
  29278. const float xc = x - rw2, yc = y - rh2;
  29279. res(x,y,z,c) = cubic_cut_atXY(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c,(T)0);
  29280. }
  29281. } break;
  29282. case 1 : { // Linear interpolation
  29283. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
  29284. cimg_forXYZC(res,x,y,z,c) {
  29285. const float xc = x - rw2, yc = y - rh2;
  29286. res(x,y,z,c) = (T)linear_atXY(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c,(T)0);
  29287. }
  29288. } break;
  29289. default : { // Nearest-neighbor interpolation
  29290. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
  29291. cimg_forXYZC(res,x,y,z,c) {
  29292. const float xc = x - rw2, yc = y - rh2;
  29293. res(x,y,z,c) = atXY((int)cimg::round(w2 + xc*ca + yc*sa),
  29294. (int)cimg::round(h2 - xc*sa + yc*ca),z,c,(T)0);
  29295. }
  29296. }
  29297. }
  29298. }
  29299. }
  29300. //! Rotate volumetric image with arbitrary angle and axis.
  29301. /**
  29302. \param u X-coordinate of the 3d rotation axis.
  29303. \param v Y-coordinate of the 3d rotation axis.
  29304. \param w Z-coordinate of the 3d rotation axis.
  29305. \param angle Rotation angle, in degrees.
  29306. \param interpolation Type of interpolation. Can be <tt>{ 0=nearest | 1=linear | 2=cubic }</tt>.
  29307. \param boundary_conditions Boundary conditions.
  29308. Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
  29309. \note Most of the time, size of the image is modified.
  29310. **/
  29311. CImg<T> rotate(const float u, const float v, const float w, const float angle,
  29312. const unsigned int interpolation, const unsigned int boundary_conditions) {
  29313. const float nangle = cimg::mod(angle,360.0f);
  29314. if (nangle==0.0f) return *this;
  29315. return get_rotate(u,v,w,nangle,interpolation,boundary_conditions).move_to(*this);
  29316. }
  29317. //! Rotate volumetric image with arbitrary angle and axis \newinstance.
  29318. CImg<T> get_rotate(const float u, const float v, const float w, const float angle,
  29319. const unsigned int interpolation, const unsigned int boundary_conditions) const {
  29320. if (is_empty()) return *this;
  29321. CImg<T> res;
  29322. const float
  29323. w1 = _width - 1, h1 = _height - 1, d1 = _depth -1,
  29324. w2 = 0.5f*w1, h2 = 0.5f*h1, d2 = 0.5f*d1;
  29325. CImg<floatT> R = CImg<floatT>::rotation_matrix(u,v,w,angle);
  29326. const CImg<Tfloat>
  29327. X = R*CImg<Tfloat>(8,3,1,1,
  29328. 0.0f,w1,w1,0.0f,0.0f,w1,w1,0.0f,
  29329. 0.0f,0.0f,h1,h1,0.0f,0.0f,h1,h1,
  29330. 0.0f,0.0f,0.0f,0.0f,d1,d1,d1,d1);
  29331. float
  29332. xm, xM = X.get_shared_row(0).max_min(xm),
  29333. ym, yM = X.get_shared_row(1).max_min(ym),
  29334. zm, zM = X.get_shared_row(2).max_min(zm);
  29335. const int
  29336. dx = (int)cimg::round(xM - xm),
  29337. dy = (int)cimg::round(yM - ym),
  29338. dz = (int)cimg::round(zM - zm);
  29339. R.transpose();
  29340. res.assign(1 + dx,1 + dy,1 + dz,_spectrum);
  29341. const float rw2 = 0.5f*dx, rh2 = 0.5f*dy, rd2 = 0.5f*dz;
  29342. _rotate(res,R,interpolation,boundary_conditions,w2,h2,d2,rw2,rh2,rd2);
  29343. return res;
  29344. }
  29345. //! Rotate volumetric image with arbitrary angle and axis, around a center point.
  29346. /**
  29347. \param u X-coordinate of the 3d rotation axis.
  29348. \param v Y-coordinate of the 3d rotation axis.
  29349. \param w Z-coordinate of the 3d rotation axis.
  29350. \param angle Rotation angle, in degrees.
  29351. \param cx X-coordinate of the rotation center.
  29352. \param cy Y-coordinate of the rotation center.
  29353. \param cz Z-coordinate of the rotation center.
  29354. \param interpolation Type of interpolation. Can be <tt>{ 0=nearest | 1=linear | 2=cubic | 3=mirror }</tt>.
  29355. \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic }</tt>.
  29356. \note Most of the time, size of the image is modified.
  29357. **/
  29358. CImg<T> rotate(const float u, const float v, const float w, const float angle,
  29359. const float cx, const float cy, const float cz,
  29360. const unsigned int interpolation=1, const unsigned int boundary_conditions=0) {
  29361. const float nangle = cimg::mod(angle,360.0f);
  29362. if (nangle==0.0f) return *this;
  29363. return get_rotate(u,v,w,nangle,cx,cy,cz,interpolation,boundary_conditions).move_to(*this);
  29364. }
  29365. //! Rotate volumetric image with arbitrary angle and axis, around a center point \newinstance.
  29366. CImg<T> get_rotate(const float u, const float v, const float w, const float angle,
  29367. const float cx, const float cy, const float cz,
  29368. const unsigned int interpolation=1, const unsigned int boundary_conditions=0) const {
  29369. if (is_empty()) return *this;
  29370. CImg<T> res(_width,_height,_depth,_spectrum);
  29371. CImg<floatT> R = CImg<floatT>::rotation_matrix(u,v,w,-angle);
  29372. _rotate(res,R,interpolation,boundary_conditions,cx,cy,cz,cx,cy,cz);
  29373. return res;
  29374. }
  29375. // [internal] Perform 3d rotation with arbitrary axis and angle.
  29376. void _rotate(CImg<T>& res, const CImg<Tfloat>& R,
  29377. const unsigned int interpolation, const unsigned int boundary_conditions,
  29378. const float w2, const float h2, const float d2,
  29379. const float rw2, const float rh2, const float rd2) const {
  29380. switch (boundary_conditions) {
  29381. case 3 : // Mirror
  29382. switch (interpolation) {
  29383. case 2 : { // Cubic interpolation
  29384. const float ww = 2.0f*width(), hh = 2.0f*height(), dd = 2.0f*depth();
  29385. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
  29386. cimg_forXYZ(res,x,y,z) {
  29387. const float
  29388. xc = x - rw2, yc = y - rh2, zc = z - rd2,
  29389. X = cimg::mod((float)(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),ww),
  29390. Y = cimg::mod((float)(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),hh),
  29391. Z = cimg::mod((float)(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),dd);
  29392. cimg_forC(res,c) res(x,y,z,c) = _cubic_cut_atXYZ(X<width()?X:ww - X - 1,
  29393. Y<height()?Y:hh - Y - 1,
  29394. Z<depth()?Z:dd - Z - z,c);
  29395. }
  29396. } break;
  29397. case 1 : { // Linear interpolation
  29398. const float ww = 2.0f*width(), hh = 2.0f*height(), dd = 2.0f*depth();
  29399. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
  29400. cimg_forXYZ(res,x,y,z) {
  29401. const float
  29402. xc = x - rw2, yc = y - rh2, zc = z - rd2,
  29403. X = cimg::mod((float)(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),ww),
  29404. Y = cimg::mod((float)(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),hh),
  29405. Z = cimg::mod((float)(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),dd);
  29406. cimg_forC(res,c) res(x,y,z,c) = (T)_linear_atXYZ(X<width()?X:ww - X - 1,
  29407. Y<height()?Y:hh - Y - 1,
  29408. Z<depth()?Z:dd - Z - 1,c);
  29409. }
  29410. } break;
  29411. default : { // Nearest-neighbor interpolation
  29412. const int ww = 2*width(), hh = 2*height(), dd = 2*depth();
  29413. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
  29414. cimg_forXYZ(res,x,y,z) {
  29415. const float xc = x - rw2, yc = y - rh2, zc = z - rd2;
  29416. const int
  29417. X = cimg::mod((int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),ww),
  29418. Y = cimg::mod((int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),hh),
  29419. Z = cimg::mod((int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),dd);
  29420. cimg_forC(res,c) res(x,y,z,c) = (*this)(X<width()?X:ww - X - 1,
  29421. Y<height()?Y:hh - Y - 1,
  29422. Z<depth()?Z:dd - Z - 1,c);
  29423. }
  29424. }
  29425. } break;
  29426. case 2 : // Periodic
  29427. switch (interpolation) {
  29428. case 2 : { // Cubic interpolation
  29429. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
  29430. cimg_forXYZ(res,x,y,z) {
  29431. const float
  29432. xc = x - rw2, yc = y - rh2, zc = z - rd2,
  29433. X = cimg::mod((float)(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),(float)width()),
  29434. Y = cimg::mod((float)(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),(float)height()),
  29435. Z = cimg::mod((float)(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),(float)depth());
  29436. cimg_forC(res,c) res(x,y,z,c) = _cubic_cut_atXYZ(X,Y,Z,c);
  29437. }
  29438. } break;
  29439. case 1 : { // Linear interpolation
  29440. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
  29441. cimg_forXYZ(res,x,y,z) {
  29442. const float
  29443. xc = x - rw2, yc = y - rh2, zc = z - rd2,
  29444. X = cimg::mod((float)(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),(float)width()),
  29445. Y = cimg::mod((float)(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),(float)height()),
  29446. Z = cimg::mod((float)(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),(float)depth());
  29447. cimg_forC(res,c) res(x,y,z,c) = (T)_linear_atXYZ(X,Y,Z,c);
  29448. }
  29449. } break;
  29450. default : { // Nearest-neighbor interpolation
  29451. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
  29452. cimg_forXYZ(res,x,y,z) {
  29453. const float xc = x - rw2, yc = y - rh2, zc = z - rd2;
  29454. const int
  29455. X = cimg::mod((int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),width()),
  29456. Y = cimg::mod((int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),height()),
  29457. Z = cimg::mod((int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),depth());
  29458. cimg_forC(res,c) res(x,y,z,c) = (*this)(X,Y,Z,c);
  29459. }
  29460. }
  29461. } break;
  29462. case 1 : // Neumann
  29463. switch (interpolation) {
  29464. case 2 : { // Cubic interpolation
  29465. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
  29466. cimg_forXYZ(res,x,y,z) {
  29467. const float
  29468. xc = x - rw2, yc = y - rh2, zc = z - rd2,
  29469. X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
  29470. Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
  29471. Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
  29472. cimg_forC(res,c) res(x,y,z,c) = _cubic_cut_atXYZ(X,Y,Z,c);
  29473. }
  29474. } break;
  29475. case 1 : { // Linear interpolation
  29476. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
  29477. cimg_forXYZ(res,x,y,z) {
  29478. const float
  29479. xc = x - rw2, yc = y - rh2, zc = z - rd2,
  29480. X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
  29481. Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
  29482. Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
  29483. cimg_forC(res,c) res(x,y,z,c) = _linear_atXYZ(X,Y,Z,c);
  29484. }
  29485. } break;
  29486. default : { // Nearest-neighbor interpolation
  29487. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
  29488. cimg_forXYZ(res,x,y,z) {
  29489. const float xc = x - rw2, yc = y - rh2, zc = z - rd2;
  29490. const int
  29491. X = (int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),
  29492. Y = (int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),
  29493. Z = (int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc);
  29494. cimg_forC(res,c) res(x,y,z,c) = _atXYZ(X,Y,Z,c);
  29495. }
  29496. }
  29497. } break;
  29498. default : // Dirichlet
  29499. switch (interpolation) {
  29500. case 2 : { // Cubic interpolation
  29501. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
  29502. cimg_forXYZ(res,x,y,z) {
  29503. const float
  29504. xc = x - rw2, yc = y - rh2, zc = z - rd2,
  29505. X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
  29506. Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
  29507. Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
  29508. cimg_forC(res,c) res(x,y,z,c) = cubic_cut_atXYZ(X,Y,Z,c,(T)0);
  29509. }
  29510. } break;
  29511. case 1 : { // Linear interpolation
  29512. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
  29513. cimg_forXYZ(res,x,y,z) {
  29514. const float
  29515. xc = x - rw2, yc = y - rh2, zc = z - rd2,
  29516. X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
  29517. Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
  29518. Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
  29519. cimg_forC(res,c) res(x,y,z,c) = linear_atXYZ(X,Y,Z,c,(T)0);
  29520. }
  29521. } break;
  29522. default : { // Nearest-neighbor interpolation
  29523. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
  29524. cimg_forXYZ(res,x,y,z) {
  29525. const float xc = x - rw2, yc = y - rh2, zc = z - rd2;
  29526. const int
  29527. X = (int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),
  29528. Y = (int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),
  29529. Z = (int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc);
  29530. cimg_forC(res,c) res(x,y,z,c) = atXYZ(X,Y,Z,c,(T)0);
  29531. }
  29532. }
  29533. } break;
  29534. }
  29535. }
  29536. //! Warp image content by a warping field.
  29537. /**
  29538. \param warp Warping field.
  29539. \param mode Can be { 0=backward-absolute | 1=backward-relative | 2=forward-absolute | 3=foward-relative }
  29540. \param interpolation Can be <tt>{ 0=nearest | 1=linear | 2=cubic }</tt>.
  29541. \param boundary_conditions Boundary conditions <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
  29542. **/
  29543. template<typename t>
  29544. CImg<T>& warp(const CImg<t>& warp, const unsigned int mode=0,
  29545. const unsigned int interpolation=1, const unsigned int boundary_conditions=0) {
  29546. return get_warp(warp,mode,interpolation,boundary_conditions).move_to(*this);
  29547. }
  29548. //! Warp image content by a warping field \newinstance
  29549. template<typename t>
  29550. CImg<T> get_warp(const CImg<t>& warp, const unsigned int mode=0,
  29551. const unsigned int interpolation=1, const unsigned int boundary_conditions=0) const {
  29552. if (is_empty() || !warp) return *this;
  29553. if (mode && !is_sameXYZ(warp))
  29554. throw CImgArgumentException(_cimg_instance
  29555. "warp(): Instance and specified relative warping field (%u,%u,%u,%u,%p) "
  29556. "have different XYZ dimensions.",
  29557. cimg_instance,
  29558. warp._width,warp._height,warp._depth,warp._spectrum,warp._data);
  29559. CImg<T> res(warp._width,warp._height,warp._depth,_spectrum);
  29560. if (warp._spectrum==1) { // 1d warping
  29561. if (mode>=3) { // Forward-relative warp
  29562. res.fill((T)0);
  29563. if (interpolation>=1) // Linear interpolation
  29564. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  29565. cimg_forYZC(res,y,z,c) {
  29566. const t *ptrs0 = warp.data(0,y,z); const T *ptrs = data(0,y,z,c);
  29567. cimg_forX(res,x) res.set_linear_atX(*(ptrs++),x + (float)*(ptrs0++),y,z,c);
  29568. }
  29569. else // Nearest-neighbor interpolation
  29570. cimg_forYZC(res,y,z,c) {
  29571. const t *ptrs0 = warp.data(0,y,z); const T *ptrs = data(0,y,z,c);
  29572. cimg_forX(res,x) {
  29573. const int X = x + (int)cimg::round(*(ptrs0++));
  29574. if (X>=0 && X<width()) res(X,y,z,c) = *(ptrs++);
  29575. }
  29576. }
  29577. } else if (mode==2) { // Forward-absolute warp
  29578. res.fill((T)0);
  29579. if (interpolation>=1) // Linear interpolation
  29580. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  29581. cimg_forYZC(res,y,z,c) {
  29582. const t *ptrs0 = warp.data(0,y,z); const T *ptrs = data(0,y,z,c);
  29583. cimg_forX(res,x) res.set_linear_atX(*(ptrs++),(float)*(ptrs0++),y,z,c);
  29584. }
  29585. else // Nearest-neighbor interpolation
  29586. cimg_forYZC(res,y,z,c) {
  29587. const t *ptrs0 = warp.data(0,y,z); const T *ptrs = data(0,y,z,c);
  29588. cimg_forX(res,x) {
  29589. const int X = (int)cimg::round(*(ptrs0++));
  29590. if (X>=0 && X<width()) res(X,y,z,c) = *(ptrs++);
  29591. }
  29592. }
  29593. } else if (mode==1) { // Backward-relative warp
  29594. if (interpolation==2) // Cubic interpolation
  29595. switch (boundary_conditions) {
  29596. case 3 : { // Mirror
  29597. const float w2 = 2.0f*width();
  29598. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  29599. cimg_forYZC(res,y,z,c) {
  29600. const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  29601. cimg_forX(res,x) {
  29602. const float mx = cimg::mod(x - (float)*(ptrs0++),w2);
  29603. *(ptrd++) = _cubic_cut_atX(mx<width()?mx:w2 - mx - 1,y,z,c);
  29604. }
  29605. }
  29606. } break;
  29607. case 2 : // Periodic
  29608. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  29609. cimg_forYZC(res,y,z,c) {
  29610. const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  29611. cimg_forX(res,x) *(ptrd++) = _cubic_cut_atX(cimg::mod(x - (float)*(ptrs0++),(float)_width),y,z,c);
  29612. }
  29613. break;
  29614. case 1 : // Neumann
  29615. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  29616. cimg_forYZC(res,y,z,c) {
  29617. const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  29618. cimg_forX(res,x) *(ptrd++) = _cubic_cut_atX(x - (float)*(ptrs0++),y,z,c);
  29619. }
  29620. break;
  29621. default : // Dirichlet
  29622. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  29623. cimg_forYZC(res,y,z,c) {
  29624. const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  29625. cimg_forX(res,x) *(ptrd++) = cubic_cut_atX(x - (float)*(ptrs0++),y,z,c,(T)0);
  29626. }
  29627. }
  29628. else if (interpolation==1) // Linear interpolation
  29629. switch (boundary_conditions) {
  29630. case 3 : { // Mirror
  29631. const float w2 = 2.0f*width();
  29632. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  29633. cimg_forYZC(res,y,z,c) {
  29634. const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  29635. cimg_forX(res,x) {
  29636. const float mx = cimg::mod(x - (float)*(ptrs0++),w2);
  29637. *(ptrd++) = (T)_linear_atX(mx<width()?mx:w2 - mx - 1,y,z,c);
  29638. }
  29639. }
  29640. } break;
  29641. case 2 : // Periodic
  29642. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
  29643. cimg_forYZC(res,y,z,c) {
  29644. const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  29645. cimg_forX(res,x) *(ptrd++) = (T)_linear_atX(cimg::mod(x - (float)*(ptrs0++),(float)_width),y,z,c);
  29646. }
  29647. break;
  29648. case 1 : // Neumann
  29649. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
  29650. cimg_forYZC(res,y,z,c) {
  29651. const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  29652. cimg_forX(res,x) *(ptrd++) = (T)_linear_atX(x - (float)*(ptrs0++),y,z,c);
  29653. }
  29654. break;
  29655. default : // Dirichlet
  29656. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
  29657. cimg_forYZC(res,y,z,c) {
  29658. const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  29659. cimg_forX(res,x) *(ptrd++) = (T)linear_atX(x - (float)*(ptrs0++),y,z,c,(T)0);
  29660. }
  29661. }
  29662. else // Nearest-neighbor interpolation
  29663. switch (boundary_conditions) {
  29664. case 3 : { // Mirror
  29665. const int w2 = 2*width();
  29666. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  29667. cimg_forYZC(res,y,z,c) {
  29668. const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  29669. cimg_forX(res,x) {
  29670. const int mx = cimg::mod(x - (int)cimg::round(*(ptrs0++)),w2);
  29671. *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,y,z,c);
  29672. }
  29673. }
  29674. } break;
  29675. case 2 : // Periodic
  29676. cimg_forYZC(res,y,z,c) {
  29677. const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  29678. cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod(x - (int)cimg::round(*(ptrs0++)),(int)_width),y,z,c);
  29679. }
  29680. break;
  29681. case 1 : // Neumann
  29682. cimg_forYZC(res,y,z,c) {
  29683. const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  29684. cimg_forX(res,x) *(ptrd++) = _atX(x - (int)*(ptrs0++),y,z,c);
  29685. }
  29686. break;
  29687. default : // Dirichlet
  29688. cimg_forYZC(res,y,z,c) {
  29689. const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  29690. cimg_forX(res,x) *(ptrd++) = atX(x - (int)*(ptrs0++),y,z,c,(T)0);
  29691. }
  29692. }
  29693. }
  29694. else { // Backward-absolute warp
  29695. if (interpolation==2) // Cubic interpolation
  29696. switch (boundary_conditions) {
  29697. case 3 : { // Mirror
  29698. const float w2 = 2.0f*width();
  29699. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  29700. cimg_forYZC(res,y,z,c) {
  29701. const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  29702. cimg_forX(res,x) {
  29703. const float mx = cimg::mod((float)*(ptrs0++),w2);
  29704. *(ptrd++) = _cubic_cut_atX(mx<width()?mx:w2 - mx - 1,0,0,c);
  29705. }
  29706. }
  29707. } break;
  29708. case 2 : // Periodic
  29709. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  29710. cimg_forYZC(res,y,z,c) {
  29711. const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  29712. cimg_forX(res,x) *(ptrd++) = _cubic_cut_atX(cimg::mod((float)*(ptrs0++),(float)_width),0,0,c);
  29713. }
  29714. break;
  29715. case 1 : // Neumann
  29716. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  29717. cimg_forYZC(res,y,z,c) {
  29718. const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  29719. cimg_forX(res,x) *(ptrd++) = _cubic_cut_atX((float)*(ptrs0++),0,0,c);
  29720. }
  29721. break;
  29722. default : // Dirichlet
  29723. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  29724. cimg_forYZC(res,y,z,c) {
  29725. const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  29726. cimg_forX(res,x) *(ptrd++) = cubic_cut_atX((float)*(ptrs0++),0,0,c,(T)0);
  29727. }
  29728. }
  29729. else if (interpolation==1) // Linear interpolation
  29730. switch (boundary_conditions) {
  29731. case 3 : { // Mirror
  29732. const float w2 = 2.0f*width();
  29733. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  29734. cimg_forYZC(res,y,z,c) {
  29735. const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  29736. cimg_forX(res,x) {
  29737. const float mx = cimg::mod((float)*(ptrs0++),w2);
  29738. *(ptrd++) = (T)_linear_atX(mx<width()?mx:w2 - mx - 1,0,0,c);
  29739. }
  29740. }
  29741. } break;
  29742. case 2 : // Periodic
  29743. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
  29744. cimg_forYZC(res,y,z,c) {
  29745. const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  29746. cimg_forX(res,x) *(ptrd++) = (T)_linear_atX(cimg::mod((float)*(ptrs0++),(float)_width),0,0,c);
  29747. }
  29748. break;
  29749. case 1 : // Neumann
  29750. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
  29751. cimg_forYZC(res,y,z,c) {
  29752. const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  29753. cimg_forX(res,x) *(ptrd++) = (T)_linear_atX((float)*(ptrs0++),0,0,c);
  29754. }
  29755. break;
  29756. default : // Dirichlet
  29757. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
  29758. cimg_forYZC(res,y,z,c) {
  29759. const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  29760. cimg_forX(res,x) *(ptrd++) = (T)linear_atX((float)*(ptrs0++),0,0,c,(T)0);
  29761. }
  29762. }
  29763. else // Nearest-neighbor interpolation
  29764. switch (boundary_conditions) {
  29765. case 3 : { // Mirror
  29766. const int w2 = 2*width();
  29767. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  29768. cimg_forYZC(res,y,z,c) {
  29769. const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  29770. cimg_forX(res,x) {
  29771. const int mx = cimg::mod((int)cimg::round(*(ptrs0++)),w2);
  29772. *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,0,0,c);
  29773. }
  29774. }
  29775. } break;
  29776. case 2 : // Periodic
  29777. cimg_forYZC(res,y,z,c) {
  29778. const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  29779. cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod((int)cimg::round(*(ptrs0++)),(int)_width),0,0,c);
  29780. }
  29781. break;
  29782. case 1 : // Neumann
  29783. cimg_forYZC(res,y,z,c) {
  29784. const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  29785. cimg_forX(res,x) *(ptrd++) = _atX((int)*(ptrs0++),0,0,c);
  29786. }
  29787. break;
  29788. default : // Dirichlet
  29789. cimg_forYZC(res,y,z,c) {
  29790. const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  29791. cimg_forX(res,x) *(ptrd++) = atX((int)*(ptrs0++),0,0,c,(T)0);
  29792. }
  29793. }
  29794. }
  29795. } else if (warp._spectrum==2) { // 2d warping
  29796. if (mode>=3) { // Forward-relative warp
  29797. res.fill((T)0);
  29798. if (interpolation>=1) // Linear interpolation
  29799. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  29800. cimg_forYZC(res,y,z,c) {
  29801. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c);
  29802. cimg_forX(res,x) res.set_linear_atXY(*(ptrs++),x + (float)*(ptrs0++),y + (float)*(ptrs1++),z,c);
  29803. }
  29804. else // Nearest-neighbor interpolation
  29805. cimg_forYZC(res,y,z,c) {
  29806. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c);
  29807. cimg_forX(res,x) {
  29808. const int X = x + (int)cimg::round(*(ptrs0++)), Y = y + (int)cimg::round(*(ptrs1++));
  29809. if (X>=0 && X<width() && Y>=0 && Y<height()) res(X,Y,z,c) = *(ptrs++);
  29810. }
  29811. }
  29812. } else if (mode==2) { // Forward-absolute warp
  29813. res.fill((T)0);
  29814. if (interpolation>=1) // Linear interpolation
  29815. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  29816. cimg_forYZC(res,y,z,c) {
  29817. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c);
  29818. cimg_forX(res,x) res.set_linear_atXY(*(ptrs++),(float)*(ptrs0++),(float)*(ptrs1++),z,c);
  29819. }
  29820. else // Nearest-neighbor interpolation
  29821. cimg_forYZC(res,y,z,c) {
  29822. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c);
  29823. cimg_forX(res,x) {
  29824. const int X = (int)cimg::round(*(ptrs0++)), Y = (int)cimg::round(*(ptrs1++));
  29825. if (X>=0 && X<width() && Y>=0 && Y<height()) res(X,Y,z,c) = *(ptrs++);
  29826. }
  29827. }
  29828. } else if (mode==1) { // Backward-relative warp
  29829. if (interpolation==2) // Cubic interpolation
  29830. switch (boundary_conditions) {
  29831. case 3 : { // Mirror
  29832. const float w2 = 2.0f*width(), h2 = 2.0f*height();
  29833. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  29834. cimg_forYZC(res,y,z,c) {
  29835. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
  29836. cimg_forX(res,x) {
  29837. const float
  29838. mx = cimg::mod(x - (float)*(ptrs0++),w2),
  29839. my = cimg::mod(y - (float)*(ptrs1++),h2);
  29840. *(ptrd++) = _cubic_cut_atXY(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,z,c);
  29841. }
  29842. }
  29843. } break;
  29844. case 2 : // Periodic
  29845. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  29846. cimg_forYZC(res,y,z,c) {
  29847. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
  29848. cimg_forX(res,x) *(ptrd++) = _cubic_cut_atXY(cimg::mod(x - (float)*(ptrs0++),(float)_width),
  29849. cimg::mod(y - (float)*(ptrs1++),(float)_height),z,c);
  29850. }
  29851. break;
  29852. case 1 : // Neumann
  29853. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  29854. cimg_forYZC(res,y,z,c) {
  29855. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
  29856. cimg_forX(res,x) *(ptrd++) = _cubic_cut_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c);
  29857. }
  29858. break;
  29859. default : // Dirichlet
  29860. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  29861. cimg_forYZC(res,y,z,c) {
  29862. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
  29863. cimg_forX(res,x) *(ptrd++) = cubic_cut_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c,(T)0);
  29864. }
  29865. }
  29866. else if (interpolation==1) // Linear interpolation
  29867. switch (boundary_conditions) {
  29868. case 3 : { // Mirror
  29869. const float w2 = 2.0f*width(), h2 = 2.0f*height();
  29870. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  29871. cimg_forYZC(res,y,z,c) {
  29872. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
  29873. cimg_forX(res,x) {
  29874. const float
  29875. mx = cimg::mod(x - (float)*(ptrs0++),w2),
  29876. my = cimg::mod(y - (float)*(ptrs1++),h2);
  29877. *(ptrd++) = (T)_linear_atXY(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,z,c);
  29878. }
  29879. }
  29880. } break;
  29881. case 2 : // Periodic
  29882. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
  29883. cimg_forYZC(res,y,z,c) {
  29884. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
  29885. cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY(cimg::mod(x - (float)*(ptrs0++),(float)_width),
  29886. cimg::mod(y - (float)*(ptrs1++),(float)_height),z,c);
  29887. }
  29888. break;
  29889. case 1 : // Neumann
  29890. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
  29891. cimg_forYZC(res,y,z,c) {
  29892. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
  29893. cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c);
  29894. }
  29895. break;
  29896. default : // Dirichlet
  29897. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
  29898. cimg_forYZC(res,y,z,c) {
  29899. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
  29900. cimg_forX(res,x) *(ptrd++) = (T)linear_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c,(T)0);
  29901. }
  29902. }
  29903. else // Nearest-neighbor interpolation
  29904. switch (boundary_conditions) {
  29905. case 3 : { // Mirror
  29906. const int w2 = 2*width(), h2 = 2*height();
  29907. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  29908. cimg_forYZC(res,y,z,c) {
  29909. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
  29910. cimg_forX(res,x) {
  29911. const int
  29912. mx = cimg::mod(x - (int)cimg::round(*(ptrs0++)),w2),
  29913. my = cimg::mod(y - (int)cimg::round(*(ptrs1++)),h2);
  29914. *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,z,c);
  29915. }
  29916. }
  29917. } break;
  29918. case 2 : // Periodic
  29919. cimg_forYZC(res,y,z,c) {
  29920. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
  29921. cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod(x - (int)cimg::round(*(ptrs0++)),(int)_width),
  29922. cimg::mod(y - (int)cimg::round(*(ptrs1++)),(int)_height),z,c);
  29923. }
  29924. break;
  29925. case 1 : // Neumann
  29926. cimg_forYZC(res,y,z,c) {
  29927. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
  29928. cimg_forX(res,x) *(ptrd++) = _atXY(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z,c);
  29929. }
  29930. break;
  29931. default : // Dirichlet
  29932. cimg_forYZC(res,y,z,c) {
  29933. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
  29934. cimg_forX(res,x) *(ptrd++) = atXY(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z,c,(T)0);
  29935. }
  29936. }
  29937. } else { // Backward-absolute warp
  29938. if (interpolation==2) // Cubic interpolation
  29939. switch (boundary_conditions) {
  29940. case 3 : { // Mirror
  29941. const float w2 = 2.0f*width(), h2 = 2.0f*height();
  29942. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  29943. cimg_forYZC(res,y,z,c) {
  29944. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
  29945. cimg_forX(res,x) {
  29946. const float
  29947. mx = cimg::mod((float)*(ptrs0++),w2),
  29948. my = cimg::mod((float)*(ptrs1++),h2);
  29949. *(ptrd++) = _cubic_cut_atXY(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,0,c);
  29950. }
  29951. }
  29952. } break;
  29953. case 2 : // Periodic
  29954. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  29955. cimg_forYZC(res,y,z,c) {
  29956. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
  29957. cimg_forX(res,x) *(ptrd++) = _cubic_cut_atXY(cimg::mod((float)*(ptrs0++),(float)_width),
  29958. cimg::mod((float)*(ptrs1++),(float)_height),0,c);
  29959. }
  29960. break;
  29961. case 1 : // Neumann
  29962. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  29963. cimg_forYZC(res,y,z,c) {
  29964. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
  29965. cimg_forX(res,x) *(ptrd++) = _cubic_cut_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c);
  29966. }
  29967. break;
  29968. default : // Dirichlet
  29969. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  29970. cimg_forYZC(res,y,z,c) {
  29971. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
  29972. cimg_forX(res,x) *(ptrd++) = cubic_cut_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c,(T)0);
  29973. }
  29974. }
  29975. else if (interpolation==1) // Linear interpolation
  29976. switch (boundary_conditions) {
  29977. case 3 : { // Mirror
  29978. const float w2 = 2.0f*width(), h2 = 2.0f*height();
  29979. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  29980. cimg_forYZC(res,y,z,c) {
  29981. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
  29982. cimg_forX(res,x) {
  29983. const float
  29984. mx = cimg::mod((float)*(ptrs0++),w2),
  29985. my = cimg::mod((float)*(ptrs1++),h2);
  29986. *(ptrd++) = (T)_linear_atXY(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,0,c);
  29987. }
  29988. }
  29989. } break;
  29990. case 2 : // Periodic
  29991. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
  29992. cimg_forYZC(res,y,z,c) {
  29993. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
  29994. cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY(cimg::mod((float)*(ptrs0++),(float)_width),
  29995. cimg::mod((float)*(ptrs1++),(float)_height),0,c);
  29996. }
  29997. break;
  29998. case 1 : // Neumann
  29999. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
  30000. cimg_forYZC(res,y,z,c) {
  30001. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
  30002. cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c);
  30003. }
  30004. break;
  30005. default : // Dirichlet
  30006. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
  30007. cimg_forYZC(res,y,z,c) {
  30008. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
  30009. cimg_forX(res,x) *(ptrd++) = (T)linear_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c,(T)0);
  30010. }
  30011. }
  30012. else // Nearest-neighbor interpolation
  30013. switch (boundary_conditions) {
  30014. case 3 : { // Mirror
  30015. const int w2 = 2*width(), h2 = 2*height();
  30016. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  30017. cimg_forYZC(res,y,z,c) {
  30018. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
  30019. cimg_forX(res,x) {
  30020. const int
  30021. mx = cimg::mod((int)cimg::round(*(ptrs0++)),w2),
  30022. my = cimg::mod((int)cimg::round(*(ptrs1++)),h2);
  30023. *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,0,c);
  30024. }
  30025. }
  30026. } break;
  30027. case 2 : // Periodic
  30028. cimg_forYZC(res,y,z,c) {
  30029. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
  30030. cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod((int)cimg::round(*(ptrs0++)),(int)_width),
  30031. cimg::mod((int)cimg::round(*(ptrs1++)),(int)_height),0,c);
  30032. }
  30033. break;
  30034. case 1 : // Neumann
  30035. cimg_forYZC(res,y,z,c) {
  30036. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
  30037. cimg_forX(res,x) *(ptrd++) = _atXY((int)*(ptrs0++),(int)*(ptrs1++),0,c);
  30038. }
  30039. break;
  30040. default : // Dirichlet
  30041. cimg_forYZC(res,y,z,c) {
  30042. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
  30043. cimg_forX(res,x) *(ptrd++) = atXY((int)*(ptrs0++),(int)*(ptrs1++),0,c,(T)0);
  30044. }
  30045. }
  30046. }
  30047. } else { // 3d warping
  30048. if (mode>=3) { // Forward-relative warp
  30049. res.fill((T)0);
  30050. if (interpolation>=1) // Linear interpolation
  30051. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  30052. cimg_forYZC(res,y,z,c) {
  30053. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
  30054. const T *ptrs = data(0,y,z,c);
  30055. cimg_forX(res,x) res.set_linear_atXYZ(*(ptrs++),x + (float)*(ptrs0++),y + (float)*(ptrs1++),
  30056. z + (float)*(ptrs2++),c);
  30057. }
  30058. else // Nearest-neighbor interpolation
  30059. cimg_forYZC(res,y,z,c) {
  30060. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
  30061. const T *ptrs = data(0,y,z,c);
  30062. cimg_forX(res,x) {
  30063. const int
  30064. X = x + (int)cimg::round(*(ptrs0++)),
  30065. Y = y + (int)cimg::round(*(ptrs1++)),
  30066. Z = z + (int)cimg::round(*(ptrs2++));
  30067. if (X>=0 && X<width() && Y>=0 && Y<height() && Z>=0 && Z<depth()) res(X,Y,Z,c) = *(ptrs++);
  30068. }
  30069. }
  30070. } else if (mode==2) { // Forward-absolute warp
  30071. res.fill((T)0);
  30072. if (interpolation>=1) // Linear interpolation
  30073. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  30074. cimg_forYZC(res,y,z,c) {
  30075. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
  30076. const T *ptrs = data(0,y,z,c);
  30077. cimg_forX(res,x) res.set_linear_atXYZ(*(ptrs++),(float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
  30078. }
  30079. else // Nearest-neighbor interpolation
  30080. cimg_forYZC(res,y,z,c) {
  30081. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
  30082. const T *ptrs = data(0,y,z,c);
  30083. cimg_forX(res,x) {
  30084. const int
  30085. X = (int)cimg::round(*(ptrs0++)),
  30086. Y = (int)cimg::round(*(ptrs1++)),
  30087. Z = (int)cimg::round(*(ptrs2++));
  30088. if (X>=0 && X<width() && Y>=0 && Y<height() && Z>=0 && Z<depth()) res(X,Y,Z,c) = *(ptrs++);
  30089. }
  30090. }
  30091. } else if (mode==1) { // Backward-relative warp
  30092. if (interpolation==2) // Cubic interpolation
  30093. switch (boundary_conditions) {
  30094. case 3 : { // Mirror
  30095. const float w2 = 2.0f*width(), h2 = 2.0f*height(), d2 = 2.0f*depth();
  30096. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  30097. cimg_forYZC(res,y,z,c) {
  30098. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
  30099. T *ptrd = res.data(0,y,z,c);
  30100. cimg_forX(res,x) {
  30101. const float
  30102. mx = cimg::mod(x - (float)*(ptrs0++),w2),
  30103. my = cimg::mod(y - (float)*(ptrs1++),h2),
  30104. mz = cimg::mod(z - (float)*(ptrs2++),d2);
  30105. *(ptrd++) = _cubic_cut_atXYZ(mx<width()?mx:w2 - mx - 1,
  30106. my<height()?my:h2 - my - 1,
  30107. mz<depth()?mz:d2 - mz - 1,c);
  30108. }
  30109. }
  30110. } break;
  30111. case 2 : // Periodic
  30112. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  30113. cimg_forYZC(res,y,z,c) {
  30114. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
  30115. T *ptrd = res.data(0,y,z,c);
  30116. cimg_forX(res,x) *(ptrd++) = _cubic_cut_atXYZ(cimg::mod(x - (float)*(ptrs0++),(float)_width),
  30117. cimg::mod(y - (float)*(ptrs1++),(float)_height),
  30118. cimg::mod(z - (float)*(ptrs2++),(float)_depth),c);
  30119. }
  30120. break;
  30121. case 1 : // Neumann
  30122. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  30123. cimg_forYZC(res,y,z,c) {
  30124. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
  30125. T *ptrd = res.data(0,y,z,c);
  30126. cimg_forX(res,x)
  30127. *(ptrd++) = _cubic_cut_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c);
  30128. }
  30129. break;
  30130. default : // Dirichlet
  30131. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  30132. cimg_forYZC(res,y,z,c) {
  30133. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
  30134. T *ptrd = res.data(0,y,z,c);
  30135. cimg_forX(res,x)
  30136. *(ptrd++) = cubic_cut_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c,(T)0);
  30137. }
  30138. }
  30139. else if (interpolation==1) // Linear interpolation
  30140. switch (boundary_conditions) {
  30141. case 3 : { // Mirror
  30142. const float w2 = 2.0f*width(), h2 = 2.0f*height(), d2 = 2.0f*depth();
  30143. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  30144. cimg_forYZC(res,y,z,c) {
  30145. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
  30146. T *ptrd = res.data(0,y,z,c);
  30147. cimg_forX(res,x) {
  30148. const float
  30149. mx = cimg::mod(x - (float)*(ptrs0++),w2),
  30150. my = cimg::mod(y - (float)*(ptrs1++),h2),
  30151. mz = cimg::mod(z - (float)*(ptrs2++),d2);
  30152. *(ptrd++) = (T)_linear_atXYZ(mx<width()?mx:w2 - mx - 1,
  30153. my<height()?my:h2 - my - 1,
  30154. mz<depth()?mz:d2 - mz - 1,c);
  30155. }
  30156. }
  30157. } break;
  30158. case 2 : // Periodic
  30159. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
  30160. cimg_forYZC(res,y,z,c) {
  30161. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
  30162. T *ptrd = res.data(0,y,z,c);
  30163. cimg_forX(res,x) *(ptrd++) = (T)_linear_atXYZ(cimg::mod(x - (float)*(ptrs0++),(float)_width),
  30164. cimg::mod(y - (float)*(ptrs1++),(float)_height),
  30165. cimg::mod(z - (float)*(ptrs2++),(float)_depth),c);
  30166. }
  30167. break;
  30168. case 1 : // Neumann
  30169. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
  30170. cimg_forYZC(res,y,z,c) {
  30171. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
  30172. T *ptrd = res.data(0,y,z,c);
  30173. cimg_forX(res,x)
  30174. *(ptrd++) = (T)_linear_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c);
  30175. }
  30176. break;
  30177. default : // Dirichlet
  30178. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
  30179. cimg_forYZC(res,y,z,c) {
  30180. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
  30181. T *ptrd = res.data(0,y,z,c);
  30182. cimg_forX(res,x)
  30183. *(ptrd++) = (T)linear_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c,(T)0);
  30184. }
  30185. }
  30186. else // Nearest neighbor interpolation
  30187. switch (boundary_conditions) {
  30188. case 3 : { // Mirror
  30189. const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth();
  30190. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  30191. cimg_forYZC(res,y,z,c) {
  30192. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
  30193. T *ptrd = res.data(0,y,z,c);
  30194. cimg_forX(res,x) {
  30195. const int
  30196. mx = cimg::mod(x - (int)cimg::round(*(ptrs0++)),w2),
  30197. my = cimg::mod(y - (int)cimg::round(*(ptrs1++)),h2),
  30198. mz = cimg::mod(z - (int)cimg::round(*(ptrs2++)),d2);
  30199. *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,
  30200. my<height()?my:h2 - my - 1,
  30201. mz<depth()?mz:d2 - mz - 1,c);
  30202. }
  30203. }
  30204. } break;
  30205. case 2 : // Periodic
  30206. cimg_forYZC(res,y,z,c) {
  30207. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
  30208. T *ptrd = res.data(0,y,z,c);
  30209. cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod(x - (int)cimg::round(*(ptrs0++)),(int)_width),
  30210. cimg::mod(y - (int)cimg::round(*(ptrs1++)),(int)_height),
  30211. cimg::mod(z - (int)cimg::round(*(ptrs2++)),(int)_depth),c);
  30212. }
  30213. break;
  30214. case 1 : // Neumann
  30215. cimg_forYZC(res,y,z,c) {
  30216. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
  30217. T *ptrd = res.data(0,y,z,c);
  30218. cimg_forX(res,x) *(ptrd++) = _atXYZ(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z - (int)*(ptrs2++),c);
  30219. }
  30220. break;
  30221. default : // Dirichlet
  30222. cimg_forYZC(res,y,z,c) {
  30223. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
  30224. T *ptrd = res.data(0,y,z,c);
  30225. cimg_forX(res,x) *(ptrd++) = atXYZ(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z - (int)*(ptrs2++),c,(T)0);
  30226. }
  30227. }
  30228. } else { // Backward-absolute warp
  30229. if (interpolation==2) // Cubic interpolation
  30230. switch (boundary_conditions) {
  30231. case 3 : { // Mirror
  30232. const float w2 = 2.0f*width(), h2 = 2.0f*height(), d2 = 2.0f*depth();
  30233. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  30234. cimg_forYZC(res,y,z,c) {
  30235. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
  30236. T *ptrd = res.data(0,y,z,c);
  30237. cimg_forX(res,x) {
  30238. const float
  30239. mx = cimg::mod((float)*(ptrs0++),w2),
  30240. my = cimg::mod((float)*(ptrs1++),h2),
  30241. mz = cimg::mod((float)*(ptrs2++),d2);
  30242. *(ptrd++) = _cubic_cut_atXYZ(mx<width()?mx:w2 - mx - 1,
  30243. my<height()?my:h2 - my - 1,
  30244. mz<depth()?mz:d2 - mz - 1,c);
  30245. }
  30246. }
  30247. } break;
  30248. case 2 : // Periodic
  30249. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  30250. cimg_forYZC(res,y,z,c) {
  30251. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
  30252. T *ptrd = res.data(0,y,z,c);
  30253. cimg_forX(res,x) *(ptrd++) = _cubic_cut_atXYZ(cimg::mod((float)*(ptrs0++),(float)_width),
  30254. cimg::mod((float)*(ptrs1++),(float)_height),
  30255. cimg::mod((float)*(ptrs2++),(float)_depth),c);
  30256. }
  30257. break;
  30258. case 1 : // Neumann
  30259. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  30260. cimg_forYZC(res,y,z,c) {
  30261. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
  30262. T *ptrd = res.data(0,y,z,c);
  30263. cimg_forX(res,x) *(ptrd++) = _cubic_cut_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
  30264. }
  30265. break;
  30266. default : // Dirichlet
  30267. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  30268. cimg_forYZC(res,y,z,c) {
  30269. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
  30270. T *ptrd = res.data(0,y,z,c);
  30271. cimg_forX(res,x) *(ptrd++) = cubic_cut_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),
  30272. c,(T)0);
  30273. }
  30274. }
  30275. else if (interpolation==1) // Linear interpolation
  30276. switch (boundary_conditions) {
  30277. case 3 : { // Mirror
  30278. const float w2 = 2.0f*width(), h2 = 2.0f*height(), d2 = 2.0f*depth();
  30279. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  30280. cimg_forYZC(res,y,z,c) {
  30281. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
  30282. T *ptrd = res.data(0,y,z,c);
  30283. cimg_forX(res,x) {
  30284. const float
  30285. mx = cimg::mod((float)*(ptrs0++),w2),
  30286. my = cimg::mod((float)*(ptrs1++),h2),
  30287. mz = cimg::mod((float)*(ptrs2++),d2);
  30288. *(ptrd++) = (T)_linear_atXYZ(mx<width()?mx:w2 - mx - 1,
  30289. my<height()?my:h2 - my - 1,
  30290. mz<depth()?mz:d2 - mz - 1,c);
  30291. }
  30292. }
  30293. } break;
  30294. case 2 :// Periodic
  30295. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
  30296. cimg_forYZC(res,y,z,c) {
  30297. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
  30298. T *ptrd = res.data(0,y,z,c);
  30299. cimg_forX(res,x) *(ptrd++) = (T)_linear_atXYZ(cimg::mod((float)*(ptrs0++),(float)_width),
  30300. cimg::mod((float)*(ptrs1++),(float)_height),
  30301. cimg::mod((float)*(ptrs2++),(float)_depth),c);
  30302. }
  30303. break;
  30304. case 1 : // Neumann
  30305. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
  30306. cimg_forYZC(res,y,z,c) {
  30307. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
  30308. T *ptrd = res.data(0,y,z,c);
  30309. cimg_forX(res,x) *(ptrd++) = (T)_linear_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
  30310. }
  30311. break;
  30312. default : // Dirichlet
  30313. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
  30314. cimg_forYZC(res,y,z,c) {
  30315. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
  30316. T *ptrd = res.data(0,y,z,c);
  30317. cimg_forX(res,x) *(ptrd++) = (T)linear_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),
  30318. c,(T)0);
  30319. }
  30320. }
  30321. else // Nearest-neighbor interpolation
  30322. switch (boundary_conditions) {
  30323. case 3 : { // Mirror
  30324. const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth();
  30325. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
  30326. cimg_forYZC(res,y,z,c) {
  30327. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
  30328. T *ptrd = res.data(0,y,z,c);
  30329. cimg_forX(res,x) {
  30330. const int
  30331. mx = cimg::mod((int)cimg::round(*(ptrs0++)),w2),
  30332. my = cimg::mod((int)cimg::round(*(ptrs1++)),h2),
  30333. mz = cimg::mod((int)cimg::round(*(ptrs2++)),d2);
  30334. *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,
  30335. my<height()?my:h2 - my - 1,
  30336. mz<depth()?mz:d2 - mz - 1,c);
  30337. }
  30338. }
  30339. } break;
  30340. case 2 : // Periodic
  30341. cimg_forYZC(res,y,z,c) {
  30342. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
  30343. T *ptrd = res.data(0,y,z,c);
  30344. cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod((int)cimg::round(*(ptrs0++)),(int)_width),
  30345. cimg::mod((int)cimg::round(*(ptrs1++)),(int)_height),
  30346. cimg::mod((int)cimg::round(*(ptrs2++)),(int)_depth),c);
  30347. }
  30348. break;
  30349. case 1 : // Neumann
  30350. cimg_forYZC(res,y,z,c) {
  30351. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
  30352. T *ptrd = res.data(0,y,z,c);
  30353. cimg_forX(res,x) *(ptrd++) = _atXYZ((int)*(ptrs0++),(int)*(ptrs1++),(int)*(ptrs2++),c);
  30354. }
  30355. break;
  30356. default : // Dirichlet
  30357. cimg_forYZC(res,y,z,c) {
  30358. const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
  30359. T *ptrd = res.data(0,y,z,c);
  30360. cimg_forX(res,x) *(ptrd++) = atXYZ((int)*(ptrs0++),(int)*(ptrs1++),(int)*(ptrs2++),c,(T)0);
  30361. }
  30362. }
  30363. }
  30364. }
  30365. return res;
  30366. }
  30367. //! Generate a 2d representation of a 3d image, with XY,XZ and YZ views.
  30368. /**
  30369. \param x0 X-coordinate of the projection point.
  30370. \param y0 Y-coordinate of the projection point.
  30371. \param z0 Z-coordinate of the projection point.
  30372. **/
  30373. CImg<T> get_projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) const {
  30374. if (is_empty() || _depth<2) return +*this;
  30375. const unsigned int
  30376. _x0 = (x0>=_width)?_width - 1:x0,
  30377. _y0 = (y0>=_height)?_height - 1:y0,
  30378. _z0 = (z0>=_depth)?_depth - 1:z0;
  30379. const CImg<T>
  30380. img_xy = get_crop(0,0,_z0,0,_width - 1,_height - 1,_z0,_spectrum - 1),
  30381. img_zy = get_crop(_x0,0,0,0,_x0,_height - 1,_depth - 1,_spectrum - 1).permute_axes("xzyc").
  30382. resize(_depth,_height,1,-100,-1),
  30383. img_xz = get_crop(0,_y0,0,0,_width - 1,_y0,_depth - 1,_spectrum - 1).resize(_width,_depth,1,-100,-1);
  30384. return CImg<T>(_width + _depth,_height + _depth,1,_spectrum,cimg::min(img_xy.min(),img_zy.min(),img_xz.min())).
  30385. draw_image(0,0,img_xy).draw_image(img_xy._width,0,img_zy).
  30386. draw_image(0,img_xy._height,img_xz);
  30387. }
  30388. //! Construct a 2d representation of a 3d image, with XY,XZ and YZ views \inplace.
  30389. CImg<T>& projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) {
  30390. if (_depth<2) return *this;
  30391. return get_projections2d(x0,y0,z0).move_to(*this);
  30392. }
  30393. //! Crop image region.
  30394. /**
  30395. \param x0 = X-coordinate of the upper-left crop rectangle corner.
  30396. \param y0 = Y-coordinate of the upper-left crop rectangle corner.
  30397. \param z0 = Z-coordinate of the upper-left crop rectangle corner.
  30398. \param c0 = C-coordinate of the upper-left crop rectangle corner.
  30399. \param x1 = X-coordinate of the lower-right crop rectangle corner.
  30400. \param y1 = Y-coordinate of the lower-right crop rectangle corner.
  30401. \param z1 = Z-coordinate of the lower-right crop rectangle corner.
  30402. \param c1 = C-coordinate of the lower-right crop rectangle corner.
  30403. \param boundary_conditions = Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
  30404. **/
  30405. CImg<T>& crop(const int x0, const int y0, const int z0, const int c0,
  30406. const int x1, const int y1, const int z1, const int c1,
  30407. const unsigned int boundary_conditions=0) {
  30408. return get_crop(x0,y0,z0,c0,x1,y1,z1,c1,boundary_conditions).move_to(*this);
  30409. }
  30410. //! Crop image region \newinstance.
  30411. CImg<T> get_crop(const int x0, const int y0, const int z0, const int c0,
  30412. const int x1, const int y1, const int z1, const int c1,
  30413. const unsigned int boundary_conditions=0) const {
  30414. if (is_empty())
  30415. throw CImgInstanceException(_cimg_instance
  30416. "crop(): Empty instance.",
  30417. cimg_instance);
  30418. const int
  30419. nx0 = x0<x1?x0:x1, nx1 = x0^x1^nx0,
  30420. ny0 = y0<y1?y0:y1, ny1 = y0^y1^ny0,
  30421. nz0 = z0<z1?z0:z1, nz1 = z0^z1^nz0,
  30422. nc0 = c0<c1?c0:c1, nc1 = c0^c1^nc0;
  30423. CImg<T> res(1U + nx1 - nx0,1U + ny1 - ny0,1U + nz1 - nz0,1U + nc1 - nc0);
  30424. if (nx0<0 || nx1>=width() || ny0<0 || ny1>=height() || nz0<0 || nz1>=depth() || nc0<0 || nc1>=spectrum())
  30425. switch (boundary_conditions) {
  30426. case 3 : { // Mirror
  30427. const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth(), s2 = 2*spectrum();
  30428. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=16 && _height*_depth*_spectrum>=4))
  30429. cimg_forXYZC(res,x,y,z,c) {
  30430. const int
  30431. mx = cimg::mod(nx0 + x,w2),
  30432. my = cimg::mod(ny0 + y,h2),
  30433. mz = cimg::mod(nz0 + z,d2),
  30434. mc = cimg::mod(nc0 + c,s2);
  30435. res(x,y,z,c) = (*this)(mx<width()?mx:w2 - mx - 1,
  30436. my<height()?my:h2 - my - 1,
  30437. mz<depth()?mz:d2 - mz - 1,
  30438. mc<spectrum()?mc:s2 - mc - 1);
  30439. }
  30440. } break;
  30441. case 2 : { // Periodic
  30442. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=16 && _height*_depth*_spectrum>=4))
  30443. cimg_forXYZC(res,x,y,z,c) {
  30444. res(x,y,z,c) = (*this)(cimg::mod(nx0 + x,width()),cimg::mod(ny0 + y,height()),
  30445. cimg::mod(nz0 + z,depth()),cimg::mod(nc0 + c,spectrum()));
  30446. }
  30447. } break;
  30448. case 1 : // Neumann
  30449. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=16 && _height*_depth*_spectrum>=4))
  30450. cimg_forXYZC(res,x,y,z,c) res(x,y,z,c) = _atXYZC(nx0 + x,ny0 + y,nz0 + z,nc0 + c);
  30451. break;
  30452. default : // Dirichlet
  30453. res.fill((T)0).draw_image(-nx0,-ny0,-nz0,-nc0,*this);
  30454. }
  30455. else res.draw_image(-nx0,-ny0,-nz0,-nc0,*this);
  30456. return res;
  30457. }
  30458. //! Crop image region \overloading.
  30459. CImg<T>& crop(const int x0, const int y0, const int z0,
  30460. const int x1, const int y1, const int z1,
  30461. const unsigned int boundary_conditions=0) {
  30462. return crop(x0,y0,z0,0,x1,y1,z1,_spectrum - 1,boundary_conditions);
  30463. }
  30464. //! Crop image region \newinstance.
  30465. CImg<T> get_crop(const int x0, const int y0, const int z0,
  30466. const int x1, const int y1, const int z1,
  30467. const unsigned int boundary_conditions=0) const {
  30468. return get_crop(x0,y0,z0,0,x1,y1,z1,_spectrum - 1,boundary_conditions);
  30469. }
  30470. //! Crop image region \overloading.
  30471. CImg<T>& crop(const int x0, const int y0,
  30472. const int x1, const int y1,
  30473. const unsigned int boundary_conditions=0) {
  30474. return crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,boundary_conditions);
  30475. }
  30476. //! Crop image region \newinstance.
  30477. CImg<T> get_crop(const int x0, const int y0,
  30478. const int x1, const int y1,
  30479. const unsigned int boundary_conditions=0) const {
  30480. return get_crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,boundary_conditions);
  30481. }
  30482. //! Crop image region \overloading.
  30483. CImg<T>& crop(const int x0, const int x1, const unsigned int boundary_conditions=0) {
  30484. return crop(x0,0,0,0,x1,_height - 1,_depth - 1,_spectrum - 1,boundary_conditions);
  30485. }
  30486. //! Crop image region \newinstance.
  30487. CImg<T> get_crop(const int x0, const int x1, const unsigned int boundary_conditions=0) const {
  30488. return get_crop(x0,0,0,0,x1,_height - 1,_depth - 1,_spectrum - 1,boundary_conditions);
  30489. }
  30490. //! Autocrop image region, regarding the specified background value.
  30491. CImg<T>& autocrop(const T& value, const char *const axes="czyx") {
  30492. if (is_empty()) return *this;
  30493. for (const char *s = axes; *s; ++s) {
  30494. const char axis = cimg::lowercase(*s);
  30495. const CImg<intT> coords = _autocrop(value,axis);
  30496. if (coords[0]==-1 && coords[1]==-1) return assign(); // Image has only 'value' pixels.
  30497. else switch (axis) {
  30498. case 'x' : {
  30499. const int x0 = coords[0], x1 = coords[1];
  30500. if (x0>=0 && x1>=0) crop(x0,x1);
  30501. } break;
  30502. case 'y' : {
  30503. const int y0 = coords[0], y1 = coords[1];
  30504. if (y0>=0 && y1>=0) crop(0,y0,_width - 1,y1);
  30505. } break;
  30506. case 'z' : {
  30507. const int z0 = coords[0], z1 = coords[1];
  30508. if (z0>=0 && z1>=0) crop(0,0,z0,_width - 1,_height - 1,z1);
  30509. } break;
  30510. default : {
  30511. const int c0 = coords[0], c1 = coords[1];
  30512. if (c0>=0 && c1>=0) crop(0,0,0,c0,_width - 1,_height - 1,_depth - 1,c1);
  30513. }
  30514. }
  30515. }
  30516. return *this;
  30517. }
  30518. //! Autocrop image region, regarding the specified background value \newinstance.
  30519. CImg<T> get_autocrop(const T& value, const char *const axes="czyx") const {
  30520. return (+*this).autocrop(value,axes);
  30521. }
  30522. //! Autocrop image region, regarding the specified background color.
  30523. /**
  30524. \param color Color used for the crop. If \c 0, color is guessed.
  30525. \param axes Axes used for the crop.
  30526. **/
  30527. CImg<T>& autocrop(const T *const color=0, const char *const axes="zyx") {
  30528. if (is_empty()) return *this;
  30529. if (!color) { // Guess color.
  30530. const CImg<T> col1 = get_vector_at(0,0,0);
  30531. const unsigned int w = _width, h = _height, d = _depth, s = _spectrum;
  30532. autocrop(col1,axes);
  30533. if (_width==w && _height==h && _depth==d && _spectrum==s) {
  30534. const CImg<T> col2 = get_vector_at(w - 1,h - 1,d - 1);
  30535. autocrop(col2,axes);
  30536. }
  30537. return *this;
  30538. }
  30539. for (const char *s = axes; *s; ++s) {
  30540. const char axis = cimg::lowercase(*s);
  30541. switch (axis) {
  30542. case 'x' : {
  30543. int x0 = width(), x1 = -1;
  30544. cimg_forC(*this,c) {
  30545. const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'x');
  30546. const int nx0 = coords[0], nx1 = coords[1];
  30547. if (nx0>=0 && nx1>=0) { x0 = std::min(x0,nx0); x1 = std::max(x1,nx1); }
  30548. }
  30549. if (x0==width() && x1==-1) return assign(); else crop(x0,x1);
  30550. } break;
  30551. case 'y' : {
  30552. int y0 = height(), y1 = -1;
  30553. cimg_forC(*this,c) {
  30554. const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'y');
  30555. const int ny0 = coords[0], ny1 = coords[1];
  30556. if (ny0>=0 && ny1>=0) { y0 = std::min(y0,ny0); y1 = std::max(y1,ny1); }
  30557. }
  30558. if (y0==height() && y1==-1) return assign(); else crop(0,y0,_width - 1,y1);
  30559. } break;
  30560. default : {
  30561. int z0 = depth(), z1 = -1;
  30562. cimg_forC(*this,c) {
  30563. const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'z');
  30564. const int nz0 = coords[0], nz1 = coords[1];
  30565. if (nz0>=0 && nz1>=0) { z0 = std::min(z0,nz0); z1 = std::max(z1,nz1); }
  30566. }
  30567. if (z0==depth() && z1==-1) return assign(); else crop(0,0,z0,_width - 1,_height - 1,z1);
  30568. }
  30569. }
  30570. }
  30571. return *this;
  30572. }
  30573. //! Autocrop image region, regarding the specified background color \newinstance.
  30574. CImg<T> get_autocrop(const T *const color=0, const char *const axes="zyx") const {
  30575. return (+*this).autocrop(color,axes);
  30576. }
  30577. //! Autocrop image region, regarding the specified background color \overloading.
  30578. template<typename t> CImg<T>& autocrop(const CImg<t>& color, const char *const axes="zyx") {
  30579. return get_autocrop(color,axes).move_to(*this);
  30580. }
  30581. //! Autocrop image region, regarding the specified background color \newinstance.
  30582. template<typename t> CImg<T> get_autocrop(const CImg<t>& color, const char *const axes="zyx") const {
  30583. return get_autocrop(color._data,axes);
  30584. }
  30585. CImg<intT> _autocrop(const T& value, const char axis) const {
  30586. CImg<intT> res;
  30587. switch (cimg::lowercase(axis)) {
  30588. case 'x' : {
  30589. int x0 = -1, x1 = -1;
  30590. cimg_forX(*this,x) cimg_forYZC(*this,y,z,c)
  30591. if ((*this)(x,y,z,c)!=value) { x0 = x; x = width(); y = height(); z = depth(); c = spectrum(); }
  30592. if (x0>=0) {
  30593. for (int x = width() - 1; x>=0; --x) cimg_forYZC(*this,y,z,c)
  30594. if ((*this)(x,y,z,c)!=value) { x1 = x; x = 0; y = height(); z = depth(); c = spectrum(); }
  30595. }
  30596. res = CImg<intT>::vector(x0,x1);
  30597. } break;
  30598. case 'y' : {
  30599. int y0 = -1, y1 = -1;
  30600. cimg_forY(*this,y) cimg_forXZC(*this,x,z,c)
  30601. if ((*this)(x,y,z,c)!=value) { y0 = y; x = width(); y = height(); z = depth(); c = spectrum(); }
  30602. if (y0>=0) {
  30603. for (int y = height() - 1; y>=0; --y) cimg_forXZC(*this,x,z,c)
  30604. if ((*this)(x,y,z,c)!=value) { y1 = y; x = width(); y = 0; z = depth(); c = spectrum(); }
  30605. }
  30606. res = CImg<intT>::vector(y0,y1);
  30607. } break;
  30608. case 'z' : {
  30609. int z0 = -1, z1 = -1;
  30610. cimg_forZ(*this,z) cimg_forXYC(*this,x,y,c)
  30611. if ((*this)(x,y,z,c)!=value) { z0 = z; x = width(); y = height(); z = depth(); c = spectrum(); }
  30612. if (z0>=0) {
  30613. for (int z = depth() - 1; z>=0; --z) cimg_forXYC(*this,x,y,c)
  30614. if ((*this)(x,y,z,c)!=value) { z1 = z; x = width(); y = height(); z = 0; c = spectrum(); }
  30615. }
  30616. res = CImg<intT>::vector(z0,z1);
  30617. } break;
  30618. default : {
  30619. int c0 = -1, c1 = -1;
  30620. cimg_forC(*this,c) cimg_forXYZ(*this,x,y,z)
  30621. if ((*this)(x,y,z,c)!=value) { c0 = c; x = width(); y = height(); z = depth(); c = spectrum(); }
  30622. if (c0>=0) {
  30623. for (int c = spectrum() - 1; c>=0; --c) cimg_forXYZ(*this,x,y,z)
  30624. if ((*this)(x,y,z,c)!=value) { c1 = c; x = width(); y = height(); z = depth(); c = 0; }
  30625. }
  30626. res = CImg<intT>::vector(c0,c1);
  30627. }
  30628. }
  30629. return res;
  30630. }
  30631. //! Return specified image column.
  30632. /**
  30633. \param x0 Image column.
  30634. **/
  30635. CImg<T> get_column(const int x0) const {
  30636. return get_columns(x0,x0);
  30637. }
  30638. //! Return specified image column \inplace.
  30639. CImg<T>& column(const int x0) {
  30640. return columns(x0,x0);
  30641. }
  30642. //! Return specified range of image columns.
  30643. /**
  30644. \param x0 Starting image column.
  30645. \param x1 Ending image column.
  30646. **/
  30647. CImg<T>& columns(const int x0, const int x1) {
  30648. return get_columns(x0,x1).move_to(*this);
  30649. }
  30650. //! Return specified range of image columns \inplace.
  30651. CImg<T> get_columns(const int x0, const int x1) const {
  30652. return get_crop(x0,0,0,0,x1,height() - 1,depth() - 1,spectrum() - 1);
  30653. }
  30654. //! Return specified image row.
  30655. CImg<T> get_row(const int y0) const {
  30656. return get_rows(y0,y0);
  30657. }
  30658. //! Return specified image row \inplace.
  30659. /**
  30660. \param y0 Image row.
  30661. **/
  30662. CImg<T>& row(const int y0) {
  30663. return rows(y0,y0);
  30664. }
  30665. //! Return specified range of image rows.
  30666. /**
  30667. \param y0 Starting image row.
  30668. \param y1 Ending image row.
  30669. **/
  30670. CImg<T> get_rows(const int y0, const int y1) const {
  30671. return get_crop(0,y0,0,0,width() - 1,y1,depth() - 1,spectrum() - 1);
  30672. }
  30673. //! Return specified range of image rows \inplace.
  30674. CImg<T>& rows(const int y0, const int y1) {
  30675. return get_rows(y0,y1).move_to(*this);
  30676. }
  30677. //! Return specified image slice.
  30678. /**
  30679. \param z0 Image slice.
  30680. **/
  30681. CImg<T> get_slice(const int z0) const {
  30682. return get_slices(z0,z0);
  30683. }
  30684. //! Return specified image slice \inplace.
  30685. CImg<T>& slice(const int z0) {
  30686. return slices(z0,z0);
  30687. }
  30688. //! Return specified range of image slices.
  30689. /**
  30690. \param z0 Starting image slice.
  30691. \param z1 Ending image slice.
  30692. **/
  30693. CImg<T> get_slices(const int z0, const int z1) const {
  30694. return get_crop(0,0,z0,0,width() - 1,height() - 1,z1,spectrum() - 1);
  30695. }
  30696. //! Return specified range of image slices \inplace.
  30697. CImg<T>& slices(const int z0, const int z1) {
  30698. return get_slices(z0,z1).move_to(*this);
  30699. }
  30700. //! Return specified image channel.
  30701. /**
  30702. \param c0 Image channel.
  30703. **/
  30704. CImg<T> get_channel(const int c0) const {
  30705. return get_channels(c0,c0);
  30706. }
  30707. //! Return specified image channel \inplace.
  30708. CImg<T>& channel(const int c0) {
  30709. return channels(c0,c0);
  30710. }
  30711. //! Return specified range of image channels.
  30712. /**
  30713. \param c0 Starting image channel.
  30714. \param c1 Ending image channel.
  30715. **/
  30716. CImg<T> get_channels(const int c0, const int c1) const {
  30717. return get_crop(0,0,0,c0,width() - 1,height() - 1,depth() - 1,c1);
  30718. }
  30719. //! Return specified range of image channels \inplace.
  30720. CImg<T>& channels(const int c0, const int c1) {
  30721. return get_channels(c0,c1).move_to(*this);
  30722. }
  30723. //! Return stream line of a 2d or 3d vector field.
  30724. CImg<floatT> get_streamline(const float x, const float y, const float z,
  30725. const float L=256, const float dl=0.1f,
  30726. const unsigned int interpolation_type=2, const bool is_backward_tracking=false,
  30727. const bool is_oriented_only=false) const {
  30728. if (_spectrum!=2 && _spectrum!=3)
  30729. throw CImgInstanceException(_cimg_instance
  30730. "streamline(): Instance is not a 2d or 3d vector field.",
  30731. cimg_instance);
  30732. if (_spectrum==2) {
  30733. if (is_oriented_only) {
  30734. typename CImg<T>::_functor4d_streamline2d_oriented func(*this);
  30735. return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true,
  30736. 0,0,0,_width - 1.0f,_height - 1.0f,0.0f);
  30737. } else {
  30738. typename CImg<T>::_functor4d_streamline2d_directed func(*this);
  30739. return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false,
  30740. 0,0,0,_width - 1.0f,_height - 1.0f,0.0f);
  30741. }
  30742. }
  30743. if (is_oriented_only) {
  30744. typename CImg<T>::_functor4d_streamline3d_oriented func(*this);
  30745. return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true,
  30746. 0,0,0,_width - 1.0f,_height - 1.0f,_depth - 1.0f);
  30747. }
  30748. typename CImg<T>::_functor4d_streamline3d_directed func(*this);
  30749. return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false,
  30750. 0,0,0,_width - 1.0f,_height - 1.0f,_depth - 1.0f);
  30751. }
  30752. //! Return stream line of a 3d vector field.
  30753. /**
  30754. \param func Vector field function.
  30755. \param x X-coordinate of the starting point of the streamline.
  30756. \param y Y-coordinate of the starting point of the streamline.
  30757. \param z Z-coordinate of the starting point of the streamline.
  30758. \param L Streamline length.
  30759. \param dl Streamline length increment.
  30760. \param interpolation_type Type of interpolation.
  30761. Can be <tt>{ 0=nearest int | 1=linear | 2=2nd-order RK | 3=4th-order RK. }</tt>.
  30762. \param is_backward_tracking Tells if the streamline is estimated forward or backward.
  30763. \param is_oriented_only Tells if the direction of the vectors must be ignored.
  30764. \param x0 X-coordinate of the first bounding-box vertex.
  30765. \param y0 Y-coordinate of the first bounding-box vertex.
  30766. \param z0 Z-coordinate of the first bounding-box vertex.
  30767. \param x1 X-coordinate of the second bounding-box vertex.
  30768. \param y1 Y-coordinate of the second bounding-box vertex.
  30769. \param z1 Z-coordinate of the second bounding-box vertex.
  30770. **/
  30771. template<typename tfunc>
  30772. static CImg<floatT> streamline(const tfunc& func,
  30773. const float x, const float y, const float z,
  30774. const float L=256, const float dl=0.1f,
  30775. const unsigned int interpolation_type=2, const bool is_backward_tracking=false,
  30776. const bool is_oriented_only=false,
  30777. const float x0=0, const float y0=0, const float z0=0,
  30778. const float x1=0, const float y1=0, const float z1=0) {
  30779. if (dl<=0)
  30780. throw CImgArgumentException("CImg<%s>::streamline(): Invalid specified integration length %g "
  30781. "(should be >0).",
  30782. pixel_type(),
  30783. dl);
  30784. const bool is_bounded = (x0!=x1 || y0!=y1 || z0!=z1);
  30785. if (L<=0 || (is_bounded && (x<x0 || x>x1 || y<y0 || y>y1 || z<z0 || z>z1))) return CImg<floatT>();
  30786. const unsigned int size_L = (unsigned int)cimg::round(L/dl + 1);
  30787. CImg<floatT> coordinates(size_L,3);
  30788. const float dl2 = dl/2;
  30789. float
  30790. *ptr_x = coordinates.data(0,0),
  30791. *ptr_y = coordinates.data(0,1),
  30792. *ptr_z = coordinates.data(0,2),
  30793. pu = (float)(dl*func(x,y,z,0)),
  30794. pv = (float)(dl*func(x,y,z,1)),
  30795. pw = (float)(dl*func(x,y,z,2)),
  30796. X = x, Y = y, Z = z;
  30797. switch (interpolation_type) {
  30798. case 0 : { // Nearest integer interpolation.
  30799. cimg_forX(coordinates,l) {
  30800. *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
  30801. const int
  30802. xi = (int)(X>0?X + 0.5f:X - 0.5f),
  30803. yi = (int)(Y>0?Y + 0.5f:Y - 0.5f),
  30804. zi = (int)(Z>0?Z + 0.5f:Z - 0.5f);
  30805. float
  30806. u = (float)(dl*func((float)xi,(float)yi,(float)zi,0)),
  30807. v = (float)(dl*func((float)xi,(float)yi,(float)zi,1)),
  30808. w = (float)(dl*func((float)xi,(float)yi,(float)zi,2));
  30809. if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
  30810. if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
  30811. if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
  30812. }
  30813. } break;
  30814. case 1 : { // First-order interpolation.
  30815. cimg_forX(coordinates,l) {
  30816. *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
  30817. float
  30818. u = (float)(dl*func(X,Y,Z,0)),
  30819. v = (float)(dl*func(X,Y,Z,1)),
  30820. w = (float)(dl*func(X,Y,Z,2));
  30821. if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
  30822. if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
  30823. if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
  30824. }
  30825. } break;
  30826. case 2 : { // Second order interpolation.
  30827. cimg_forX(coordinates,l) {
  30828. *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
  30829. float
  30830. u0 = (float)(dl2*func(X,Y,Z,0)),
  30831. v0 = (float)(dl2*func(X,Y,Z,1)),
  30832. w0 = (float)(dl2*func(X,Y,Z,2));
  30833. if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; }
  30834. float
  30835. u = (float)(dl*func(X + u0,Y + v0,Z + w0,0)),
  30836. v = (float)(dl*func(X + u0,Y + v0,Z + w0,1)),
  30837. w = (float)(dl*func(X + u0,Y + v0,Z + w0,2));
  30838. if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
  30839. if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
  30840. if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
  30841. }
  30842. } break;
  30843. default : { // Fourth order interpolation.
  30844. cimg_forX(coordinates,x) {
  30845. *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
  30846. float
  30847. u0 = (float)(dl2*func(X,Y,Z,0)),
  30848. v0 = (float)(dl2*func(X,Y,Z,1)),
  30849. w0 = (float)(dl2*func(X,Y,Z,2));
  30850. if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; }
  30851. float
  30852. u1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,0)),
  30853. v1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,1)),
  30854. w1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,2));
  30855. if (is_oriented_only && u1*pu + v1*pv + w1*pw<0) { u1 = -u1; v1 = -v1; w1 = -w1; }
  30856. float
  30857. u2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,0)),
  30858. v2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,1)),
  30859. w2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,2));
  30860. if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u2 = -u2; v2 = -v2; w2 = -w2; }
  30861. float
  30862. u3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,0)),
  30863. v3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,1)),
  30864. w3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,2));
  30865. if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u3 = -u3; v3 = -v3; w3 = -w3; }
  30866. const float
  30867. u = (u0 + u3)/3 + (u1 + u2)/1.5f,
  30868. v = (v0 + v3)/3 + (v1 + v2)/1.5f,
  30869. w = (w0 + w3)/3 + (w1 + w2)/1.5f;
  30870. if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
  30871. if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
  30872. }
  30873. }
  30874. }
  30875. if (ptr_x!=coordinates.data(0,1)) coordinates.resize((int)(ptr_x-coordinates.data()),3,1,1,0);
  30876. return coordinates;
  30877. }
  30878. //! Return stream line of a 3d vector field \overloading.
  30879. static CImg<floatT> streamline(const char *const expression,
  30880. const float x, const float y, const float z,
  30881. const float L=256, const float dl=0.1f,
  30882. const unsigned int interpolation_type=2, const bool is_backward_tracking=true,
  30883. const bool is_oriented_only=false,
  30884. const float x0=0, const float y0=0, const float z0=0,
  30885. const float x1=0, const float y1=0, const float z1=0) {
  30886. _functor4d_streamline_expr func(expression);
  30887. return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,is_oriented_only,x0,y0,z0,x1,y1,z1);
  30888. }
  30889. struct _functor4d_streamline2d_directed {
  30890. const CImg<T>& ref;
  30891. _functor4d_streamline2d_directed(const CImg<T>& pref):ref(pref) {}
  30892. float operator()(const float x, const float y, const float z, const unsigned int c) const {
  30893. return c<2?(float)ref._linear_atXY(x,y,(int)z,c):0;
  30894. }
  30895. };
  30896. struct _functor4d_streamline3d_directed {
  30897. const CImg<T>& ref;
  30898. _functor4d_streamline3d_directed(const CImg<T>& pref):ref(pref) {}
  30899. float operator()(const float x, const float y, const float z, const unsigned int c) const {
  30900. return (float)ref._linear_atXYZ(x,y,z,c);
  30901. }
  30902. };
  30903. struct _functor4d_streamline2d_oriented {
  30904. const CImg<T>& ref;
  30905. CImg<floatT> *pI;
  30906. _functor4d_streamline2d_oriented(const CImg<T>& pref):ref(pref),pI(0) { pI = new CImg<floatT>(2,2,1,2); }
  30907. ~_functor4d_streamline2d_oriented() { delete pI; }
  30908. float operator()(const float x, const float y, const float z, const unsigned int c) const {
  30909. #define _cimg_vecalign2d(i,j) \
  30910. 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); }
  30911. int
  30912. xi = (int)x - (x>=0?0:1), nxi = xi + 1,
  30913. yi = (int)y - (y>=0?0:1), nyi = yi + 1,
  30914. zi = (int)z;
  30915. const float
  30916. dx = x - xi,
  30917. dy = y - yi;
  30918. if (c==0) {
  30919. CImg<floatT>& I = *pI;
  30920. if (xi<0) xi = 0;
  30921. if (nxi<0) nxi = 0;
  30922. if (xi>=ref.width()) xi = ref.width() - 1;
  30923. if (nxi>=ref.width()) nxi = ref.width() - 1;
  30924. if (yi<0) yi = 0;
  30925. if (nyi<0) nyi = 0;
  30926. if (yi>=ref.height()) yi = ref.height() - 1;
  30927. if (nyi>=ref.height()) nyi = ref.height() - 1;
  30928. I(0,0,0) = (float)ref(xi,yi,zi,0); I(0,0,1) = (float)ref(xi,yi,zi,1);
  30929. I(1,0,0) = (float)ref(nxi,yi,zi,0); I(1,0,1) = (float)ref(nxi,yi,zi,1);
  30930. I(1,1,0) = (float)ref(nxi,nyi,zi,0); I(1,1,1) = (float)ref(nxi,nyi,zi,1);
  30931. I(0,1,0) = (float)ref(xi,nyi,zi,0); I(0,1,1) = (float)ref(xi,nyi,zi,1);
  30932. _cimg_vecalign2d(1,0); _cimg_vecalign2d(1,1); _cimg_vecalign2d(0,1);
  30933. }
  30934. return c<2?(float)pI->_linear_atXY(dx,dy,0,c):0;
  30935. }
  30936. };
  30937. struct _functor4d_streamline3d_oriented {
  30938. const CImg<T>& ref;
  30939. CImg<floatT> *pI;
  30940. _functor4d_streamline3d_oriented(const CImg<T>& pref):ref(pref),pI(0) { pI = new CImg<floatT>(2,2,2,3); }
  30941. ~_functor4d_streamline3d_oriented() { delete pI; }
  30942. float operator()(const float x, const float y, const float z, const unsigned int c) const {
  30943. #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) { \
  30944. 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); }
  30945. int
  30946. xi = (int)x - (x>=0?0:1), nxi = xi + 1,
  30947. yi = (int)y - (y>=0?0:1), nyi = yi + 1,
  30948. zi = (int)z - (z>=0?0:1), nzi = zi + 1;
  30949. const float
  30950. dx = x - xi,
  30951. dy = y - yi,
  30952. dz = z - zi;
  30953. if (c==0) {
  30954. CImg<floatT>& I = *pI;
  30955. if (xi<0) xi = 0;
  30956. if (nxi<0) nxi = 0;
  30957. if (xi>=ref.width()) xi = ref.width() - 1;
  30958. if (nxi>=ref.width()) nxi = ref.width() - 1;
  30959. if (yi<0) yi = 0;
  30960. if (nyi<0) nyi = 0;
  30961. if (yi>=ref.height()) yi = ref.height() - 1;
  30962. if (nyi>=ref.height()) nyi = ref.height() - 1;
  30963. if (zi<0) zi = 0;
  30964. if (nzi<0) nzi = 0;
  30965. if (zi>=ref.depth()) zi = ref.depth() - 1;
  30966. if (nzi>=ref.depth()) nzi = ref.depth() - 1;
  30967. I(0,0,0,0) = (float)ref(xi,yi,zi,0); I(0,0,0,1) = (float)ref(xi,yi,zi,1);
  30968. I(0,0,0,2) = (float)ref(xi,yi,zi,2); I(1,0,0,0) = (float)ref(nxi,yi,zi,0);
  30969. I(1,0,0,1) = (float)ref(nxi,yi,zi,1); I(1,0,0,2) = (float)ref(nxi,yi,zi,2);
  30970. I(1,1,0,0) = (float)ref(nxi,nyi,zi,0); I(1,1,0,1) = (float)ref(nxi,nyi,zi,1);
  30971. I(1,1,0,2) = (float)ref(nxi,nyi,zi,2); I(0,1,0,0) = (float)ref(xi,nyi,zi,0);
  30972. I(0,1,0,1) = (float)ref(xi,nyi,zi,1); I(0,1,0,2) = (float)ref(xi,nyi,zi,2);
  30973. I(0,0,1,0) = (float)ref(xi,yi,nzi,0); I(0,0,1,1) = (float)ref(xi,yi,nzi,1);
  30974. I(0,0,1,2) = (float)ref(xi,yi,nzi,2); I(1,0,1,0) = (float)ref(nxi,yi,nzi,0);
  30975. I(1,0,1,1) = (float)ref(nxi,yi,nzi,1); I(1,0,1,2) = (float)ref(nxi,yi,nzi,2);
  30976. I(1,1,1,0) = (float)ref(nxi,nyi,nzi,0); I(1,1,1,1) = (float)ref(nxi,nyi,nzi,1);
  30977. I(1,1,1,2) = (float)ref(nxi,nyi,nzi,2); I(0,1,1,0) = (float)ref(xi,nyi,nzi,0);
  30978. I(0,1,1,1) = (float)ref(xi,nyi,nzi,1); I(0,1,1,2) = (float)ref(xi,nyi,nzi,2);
  30979. _cimg_vecalign3d(1,0,0); _cimg_vecalign3d(1,1,0); _cimg_vecalign3d(0,1,0);
  30980. _cimg_vecalign3d(0,0,1); _cimg_vecalign3d(1,0,1); _cimg_vecalign3d(1,1,1); _cimg_vecalign3d(0,1,1);
  30981. }
  30982. return (float)pI->_linear_atXYZ(dx,dy,dz,c);
  30983. }
  30984. };
  30985. struct _functor4d_streamline_expr {
  30986. _cimg_math_parser *mp;
  30987. ~_functor4d_streamline_expr() { mp->end(); delete mp; }
  30988. _functor4d_streamline_expr(const char *const expr):mp(0) {
  30989. mp = new _cimg_math_parser(expr,"streamline",CImg<T>::const_empty(),0);
  30990. }
  30991. float operator()(const float x, const float y, const float z, const unsigned int c) const {
  30992. return (float)(*mp)(x,y,z,c);
  30993. }
  30994. };
  30995. //! Return a shared-memory image referencing a range of pixels of the image instance.
  30996. /**
  30997. \param x0 X-coordinate of the starting pixel.
  30998. \param x1 X-coordinate of the ending pixel.
  30999. \param y0 Y-coordinate.
  31000. \param z0 Z-coordinate.
  31001. \param c0 C-coordinate.
  31002. **/
  31003. CImg<T> get_shared_points(const unsigned int x0, const unsigned int x1,
  31004. const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) {
  31005. const unsigned int
  31006. beg = (unsigned int)offset(x0,y0,z0,c0),
  31007. end = (unsigned int)offset(x1,y0,z0,c0);
  31008. if (beg>end || beg>=size() || end>=size())
  31009. throw CImgArgumentException(_cimg_instance
  31010. "get_shared_points(): Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).",
  31011. cimg_instance,
  31012. x0,x1,y0,z0,c0);
  31013. return CImg<T>(_data + beg,x1 - x0 + 1,1,1,1,true);
  31014. }
  31015. //! Return a shared-memory image referencing a range of pixels of the image instance \const.
  31016. const CImg<T> get_shared_points(const unsigned int x0, const unsigned int x1,
  31017. const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) const {
  31018. const unsigned int
  31019. beg = (unsigned int)offset(x0,y0,z0,c0),
  31020. end = (unsigned int)offset(x1,y0,z0,c0);
  31021. if (beg>end || beg>=size() || end>=size())
  31022. throw CImgArgumentException(_cimg_instance
  31023. "get_shared_points(): Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).",
  31024. cimg_instance,
  31025. x0,x1,y0,z0,c0);
  31026. return CImg<T>(_data + beg,x1 - x0 + 1,1,1,1,true);
  31027. }
  31028. //! Return a shared-memory image referencing a range of rows of the image instance.
  31029. /**
  31030. \param y0 Y-coordinate of the starting row.
  31031. \param y1 Y-coordinate of the ending row.
  31032. \param z0 Z-coordinate.
  31033. \param c0 C-coordinate.
  31034. **/
  31035. CImg<T> get_shared_rows(const unsigned int y0, const unsigned int y1,
  31036. const unsigned int z0=0, const unsigned int c0=0) {
  31037. const unsigned int
  31038. beg = (unsigned int)offset(0,y0,z0,c0),
  31039. end = (unsigned int)offset(0,y1,z0,c0);
  31040. if (beg>end || beg>=size() || end>=size())
  31041. throw CImgArgumentException(_cimg_instance
  31042. "get_shared_rows(): Invalid request of a shared-memory subset "
  31043. "(0->%u,%u->%u,%u,%u).",
  31044. cimg_instance,
  31045. _width - 1,y0,y1,z0,c0);
  31046. return CImg<T>(_data + beg,_width,y1 - y0 + 1,1,1,true);
  31047. }
  31048. //! Return a shared-memory image referencing a range of rows of the image instance \const.
  31049. const CImg<T> get_shared_rows(const unsigned int y0, const unsigned int y1,
  31050. const unsigned int z0=0, const unsigned int c0=0) const {
  31051. const unsigned int
  31052. beg = (unsigned int)offset(0,y0,z0,c0),
  31053. end = (unsigned int)offset(0,y1,z0,c0);
  31054. if (beg>end || beg>=size() || end>=size())
  31055. throw CImgArgumentException(_cimg_instance
  31056. "get_shared_rows(): Invalid request of a shared-memory subset "
  31057. "(0->%u,%u->%u,%u,%u).",
  31058. cimg_instance,
  31059. _width - 1,y0,y1,z0,c0);
  31060. return CImg<T>(_data + beg,_width,y1 - y0 + 1,1,1,true);
  31061. }
  31062. //! Return a shared-memory image referencing one row of the image instance.
  31063. /**
  31064. \param y0 Y-coordinate.
  31065. \param z0 Z-coordinate.
  31066. \param c0 C-coordinate.
  31067. **/
  31068. CImg<T> get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) {
  31069. return get_shared_rows(y0,y0,z0,c0);
  31070. }
  31071. //! Return a shared-memory image referencing one row of the image instance \const.
  31072. const CImg<T> get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) const {
  31073. return get_shared_rows(y0,y0,z0,c0);
  31074. }
  31075. //! Return a shared memory image referencing a range of slices of the image instance.
  31076. /**
  31077. \param z0 Z-coordinate of the starting slice.
  31078. \param z1 Z-coordinate of the ending slice.
  31079. \param c0 C-coordinate.
  31080. **/
  31081. CImg<T> get_shared_slices(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) {
  31082. const unsigned int
  31083. beg = (unsigned int)offset(0,0,z0,c0),
  31084. end = (unsigned int)offset(0,0,z1,c0);
  31085. if (beg>end || beg>=size() || end>=size())
  31086. throw CImgArgumentException(_cimg_instance
  31087. "get_shared_slices(): Invalid request of a shared-memory subset "
  31088. "(0->%u,0->%u,%u->%u,%u).",
  31089. cimg_instance,
  31090. _width - 1,_height - 1,z0,z1,c0);
  31091. return CImg<T>(_data + beg,_width,_height,z1 - z0 + 1,1,true);
  31092. }
  31093. //! Return a shared memory image referencing a range of slices of the image instance \const.
  31094. const CImg<T> get_shared_slices(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) const {
  31095. const unsigned int
  31096. beg = (unsigned int)offset(0,0,z0,c0),
  31097. end = (unsigned int)offset(0,0,z1,c0);
  31098. if (beg>end || beg>=size() || end>=size())
  31099. throw CImgArgumentException(_cimg_instance
  31100. "get_shared_slices(): Invalid request of a shared-memory subset "
  31101. "(0->%u,0->%u,%u->%u,%u).",
  31102. cimg_instance,
  31103. _width - 1,_height - 1,z0,z1,c0);
  31104. return CImg<T>(_data + beg,_width,_height,z1 - z0 + 1,1,true);
  31105. }
  31106. //! Return a shared-memory image referencing one slice of the image instance.
  31107. /**
  31108. \param z0 Z-coordinate.
  31109. \param c0 C-coordinate.
  31110. **/
  31111. CImg<T> get_shared_slice(const unsigned int z0, const unsigned int c0=0) {
  31112. return get_shared_slices(z0,z0,c0);
  31113. }
  31114. //! Return a shared-memory image referencing one slice of the image instance \const.
  31115. const CImg<T> get_shared_slice(const unsigned int z0, const unsigned int c0=0) const {
  31116. return get_shared_slices(z0,z0,c0);
  31117. }
  31118. //! Return a shared-memory image referencing a range of channels of the image instance.
  31119. /**
  31120. \param c0 C-coordinate of the starting channel.
  31121. \param c1 C-coordinate of the ending channel.
  31122. **/
  31123. CImg<T> get_shared_channels(const unsigned int c0, const unsigned int c1) {
  31124. const unsigned int
  31125. beg = (unsigned int)offset(0,0,0,c0),
  31126. end = (unsigned int)offset(0,0,0,c1);
  31127. if (beg>end || beg>=size() || end>=size())
  31128. throw CImgArgumentException(_cimg_instance
  31129. "get_shared_channels(): Invalid request of a shared-memory subset "
  31130. "(0->%u,0->%u,0->%u,%u->%u).",
  31131. cimg_instance,
  31132. _width - 1,_height - 1,_depth - 1,c0,c1);
  31133. return CImg<T>(_data + beg,_width,_height,_depth,c1 - c0 + 1,true);
  31134. }
  31135. //! Return a shared-memory image referencing a range of channels of the image instance \const.
  31136. const CImg<T> get_shared_channels(const unsigned int c0, const unsigned int c1) const {
  31137. const unsigned int
  31138. beg = (unsigned int)offset(0,0,0,c0),
  31139. end = (unsigned int)offset(0,0,0,c1);
  31140. if (beg>end || beg>=size() || end>=size())
  31141. throw CImgArgumentException(_cimg_instance
  31142. "get_shared_channels(): Invalid request of a shared-memory subset "
  31143. "(0->%u,0->%u,0->%u,%u->%u).",
  31144. cimg_instance,
  31145. _width - 1,_height - 1,_depth - 1,c0,c1);
  31146. return CImg<T>(_data + beg,_width,_height,_depth,c1 - c0 + 1,true);
  31147. }
  31148. //! Return a shared-memory image referencing one channel of the image instance.
  31149. /**
  31150. \param c0 C-coordinate.
  31151. **/
  31152. CImg<T> get_shared_channel(const unsigned int c0) {
  31153. return get_shared_channels(c0,c0);
  31154. }
  31155. //! Return a shared-memory image referencing one channel of the image instance \const.
  31156. const CImg<T> get_shared_channel(const unsigned int c0) const {
  31157. return get_shared_channels(c0,c0);
  31158. }
  31159. //! Return a shared-memory version of the image instance.
  31160. CImg<T> get_shared() {
  31161. return CImg<T>(_data,_width,_height,_depth,_spectrum,true);
  31162. }
  31163. //! Return a shared-memory version of the image instance \const.
  31164. const CImg<T> get_shared() const {
  31165. return CImg<T>(_data,_width,_height,_depth,_spectrum,true);
  31166. }
  31167. //! Split image into a list along specified axis.
  31168. /**
  31169. \param axis Splitting axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
  31170. \param nb Number of splitted parts.
  31171. \note
  31172. - If \c nb==0, instance image is splitted into blocs of egal values along the specified axis.
  31173. - If \c nb<=0, instance image is splitted into blocs of -\c nb pixel wide.
  31174. - If \c nb>0, instance image is splitted into \c nb blocs.
  31175. **/
  31176. CImgList<T> get_split(const char axis, const int nb=-1) const {
  31177. CImgList<T> res;
  31178. if (is_empty()) return res;
  31179. const char _axis = cimg::lowercase(axis);
  31180. if (nb<0) { // Split by bloc size.
  31181. const unsigned int dp = (unsigned int)(nb?-nb:1);
  31182. switch (_axis) {
  31183. case 'x': {
  31184. if (_width>dp) {
  31185. res.assign(_width/dp + (_width%dp?1:0),1,1);
  31186. const unsigned int pe = _width - dp;
  31187. cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=128 && _height*_depth*_spectrum>=128))
  31188. for (unsigned int p = 0; p<pe; p+=dp)
  31189. get_crop(p,0,0,0,p + dp - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res[p/dp]);
  31190. get_crop((res._width - 1)*dp,0,0,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back());
  31191. } else res.assign(*this);
  31192. } break;
  31193. case 'y': {
  31194. if (_height>dp) {
  31195. res.assign(_height/dp + (_height%dp?1:0),1,1);
  31196. const unsigned int pe = _height - dp;
  31197. cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=128 && _width*_depth*_spectrum>=128))
  31198. for (unsigned int p = 0; p<pe; p+=dp)
  31199. get_crop(0,p,0,0,_width - 1,p + dp - 1,_depth - 1,_spectrum - 1).move_to(res[p/dp]);
  31200. get_crop(0,(res._width - 1)*dp,0,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back());
  31201. } else res.assign(*this);
  31202. } break;
  31203. case 'z': {
  31204. if (_depth>dp) {
  31205. res.assign(_depth/dp + (_depth%dp?1:0),1,1);
  31206. const unsigned int pe = _depth - dp;
  31207. cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=128 && _width*_height*_spectrum>=128))
  31208. for (unsigned int p = 0; p<pe; p+=dp)
  31209. get_crop(0,0,p,0,_width - 1,_height - 1,p + dp - 1,_spectrum - 1).move_to(res[p/dp]);
  31210. get_crop(0,0,(res._width - 1)*dp,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back());
  31211. } else res.assign(*this);
  31212. } break;
  31213. case 'c' : {
  31214. if (_spectrum>dp) {
  31215. res.assign(_spectrum/dp + (_spectrum%dp?1:0),1,1);
  31216. const unsigned int pe = _spectrum - dp;
  31217. cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=128 && _width*_height*_depth>=128))
  31218. for (unsigned int p = 0; p<pe; p+=dp)
  31219. get_crop(0,0,0,p,_width - 1,_height - 1,_depth - 1,p + dp - 1).move_to(res[p/dp]);
  31220. get_crop(0,0,0,(res._width - 1)*dp,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back());
  31221. } else res.assign(*this);
  31222. }
  31223. }
  31224. } else if (nb>0) { // Split by number of (non-homogeneous) blocs.
  31225. const unsigned int siz = _axis=='x'?_width:_axis=='y'?_height:_axis=='z'?_depth:_axis=='c'?_spectrum:0;
  31226. if ((unsigned int)nb>siz)
  31227. throw CImgArgumentException(_cimg_instance
  31228. "get_split(): Instance cannot be split along %c-axis into %u blocs.",
  31229. cimg_instance,
  31230. axis,nb);
  31231. if (nb==1) res.assign(*this);
  31232. else {
  31233. int err = (int)siz;
  31234. unsigned int _p = 0;
  31235. switch (_axis) {
  31236. case 'x' : {
  31237. cimg_forX(*this,p) if ((err-=nb)<=0) {
  31238. get_crop(_p,0,0,0,p,_height - 1,_depth - 1,_spectrum - 1).move_to(res);
  31239. err+=(int)siz;
  31240. _p = p + 1U;
  31241. }
  31242. } break;
  31243. case 'y' : {
  31244. cimg_forY(*this,p) if ((err-=nb)<=0) {
  31245. get_crop(0,_p,0,0,_width - 1,p,_depth - 1,_spectrum - 1).move_to(res);
  31246. err+=(int)siz;
  31247. _p = p + 1U;
  31248. }
  31249. } break;
  31250. case 'z' : {
  31251. cimg_forZ(*this,p) if ((err-=nb)<=0) {
  31252. get_crop(0,0,_p,0,_width - 1,_height - 1,p,_spectrum - 1).move_to(res);
  31253. err+=(int)siz;
  31254. _p = p + 1U;
  31255. }
  31256. } break;
  31257. case 'c' : {
  31258. cimg_forC(*this,p) if ((err-=nb)<=0) {
  31259. get_crop(0,0,0,_p,_width - 1,_height - 1,_depth - 1,p).move_to(res);
  31260. err+=(int)siz;
  31261. _p = p + 1U;
  31262. }
  31263. }
  31264. }
  31265. }
  31266. } else { // Split by egal values according to specified axis.
  31267. T current = *_data;
  31268. switch (_axis) {
  31269. case 'x' : {
  31270. int i0 = 0;
  31271. cimg_forX(*this,i)
  31272. if ((*this)(i)!=current) { get_columns(i0,i - 1).move_to(res); i0 = i; current = (*this)(i); }
  31273. get_columns(i0,width() - 1).move_to(res);
  31274. } break;
  31275. case 'y' : {
  31276. int i0 = 0;
  31277. cimg_forY(*this,i)
  31278. if ((*this)(0,i)!=current) { get_rows(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,i); }
  31279. get_rows(i0,height() - 1).move_to(res);
  31280. } break;
  31281. case 'z' : {
  31282. int i0 = 0;
  31283. cimg_forZ(*this,i)
  31284. if ((*this)(0,0,i)!=current) { get_slices(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,0,i); }
  31285. get_slices(i0,depth() - 1).move_to(res);
  31286. } break;
  31287. case 'c' : {
  31288. int i0 = 0;
  31289. cimg_forC(*this,i)
  31290. if ((*this)(0,0,0,i)!=current) { get_channels(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,0,0,i); }
  31291. get_channels(i0,spectrum() - 1).move_to(res);
  31292. } break;
  31293. default : {
  31294. longT i0 = 0;
  31295. cimg_foroff(*this,i)
  31296. if ((*this)[i]!=current) {
  31297. CImg<T>(_data + i0,1,(unsigned int)(i - i0)).move_to(res);
  31298. i0 = (longT)i; current = (*this)[i];
  31299. }
  31300. CImg<T>(_data + i0,1,(unsigned int)(size() - i0)).move_to(res);
  31301. }
  31302. }
  31303. }
  31304. return res;
  31305. }
  31306. //! Split image into a list of sub-images, according to a specified splitting value sequence and optionally axis.
  31307. /**
  31308. \param values Splitting value sequence.
  31309. \param axis Axis along which the splitting is performed. Can be '0' to ignore axis.
  31310. \param keep_values Tells if the splitting sequence must be kept in the splitted blocs.
  31311. **/
  31312. template<typename t>
  31313. CImgList<T> get_split(const CImg<t>& values, const char axis=0, const bool keep_values=true) const {
  31314. CImgList<T> res;
  31315. if (is_empty()) return res;
  31316. const ulongT vsiz = values.size();
  31317. const char _axis = cimg::lowercase(axis);
  31318. if (!vsiz) return CImgList<T>(*this);
  31319. if (vsiz==1) { // Split according to a single value.
  31320. const T value = (T)*values;
  31321. switch (_axis) {
  31322. case 'x' : {
  31323. unsigned int i0 = 0, i = 0;
  31324. do {
  31325. while (i<_width && (*this)(i)==value) ++i;
  31326. if (i>i0) { if (keep_values) get_columns(i0,i - 1).move_to(res); i0 = i; }
  31327. while (i<_width && (*this)(i)!=value) ++i;
  31328. if (i>i0) { get_columns(i0,i - 1).move_to(res); i0 = i; }
  31329. } while (i<_width);
  31330. } break;
  31331. case 'y' : {
  31332. unsigned int i0 = 0, i = 0;
  31333. do {
  31334. while (i<_height && (*this)(0,i)==value) ++i;
  31335. if (i>i0) { if (keep_values) get_rows(i0,i - 1).move_to(res); i0 = i; }
  31336. while (i<_height && (*this)(0,i)!=value) ++i;
  31337. if (i>i0) { get_rows(i0,i - 1).move_to(res); i0 = i; }
  31338. } while (i<_height);
  31339. } break;
  31340. case 'z' : {
  31341. unsigned int i0 = 0, i = 0;
  31342. do {
  31343. while (i<_depth && (*this)(0,0,i)==value) ++i;
  31344. if (i>i0) { if (keep_values) get_slices(i0,i - 1).move_to(res); i0 = i; }
  31345. while (i<_depth && (*this)(0,0,i)!=value) ++i;
  31346. if (i>i0) { get_slices(i0,i - 1).move_to(res); i0 = i; }
  31347. } while (i<_depth);
  31348. } break;
  31349. case 'c' : {
  31350. unsigned int i0 = 0, i = 0;
  31351. do {
  31352. while (i<_spectrum && (*this)(0,0,0,i)==value) ++i;
  31353. if (i>i0) { if (keep_values) get_channels(i0,i - 1).move_to(res); i0 = i; }
  31354. while (i<_spectrum && (*this)(0,0,0,i)!=value) ++i;
  31355. if (i>i0) { get_channels(i0,i - 1).move_to(res); i0 = i; }
  31356. } while (i<_spectrum);
  31357. } break;
  31358. default : {
  31359. const ulongT siz = size();
  31360. ulongT i0 = 0, i = 0;
  31361. do {
  31362. while (i<siz && (*this)[i]==value) ++i;
  31363. if (i>i0) { if (keep_values) CImg<T>(_data + i0,1,(unsigned int)(i - i0)).move_to(res); i0 = i; }
  31364. while (i<siz && (*this)[i]!=value) ++i;
  31365. if (i>i0) { CImg<T>(_data + i0,1,(unsigned int)(i - i0)).move_to(res); i0 = i; }
  31366. } while (i<siz);
  31367. }
  31368. }
  31369. } else { // Split according to multiple values.
  31370. ulongT j = 0;
  31371. switch (_axis) {
  31372. case 'x' : {
  31373. unsigned int i0 = 0, i1 = 0, i = 0;
  31374. do {
  31375. if ((*this)(i)==*values) {
  31376. i1 = i; j = 0;
  31377. while (i<_width && (*this)(i)==values[j]) { ++i; if (++j>=vsiz) j = 0; }
  31378. i-=j;
  31379. if (i>i1) {
  31380. if (i1>i0) get_columns(i0,i1 - 1).move_to(res);
  31381. if (keep_values) get_columns(i1,i - 1).move_to(res);
  31382. i0 = i;
  31383. } else ++i;
  31384. } else ++i;
  31385. } while (i<_width);
  31386. if (i0<_width) get_columns(i0,width() - 1).move_to(res);
  31387. } break;
  31388. case 'y' : {
  31389. unsigned int i0 = 0, i1 = 0, i = 0;
  31390. do {
  31391. if ((*this)(0,i)==*values) {
  31392. i1 = i; j = 0;
  31393. while (i<_height && (*this)(0,i)==values[j]) { ++i; if (++j>=vsiz) j = 0; }
  31394. i-=j;
  31395. if (i>i1) {
  31396. if (i1>i0) get_rows(i0,i1 - 1).move_to(res);
  31397. if (keep_values) get_rows(i1,i - 1).move_to(res);
  31398. i0 = i;
  31399. } else ++i;
  31400. } else ++i;
  31401. } while (i<_height);
  31402. if (i0<_height) get_rows(i0,height() - 1).move_to(res);
  31403. } break;
  31404. case 'z' : {
  31405. unsigned int i0 = 0, i1 = 0, i = 0;
  31406. do {
  31407. if ((*this)(0,0,i)==*values) {
  31408. i1 = i; j = 0;
  31409. while (i<_depth && (*this)(0,0,i)==values[j]) { ++i; if (++j>=vsiz) j = 0; }
  31410. i-=j;
  31411. if (i>i1) {
  31412. if (i1>i0) get_slices(i0,i1 - 1).move_to(res);
  31413. if (keep_values) get_slices(i1,i - 1).move_to(res);
  31414. i0 = i;
  31415. } else ++i;
  31416. } else ++i;
  31417. } while (i<_depth);
  31418. if (i0<_depth) get_slices(i0,depth() - 1).move_to(res);
  31419. } break;
  31420. case 'c' : {
  31421. unsigned int i0 = 0, i1 = 0, i = 0;
  31422. do {
  31423. if ((*this)(0,0,0,i)==*values) {
  31424. i1 = i; j = 0;
  31425. while (i<_spectrum && (*this)(0,0,0,i)==values[j]) { ++i; if (++j>=vsiz) j = 0; }
  31426. i-=j;
  31427. if (i>i1) {
  31428. if (i1>i0) get_channels(i0,i1 - 1).move_to(res);
  31429. if (keep_values) get_channels(i1,i - 1).move_to(res);
  31430. i0 = i;
  31431. } else ++i;
  31432. } else ++i;
  31433. } while (i<_spectrum);
  31434. if (i0<_spectrum) get_channels(i0,spectrum() - 1).move_to(res);
  31435. } break;
  31436. default : {
  31437. ulongT i0 = 0, i1 = 0, i = 0;
  31438. const ulongT siz = size();
  31439. do {
  31440. if ((*this)[i]==*values) {
  31441. i1 = i; j = 0;
  31442. while (i<siz && (*this)[i]==values[j]) { ++i; if (++j>=vsiz) j = 0; }
  31443. i-=j;
  31444. if (i>i1) {
  31445. if (i1>i0) CImg<T>(_data + i0,1,(unsigned int)(i1 - i0)).move_to(res);
  31446. if (keep_values) CImg<T>(_data + i1,1,(unsigned int)(i - i1)).move_to(res);
  31447. i0 = i;
  31448. } else ++i;
  31449. } else ++i;
  31450. } while (i<siz);
  31451. if (i0<siz) CImg<T>(_data + i0,1,(unsigned int)(siz - i0)).move_to(res);
  31452. } break;
  31453. }
  31454. }
  31455. return res;
  31456. }
  31457. //! Append two images along specified axis.
  31458. /**
  31459. \param img Image to append with instance image.
  31460. \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
  31461. \param align Append alignment in \c [0,1].
  31462. **/
  31463. template<typename t>
  31464. CImg<T>& append(const CImg<t>& img, const char axis='x', const float align=0) {
  31465. if (is_empty()) return assign(img,false);
  31466. if (!img) return *this;
  31467. return CImgList<T>(*this,true).insert(img).get_append(axis,align).move_to(*this);
  31468. }
  31469. //! Append two images along specified axis \specialization.
  31470. CImg<T>& append(const CImg<T>& img, const char axis='x', const float align=0) {
  31471. if (is_empty()) return assign(img,false);
  31472. if (!img) return *this;
  31473. return CImgList<T>(*this,img,true).get_append(axis,align).move_to(*this);
  31474. }
  31475. //! Append two images along specified axis \const.
  31476. template<typename t>
  31477. CImg<_cimg_Tt> get_append(const CImg<T>& img, const char axis='x', const float align=0) const {
  31478. if (is_empty()) return +img;
  31479. if (!img) return +*this;
  31480. return CImgList<_cimg_Tt>(*this,true).insert(img).get_append(axis,align);
  31481. }
  31482. //! Append two images along specified axis \specialization.
  31483. CImg<T> get_append(const CImg<T>& img, const char axis='x', const float align=0) const {
  31484. if (is_empty()) return +img;
  31485. if (!img) return +*this;
  31486. return CImgList<T>(*this,img,true).get_append(axis,align);
  31487. }
  31488. //@}
  31489. //---------------------------------------
  31490. //
  31491. //! \name Filtering / Transforms
  31492. //@{
  31493. //---------------------------------------
  31494. //! Correlate image by a kernel.
  31495. /**
  31496. \param kernel = the correlation kernel.
  31497. \param boundary_conditions boundary conditions can be (false=dirichlet, true=neumann)
  31498. \param is_normalized = enable local normalization.
  31499. \note
  31500. - The correlation of the image instance \p *this by the kernel \p kernel is defined to be:
  31501. res(x,y,z) = sum_{i,j,k} (*this)(x + i,y + j,z + k)*kernel(i,j,k).
  31502. **/
  31503. template<typename t>
  31504. CImg<T>& correlate(const CImg<t>& kernel, const bool boundary_conditions=true,
  31505. const bool is_normalized=false) {
  31506. if (is_empty() || !kernel) return *this;
  31507. return get_correlate(kernel,boundary_conditions,is_normalized).move_to(*this);
  31508. }
  31509. template<typename t>
  31510. CImg<_cimg_Ttfloat> get_correlate(const CImg<t>& kernel, const bool boundary_conditions=true,
  31511. const bool is_normalized=false) const {
  31512. return _correlate(kernel,boundary_conditions,is_normalized,false);
  31513. }
  31514. //! Correlate image by a kernel \newinstance.
  31515. template<typename t>
  31516. CImg<_cimg_Ttfloat> _correlate(const CImg<t>& kernel, const bool boundary_conditions,
  31517. const bool is_normalized, const bool is_convolution) const {
  31518. if (is_empty() || !kernel) return *this;
  31519. typedef _cimg_Ttfloat Ttfloat;
  31520. CImg<Ttfloat> res;
  31521. const ulongT
  31522. res_whd = (ulongT)_width*_height*_depth,
  31523. res_size = res_whd*std::max(_spectrum,kernel._spectrum);
  31524. const bool
  31525. is_inner_parallel = _width*_height*_depth>=32768,
  31526. is_outer_parallel = res_size>=32768;
  31527. _cimg_abort_init_omp;
  31528. cimg_abort_init;
  31529. if (kernel._width==kernel._height &&
  31530. ((kernel._depth==1 && kernel._width<=6) || (kernel._depth==kernel._width && kernel._width<=3))) {
  31531. // Special optimization done for 2x2, 3x3, 4x4, 5x5, 6x6, 2x2x2 and 3x3x3 kernel.
  31532. if (!boundary_conditions && res_whd<=3000*3000) { // Dirichlet boundaries
  31533. // For relatively small images, adding a zero border then use optimized NxN convolution loops is faster.
  31534. res = (kernel._depth==1?get_crop(-1,-1,_width,_height):get_crop(-1,-1,-1,_width,_height,_depth)).
  31535. _correlate(kernel,true,is_normalized,is_convolution);
  31536. if (kernel._depth==1) res.crop(1,1,res._width - 2,res._height - 2);
  31537. else res.crop(1,1,1,res._width - 2,res._height - 2,res._depth - 2);
  31538. } else { // Neumann boundaries
  31539. res.assign(_width,_height,_depth,std::max(_spectrum,kernel._spectrum));
  31540. cimg::unused(is_inner_parallel,is_outer_parallel);
  31541. CImg<t> _kernel;
  31542. if (is_convolution) { // Add empty column/row/slice to shift kernel center in case of convolution
  31543. const int dw = !(kernel.width()%2), dh = !(kernel.height()%2), dd = !(kernel.depth()%2);
  31544. if (dw || dh || dd)
  31545. kernel.get_resize(kernel.width() + dw,kernel.height() + dh,kernel.depth() + dd,-100,0,0).
  31546. move_to(_kernel);
  31547. }
  31548. if (!_kernel) _kernel = kernel.get_shared();
  31549. switch (_kernel._depth) {
  31550. case 3 : {
  31551. cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
  31552. cimg_forC(res,c) {
  31553. cimg_abort_test;
  31554. const CImg<T> img = get_shared_channel(c%_spectrum);
  31555. const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum);
  31556. CImg<T> I(27);
  31557. Ttfloat *ptrd = res.data(0,0,0,c);
  31558. if (is_normalized) {
  31559. const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M;
  31560. cimg_for3x3x3(img,x,y,z,0,I,T) {
  31561. const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] +
  31562. I[ 3]*I[ 3] + I[ 4]*I[ 4] + I[ 5]*I[ 5] +
  31563. I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] +
  31564. I[ 9]*I[ 9] + I[10]*I[10] + I[11]*I[11] +
  31565. I[12]*I[12] + I[13]*I[13] + I[14]*I[14] +
  31566. I[15]*I[15] + I[16]*I[16] + I[17]*I[17] +
  31567. I[18]*I[18] + I[19]*I[19] + I[20]*I[20] +
  31568. I[21]*I[21] + I[22]*I[22] + I[23]*I[23] +
  31569. I[24]*I[24] + I[25]*I[25] + I[26]*I[26]);
  31570. *(ptrd++) = (Ttfloat)(N?(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] +
  31571. I[ 3]*K[ 3] + I[ 4]*K[ 4] + I[ 5]*K[ 5] +
  31572. I[ 6]*K[ 6] + I[ 7]*K[ 7] + I[ 8]*K[ 8] +
  31573. I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] +
  31574. I[12]*K[12] + I[13]*K[13] + I[14]*K[14] +
  31575. I[15]*K[15] + I[16]*K[16] + I[17]*K[17] +
  31576. I[18]*K[18] + I[19]*K[19] + I[20]*K[20] +
  31577. I[21]*K[21] + I[22]*K[22] + I[23]*K[23] +
  31578. I[24]*K[24] + I[25]*K[25] + I[26]*K[26])/std::sqrt(N):0);
  31579. }
  31580. } else cimg_for3x3x3(img,x,y,z,0,I,T)
  31581. *(ptrd++) = (Ttfloat)(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] +
  31582. I[ 3]*K[ 3] + I[ 4]*K[ 4] + I[ 5]*K[ 5] +
  31583. I[ 6]*K[ 6] + I[ 7]*K[ 7] + I[ 8]*K[ 8] +
  31584. I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] +
  31585. I[12]*K[12] + I[13]*K[13] + I[14]*K[14] +
  31586. I[15]*K[15] + I[16]*K[16] + I[17]*K[17] +
  31587. I[18]*K[18] + I[19]*K[19] + I[20]*K[20] +
  31588. I[21]*K[21] + I[22]*K[22] + I[23]*K[23] +
  31589. I[24]*K[24] + I[25]*K[25] + I[26]*K[26]);
  31590. }
  31591. } break;
  31592. case 2 : {
  31593. cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
  31594. cimg_forC(res,c) {
  31595. cimg_abort_test;
  31596. const CImg<T> img = get_shared_channel(c%_spectrum);
  31597. const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum);
  31598. CImg<T> I(8);
  31599. Ttfloat *ptrd = res.data(0,0,0,c);
  31600. if (is_normalized) {
  31601. const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M;
  31602. cimg_for2x2x2(img,x,y,z,0,I,T) {
  31603. const Ttfloat N = M*(I[0]*I[0] + I[1]*I[1] +
  31604. I[2]*I[2] + I[3]*I[3] +
  31605. I[4]*I[4] + I[5]*I[5] +
  31606. I[6]*I[6] + I[7]*I[7]);
  31607. *(ptrd++) = (Ttfloat)(N?(I[0]*K[0] + I[1]*K[1] +
  31608. I[2]*K[2] + I[3]*K[3] +
  31609. I[4]*K[4] + I[5]*K[5] +
  31610. I[6]*K[6] + I[7]*K[7])/std::sqrt(N):0);
  31611. }
  31612. } else cimg_for2x2x2(img,x,y,z,0,I,T)
  31613. *(ptrd++) = (Ttfloat)(I[0]*K[0] + I[1]*K[1] +
  31614. I[2]*K[2] + I[3]*K[3] +
  31615. I[4]*K[4] + I[5]*K[5] +
  31616. I[6]*K[6] + I[7]*K[7]);
  31617. }
  31618. } break;
  31619. default :
  31620. case 1 :
  31621. switch (_kernel._width) {
  31622. case 6 : {
  31623. cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
  31624. cimg_forC(res,c) {
  31625. cimg_abort_test;
  31626. const CImg<T> img = get_shared_channel(c%_spectrum);
  31627. const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum);
  31628. CImg<T> I(36);
  31629. Ttfloat *ptrd = res.data(0,0,0,c);
  31630. if (is_normalized) {
  31631. const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M;
  31632. cimg_forZ(img,z) cimg_for6x6(img,x,y,z,0,I,T) {
  31633. const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] + I[ 4]*I[ 4] +
  31634. I[ 5]*I[ 5] + I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] + I[ 9]*I[ 9] +
  31635. I[10]*I[10] + I[11]*I[11] + I[12]*I[12] + I[13]*I[13] + I[14]*I[14] +
  31636. I[15]*I[15] + I[16]*I[16] + I[17]*I[17] + I[18]*I[18] + I[19]*I[19] +
  31637. I[20]*I[20] + I[21]*I[21] + I[22]*I[22] + I[23]*I[23] + I[24]*I[24] +
  31638. I[25]*I[25] + I[26]*I[26] + I[27]*I[27] + I[28]*I[28] + I[29]*I[29] +
  31639. I[30]*I[30] + I[31]*I[31] + I[32]*I[32] + I[33]*I[33] + I[34]*I[34] +
  31640. I[35]*I[35]);
  31641. *(ptrd++) = (Ttfloat)(N?(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] +
  31642. I[ 4]*K[ 4] + I[ 5]*K[ 5] + I[ 6]*K[ 6] + I[ 7]*K[ 7] +
  31643. I[ 8]*K[ 8] + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] +
  31644. I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + I[15]*K[15] +
  31645. I[16]*K[16] + I[17]*K[17] + I[18]*K[18] + I[19]*K[19] +
  31646. I[20]*K[20] + I[21]*K[21] + I[22]*K[22] + I[23]*K[23] +
  31647. I[24]*K[24] + I[25]*K[25] + I[26]*K[26] + I[27]*K[27] +
  31648. I[28]*K[28] + I[29]*K[29] + I[30]*K[30] + I[31]*K[31] +
  31649. I[32]*K[32] + I[33]*K[33] + I[34]*K[34] + I[35]*K[35])/
  31650. std::sqrt(N):0);
  31651. }
  31652. } else cimg_forZ(img,z) cimg_for6x6(img,x,y,z,0,I,T)
  31653. *(ptrd++) = (Ttfloat)(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] +
  31654. I[ 4]*K[ 4] + I[ 5]*K[ 5] + I[ 6]*K[ 6] + I[ 7]*K[ 7] +
  31655. I[ 8]*K[ 8] + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] +
  31656. I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + I[15]*K[15] +
  31657. I[16]*K[16] + I[17]*K[17] + I[18]*K[18] + I[19]*K[19] +
  31658. I[20]*K[20] + I[21]*K[21] + I[22]*K[22] + I[23]*K[23] +
  31659. I[24]*K[24] + I[25]*K[25] + I[26]*K[26] + I[27]*K[27] +
  31660. I[28]*K[28] + I[29]*K[29] + I[30]*K[30] + I[31]*K[31] +
  31661. I[32]*K[32] + I[33]*K[33] + I[34]*K[34] + I[35]*K[35]);
  31662. }
  31663. } break;
  31664. case 5 : {
  31665. cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
  31666. cimg_forC(res,c) {
  31667. cimg_abort_test;
  31668. const CImg<T> img = get_shared_channel(c%_spectrum);
  31669. const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum);
  31670. CImg<T> I(25);
  31671. Ttfloat *ptrd = res.data(0,0,0,c);
  31672. if (is_normalized) {
  31673. const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M;
  31674. cimg_forZ(img,z) cimg_for5x5(img,x,y,z,0,I,T) {
  31675. const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] + I[ 4]*I[ 4] +
  31676. I[ 5]*I[ 5] + I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] + I[ 9]*I[ 9] +
  31677. I[10]*I[10] + I[11]*I[11] + I[12]*I[12] + I[13]*I[13] + I[14]*I[14] +
  31678. I[15]*I[15] + I[16]*I[16] + I[17]*I[17] + I[18]*I[18] + I[19]*I[19] +
  31679. I[20]*I[20] + I[21]*I[21] + I[22]*I[22] + I[23]*I[23] + I[24]*I[24]);
  31680. *(ptrd++) = (Ttfloat)(N?(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] +
  31681. I[ 4]*K[ 4] + I[ 5]*K[ 5] + I[ 6]*K[ 6] + I[ 7]*K[ 7] +
  31682. I[ 8]*K[ 8] + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] +
  31683. I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + I[15]*K[15] +
  31684. I[16]*K[16] + I[17]*K[17] + I[18]*K[18] + I[19]*K[19] +
  31685. I[20]*K[20] + I[21]*K[21] + I[22]*K[22] + I[23]*K[23] +
  31686. I[24]*K[24])/std::sqrt(N):0);
  31687. }
  31688. } else cimg_forZ(img,z) cimg_for5x5(img,x,y,z,0,I,T)
  31689. *(ptrd++) = (Ttfloat)(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] +
  31690. I[ 4]*K[ 4] + I[ 5]*K[ 5] + I[ 6]*K[ 6] + I[ 7]*K[ 7] +
  31691. I[ 8]*K[ 8] + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] +
  31692. I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + I[15]*K[15] +
  31693. I[16]*K[16] + I[17]*K[17] + I[18]*K[18] + I[19]*K[19] +
  31694. I[20]*K[20] + I[21]*K[21] + I[22]*K[22] + I[23]*K[23] +
  31695. I[24]*K[24]);
  31696. }
  31697. } break;
  31698. case 4 : {
  31699. cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
  31700. cimg_forC(res,c) {
  31701. cimg_abort_test;
  31702. const CImg<T> img = get_shared_channel(c%_spectrum);
  31703. const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum);
  31704. CImg<T> I(16);
  31705. Ttfloat *ptrd = res.data(0,0,0,c);
  31706. if (is_normalized) {
  31707. const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M;
  31708. cimg_forZ(img,z) cimg_for4x4(img,x,y,z,0,I,T) {
  31709. const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] +
  31710. I[ 4]*I[ 4] + I[ 5]*I[ 5] + I[ 6]*I[ 6] + I[ 7]*I[ 7] +
  31711. I[ 8]*I[ 8] + I[ 9]*I[ 9] + I[10]*I[10] + I[11]*I[11] +
  31712. I[12]*I[12] + I[13]*I[13] + I[14]*I[14] + I[15]*I[15]);
  31713. *(ptrd++) = (Ttfloat)(N?(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] +
  31714. I[ 4]*K[ 4] + I[ 5]*K[ 5] + I[ 6]*K[ 6] + I[ 7]*K[ 7] +
  31715. I[ 8]*K[ 8] + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] +
  31716. I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + I[15]*K[15])/
  31717. std::sqrt(N):0);
  31718. }
  31719. } else cimg_forZ(img,z) cimg_for4x4(img,x,y,z,0,I,T)
  31720. *(ptrd++) = (Ttfloat)(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] +
  31721. I[ 4]*K[ 4] + I[ 5]*K[ 5] + I[ 6]*K[ 6] + I[ 7]*K[ 7] +
  31722. I[ 8]*K[ 8] + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] +
  31723. I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + I[15]*K[15]);
  31724. }
  31725. } break;
  31726. case 3 : {
  31727. cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
  31728. cimg_forC(res,c) {
  31729. cimg_abort_test;
  31730. const CImg<T> img = get_shared_channel(c%_spectrum);
  31731. const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum);
  31732. CImg<T> I(9);
  31733. Ttfloat *ptrd = res.data(0,0,0,c);
  31734. if (is_normalized) {
  31735. const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M;
  31736. cimg_forZ(img,z) cimg_for3x3(img,x,y,z,0,I,T) {
  31737. const Ttfloat N = M*(I[0]*I[0] + I[1]*I[1] + I[2]*I[2] +
  31738. I[3]*I[3] + I[4]*I[4] + I[5]*I[5] +
  31739. I[6]*I[6] + I[7]*I[7] + I[8]*I[8]);
  31740. *(ptrd++) = (Ttfloat)(N?(I[0]*K[0] + I[1]*K[1] + I[2]*K[2] +
  31741. I[3]*K[3] + I[4]*K[4] + I[5]*K[5] +
  31742. I[6]*K[6] + I[7]*K[7] + I[8]*K[8])/std::sqrt(N):0);
  31743. }
  31744. } else cimg_forZ(img,z) cimg_for3x3(img,x,y,z,0,I,T)
  31745. *(ptrd++) = (Ttfloat)(I[0]*K[0] + I[1]*K[1] + I[2]*K[2] +
  31746. I[3]*K[3] + I[4]*K[4] + I[5]*K[5] +
  31747. I[6]*K[6] + I[7]*K[7] + I[8]*K[8]);
  31748. }
  31749. } break;
  31750. case 2 : {
  31751. cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
  31752. cimg_forC(res,c) {
  31753. cimg_abort_test;
  31754. const CImg<T> img = get_shared_channel(c%_spectrum);
  31755. const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum);
  31756. CImg<T> I(4);
  31757. Ttfloat *ptrd = res.data(0,0,0,c);
  31758. if (is_normalized) {
  31759. const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M;
  31760. cimg_forZ(img,z) cimg_for2x2(img,x,y,z,0,I,T) {
  31761. const Ttfloat N = M*(I[0]*I[0] + I[1]*I[1] +
  31762. I[2]*I[2] + I[3]*I[3]);
  31763. *(ptrd++) = (Ttfloat)(N?(I[0]*K[0] + I[1]*K[1] +
  31764. I[2]*K[2] + I[3]*K[3])/std::sqrt(N):0);
  31765. }
  31766. } else cimg_forZ(img,z) cimg_for2x2(img,x,y,z,0,I,T)
  31767. *(ptrd++) = (Ttfloat)(I[0]*K[0] + I[1]*K[1] +
  31768. I[2]*K[2] + I[3]*K[3]);
  31769. }
  31770. } break;
  31771. case 1 :
  31772. if (is_normalized) res.fill(1);
  31773. else cimg_forC(res,c) {
  31774. cimg_abort_test;
  31775. const CImg<T> img = get_shared_channel(c%_spectrum);
  31776. const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum);
  31777. res.get_shared_channel(c).assign(img)*=K[0];
  31778. }
  31779. break;
  31780. }
  31781. }
  31782. }
  31783. }
  31784. if (!res) { // Generic version for other kernels and boundary conditions.
  31785. res.assign(_width,_height,_depth,std::max(_spectrum,kernel._spectrum));
  31786. int
  31787. mx2 = kernel.width()/2, my2 = kernel.height()/2, mz2 = kernel.depth()/2,
  31788. mx1 = kernel.width() - mx2 - 1, my1 = kernel.height() - my2 - 1, mz1 = kernel.depth() - mz2 - 1;
  31789. if (is_convolution) cimg::swap(mx1,mx2,my1,my2,mz1,mz2); // Shift kernel center in case of convolution
  31790. const int
  31791. mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2;
  31792. cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel))
  31793. cimg_forC(res,c) _cimg_abort_try_omp {
  31794. cimg_abort_test;
  31795. const CImg<T> img = get_shared_channel(c%_spectrum);
  31796. const CImg<t> K = kernel.get_shared_channel(c%kernel._spectrum);
  31797. if (is_normalized) { // Normalized correlation.
  31798. const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M;
  31799. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(is_inner_parallel))
  31800. for (int z = mz1; z<mze; ++z)
  31801. for (int y = my1; y<mye; ++y)
  31802. for (int x = mx1; x<mxe; ++x) _cimg_abort_try_omp2 {
  31803. cimg_abort_test2;
  31804. Ttfloat val = 0, N = 0;
  31805. for (int zm = -mz1; zm<=mz2; ++zm)
  31806. for (int ym = -my1; ym<=my2; ++ym)
  31807. for (int xm = -mx1; xm<=mx2; ++xm) {
  31808. const Ttfloat _val = (Ttfloat)img(x + xm,y + ym,z + zm);
  31809. val+=_val*K(mx1 + xm,my1 + ym,mz1 + zm);
  31810. N+=_val*_val;
  31811. }
  31812. N*=M;
  31813. res(x,y,z,c) = (Ttfloat)(N?val/std::sqrt(N):0);
  31814. } _cimg_abort_catch_omp2
  31815. if (boundary_conditions)
  31816. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel))
  31817. cimg_forYZ(res,y,z) _cimg_abort_try_omp2 {
  31818. cimg_abort_test2;
  31819. for (int x = 0; x<width();
  31820. (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
  31821. Ttfloat val = 0, N = 0;
  31822. for (int zm = -mz1; zm<=mz2; ++zm)
  31823. for (int ym = -my1; ym<=my2; ++ym)
  31824. for (int xm = -mx1; xm<=mx2; ++xm) {
  31825. const Ttfloat _val = (Ttfloat)img._atXYZ(x + xm,y + ym,z + zm);
  31826. val+=_val*K(mx1 + xm,my1 + ym,mz1 + zm);
  31827. N+=_val*_val;
  31828. }
  31829. N*=M;
  31830. res(x,y,z,c) = (Ttfloat)(N?val/std::sqrt(N):0);
  31831. }
  31832. } _cimg_abort_catch_omp2
  31833. else
  31834. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel))
  31835. cimg_forYZ(res,y,z) _cimg_abort_try_omp2 {
  31836. cimg_abort_test2;
  31837. for (int x = 0; x<width();
  31838. (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
  31839. Ttfloat val = 0, N = 0;
  31840. for (int zm = -mz1; zm<=mz2; ++zm)
  31841. for (int ym = -my1; ym<=my2; ++ym)
  31842. for (int xm = -mx1; xm<=mx2; ++xm) {
  31843. const Ttfloat _val = (Ttfloat)img.atXYZ(x + xm,y + ym,z + zm,0,(T)0);
  31844. val+=_val*K(mx1 + xm,my1 + ym,mz1 + zm);
  31845. N+=_val*_val;
  31846. }
  31847. N*=M;
  31848. res(x,y,z,c) = (Ttfloat)(N?val/std::sqrt(N):0);
  31849. }
  31850. } _cimg_abort_catch_omp2
  31851. } else { // Classical correlation.
  31852. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(is_inner_parallel))
  31853. for (int z = mz1; z<mze; ++z)
  31854. for (int y = my1; y<mye; ++y)
  31855. for (int x = mx1; x<mxe; ++x) _cimg_abort_try_omp2 {
  31856. cimg_abort_test2;
  31857. Ttfloat val = 0;
  31858. for (int zm = -mz1; zm<=mz2; ++zm)
  31859. for (int ym = -my1; ym<=my2; ++ym)
  31860. for (int xm = -mx1; xm<=mx2; ++xm)
  31861. val+=img(x + xm,y + ym,z + zm)*K(mx1 + xm,my1 + ym,mz1 + zm);
  31862. res(x,y,z,c) = (Ttfloat)val;
  31863. } _cimg_abort_catch_omp2
  31864. if (boundary_conditions)
  31865. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel))
  31866. cimg_forYZ(res,y,z) _cimg_abort_try_omp2 {
  31867. cimg_abort_test2;
  31868. for (int x = 0; x<width();
  31869. (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
  31870. Ttfloat val = 0;
  31871. for (int zm = -mz1; zm<=mz2; ++zm)
  31872. for (int ym = -my1; ym<=my2; ++ym)
  31873. for (int xm = -mx1; xm<=mx2; ++xm)
  31874. val+=img._atXYZ(x + xm,y + ym,z + zm)*K(mx1 + xm,my1 + ym,mz1 + zm);
  31875. res(x,y,z,c) = (Ttfloat)val;
  31876. }
  31877. } _cimg_abort_catch_omp2
  31878. else
  31879. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel))
  31880. cimg_forYZ(res,y,z) _cimg_abort_try_omp2 {
  31881. cimg_abort_test2;
  31882. for (int x = 0; x<width();
  31883. (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
  31884. Ttfloat val = 0;
  31885. for (int zm = -mz1; zm<=mz2; ++zm)
  31886. for (int ym = -my1; ym<=my2; ++ym)
  31887. for (int xm = -mx1; xm<=mx2; ++xm)
  31888. val+=img.atXYZ(x + xm,y + ym,z + zm,0,(T)0)*K(mx1 + xm,my1 + ym,mz1 + zm);
  31889. res(x,y,z,c) = (Ttfloat)val;
  31890. }
  31891. } _cimg_abort_catch_omp2
  31892. }
  31893. } _cimg_abort_catch_omp
  31894. }
  31895. cimg_abort_test;
  31896. return res;
  31897. }
  31898. //! Convolve image by a kernel.
  31899. /**
  31900. \param kernel = the correlation kernel.
  31901. \param boundary_conditions boundary conditions can be (false=dirichlet, true=neumann)
  31902. \param is_normalized = enable local normalization.
  31903. \note
  31904. - The result \p res of the convolution of an image \p img by a kernel \p kernel is defined to be:
  31905. res(x,y,z) = sum_{i,j,k} img(x-i,y-j,z-k)*kernel(i,j,k)
  31906. **/
  31907. template<typename t>
  31908. CImg<T>& convolve(const CImg<t>& kernel, const bool boundary_conditions=true, const bool is_normalized=false) {
  31909. if (is_empty() || !kernel) return *this;
  31910. return get_convolve(kernel,boundary_conditions,is_normalized).move_to(*this);
  31911. }
  31912. //! Convolve image by a kernel \newinstance.
  31913. template<typename t>
  31914. CImg<_cimg_Ttfloat> get_convolve(const CImg<t>& kernel, const bool boundary_conditions=true,
  31915. const bool is_normalized=false) const {
  31916. return _correlate(CImg<t>(kernel._data,kernel.size()/kernel._spectrum,1,1,kernel._spectrum,true).
  31917. get_mirror('x').resize(kernel,-1),boundary_conditions,is_normalized,true);
  31918. }
  31919. //! Cumulate image values, optionally along specified axis.
  31920. /**
  31921. \param axis Cumulation axis. Set it to 0 to cumulate all values globally without taking axes into account.
  31922. **/
  31923. CImg<T>& cumulate(const char axis=0) {
  31924. switch (cimg::lowercase(axis)) {
  31925. case 'x' :
  31926. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=512 && _height*_depth*_spectrum>=16))
  31927. cimg_forYZC(*this,y,z,c) {
  31928. T *ptrd = data(0,y,z,c);
  31929. Tlong cumul = (Tlong)0;
  31930. cimg_forX(*this,x) { cumul+=(Tlong)*ptrd; *(ptrd++) = (T)cumul; }
  31931. }
  31932. break;
  31933. case 'y' : {
  31934. const ulongT w = (ulongT)_width;
  31935. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_height>=512 && _width*_depth*_spectrum>=16))
  31936. cimg_forXZC(*this,x,z,c) {
  31937. T *ptrd = data(x,0,z,c);
  31938. Tlong cumul = (Tlong)0;
  31939. cimg_forY(*this,y) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=w; }
  31940. }
  31941. } break;
  31942. case 'z' : {
  31943. const ulongT wh = (ulongT)_width*_height;
  31944. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_depth>=512 && _width*_depth*_spectrum>=16))
  31945. cimg_forXYC(*this,x,y,c) {
  31946. T *ptrd = data(x,y,0,c);
  31947. Tlong cumul = (Tlong)0;
  31948. cimg_forZ(*this,z) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=wh; }
  31949. }
  31950. } break;
  31951. case 'c' : {
  31952. const ulongT whd = (ulongT)_width*_height*_depth;
  31953. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_spectrum>=512 && _width*_height*_depth>=16))
  31954. cimg_forXYZ(*this,x,y,z) {
  31955. T *ptrd = data(x,y,z,0);
  31956. Tlong cumul = (Tlong)0;
  31957. cimg_forC(*this,c) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=whd; }
  31958. }
  31959. } break;
  31960. default : { // Global cumulation.
  31961. Tlong cumul = (Tlong)0;
  31962. cimg_for(*this,ptrd,T) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; }
  31963. }
  31964. }
  31965. return *this;
  31966. }
  31967. //! Cumulate image values, optionally along specified axis \newinstance.
  31968. CImg<Tlong> get_cumulate(const char axis=0) const {
  31969. return CImg<Tlong>(*this,false).cumulate(axis);
  31970. }
  31971. //! Cumulate image values, along specified axes.
  31972. /**
  31973. \param axes Cumulation axes, as a C-string.
  31974. \note \c axes may contains multiple characters, e.g. \c "xyz"
  31975. **/
  31976. CImg<T>& cumulate(const char *const axes) {
  31977. for (const char *s = axes; *s; ++s) cumulate(*s);
  31978. return *this;
  31979. }
  31980. //! Cumulate image values, along specified axes \newinstance.
  31981. CImg<Tlong> get_cumulate(const char *const axes) const {
  31982. return CImg<Tlong>(*this,false).cumulate(axes);
  31983. }
  31984. //! Erode image by a structuring element.
  31985. /**
  31986. \param kernel Structuring element.
  31987. \param boundary_conditions Boundary conditions.
  31988. \param is_real Do the erosion in real (a.k.a 'non-flat') mode (\c true) rather than binary mode (\c false).
  31989. **/
  31990. template<typename t>
  31991. CImg<T>& erode(const CImg<t>& kernel, const bool boundary_conditions=true,
  31992. const bool is_real=false) {
  31993. if (is_empty() || !kernel) return *this;
  31994. return get_erode(kernel,boundary_conditions,is_real).move_to(*this);
  31995. }
  31996. //! Erode image by a structuring element \newinstance.
  31997. template<typename t>
  31998. CImg<_cimg_Tt> get_erode(const CImg<t>& kernel, const bool boundary_conditions=true,
  31999. const bool is_real=false) const {
  32000. if (is_empty() || !kernel) return *this;
  32001. if (!is_real && kernel==0) return CImg<T>(width(),height(),depth(),spectrum(),0);
  32002. typedef _cimg_Tt Tt;
  32003. CImg<Tt> res(_width,_height,_depth,std::max(_spectrum,kernel._spectrum));
  32004. const int
  32005. mx2 = kernel.width()/2, my2 = kernel.height()/2, mz2 = kernel.depth()/2,
  32006. mx1 = kernel.width() - mx2 - 1, my1 = kernel.height() - my2 - 1, mz1 = kernel.depth() - mz2 - 1,
  32007. mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2;
  32008. const bool
  32009. is_inner_parallel = _width*_height*_depth>=32768,
  32010. is_outer_parallel = res.size()>=32768;
  32011. cimg::unused(is_inner_parallel,is_outer_parallel);
  32012. _cimg_abort_init_omp;
  32013. cimg_abort_init;
  32014. cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel))
  32015. cimg_forC(res,c) _cimg_abort_try_omp {
  32016. cimg_abort_test;
  32017. const CImg<T> img = get_shared_channel(c%_spectrum);
  32018. const CImg<t> K = kernel.get_shared_channel(c%kernel._spectrum);
  32019. if (is_real) { // Real erosion
  32020. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(is_inner_parallel))
  32021. for (int z = mz1; z<mze; ++z)
  32022. for (int y = my1; y<mye; ++y)
  32023. for (int x = mx1; x<mxe; ++x) _cimg_abort_try_omp2 {
  32024. cimg_abort_test2;
  32025. Tt min_val = cimg::type<Tt>::max();
  32026. for (int zm = -mz1; zm<=mz2; ++zm)
  32027. for (int ym = -my1; ym<=my2; ++ym)
  32028. for (int xm = -mx1; xm<=mx2; ++xm) {
  32029. const t mval = K(mx1 + xm,my1 + ym,mz1 + zm);
  32030. const Tt cval = (Tt)(img(x + xm,y + ym,z + zm) - mval);
  32031. if (cval<min_val) min_val = cval;
  32032. }
  32033. res(x,y,z,c) = min_val;
  32034. } _cimg_abort_catch_omp2
  32035. if (boundary_conditions)
  32036. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel))
  32037. cimg_forYZ(res,y,z) _cimg_abort_try_omp2 {
  32038. cimg_abort_test2;
  32039. for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
  32040. Tt min_val = cimg::type<Tt>::max();
  32041. for (int zm = -mz1; zm<=mz2; ++zm)
  32042. for (int ym = -my1; ym<=my2; ++ym)
  32043. for (int xm = -mx1; xm<=mx2; ++xm) {
  32044. const t mval = K(mx1 + xm,my1 + ym,mz1 + zm);
  32045. const Tt cval = (Tt)(img._atXYZ(x + xm,y + ym,z + zm) - mval);
  32046. if (cval<min_val) min_val = cval;
  32047. }
  32048. res(x,y,z,c) = min_val;
  32049. }
  32050. } _cimg_abort_catch_omp2
  32051. else
  32052. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel))
  32053. cimg_forYZ(res,y,z) _cimg_abort_try_omp2 {
  32054. cimg_abort_test2;
  32055. for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
  32056. Tt min_val = cimg::type<Tt>::max();
  32057. for (int zm = -mz1; zm<=mz2; ++zm)
  32058. for (int ym = -my1; ym<=my2; ++ym)
  32059. for (int xm = -mx1; xm<=mx2; ++xm) {
  32060. const t mval = K(mx1 + xm,my1 + ym,mz1 + zm);
  32061. const Tt cval = (Tt)(img.atXYZ(x + xm,y + ym,z + zm,0,(T)0) - mval);
  32062. if (cval<min_val) min_val = cval;
  32063. }
  32064. res(x,y,z,c) = min_val;
  32065. }
  32066. } _cimg_abort_catch_omp2
  32067. } else { // Binary erosion
  32068. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(is_inner_parallel))
  32069. for (int z = mz1; z<mze; ++z)
  32070. for (int y = my1; y<mye; ++y)
  32071. for (int x = mx1; x<mxe; ++x) _cimg_abort_try_omp2 {
  32072. cimg_abort_test2;
  32073. Tt min_val = cimg::type<Tt>::max();
  32074. for (int zm = -mz1; zm<=mz2; ++zm)
  32075. for (int ym = -my1; ym<=my2; ++ym)
  32076. for (int xm = -mx1; xm<=mx2; ++xm)
  32077. if (K(mx1 + xm,my1 + ym,mz1 + zm)) {
  32078. const Tt cval = (Tt)img(x + xm,y + ym,z + zm);
  32079. if (cval<min_val) min_val = cval;
  32080. }
  32081. res(x,y,z,c) = min_val;
  32082. } _cimg_abort_catch_omp2
  32083. if (boundary_conditions)
  32084. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel))
  32085. cimg_forYZ(res,y,z) _cimg_abort_try_omp2 {
  32086. cimg_abort_test2;
  32087. for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
  32088. Tt min_val = cimg::type<Tt>::max();
  32089. for (int zm = -mz1; zm<=mz2; ++zm)
  32090. for (int ym = -my1; ym<=my2; ++ym)
  32091. for (int xm = -mx1; xm<=mx2; ++xm)
  32092. if (K(mx1 + xm,my1 + ym,mz1 + zm)) {
  32093. const T cval = (Tt)img._atXYZ(x + xm,y + ym,z + zm);
  32094. if (cval<min_val) min_val = cval;
  32095. }
  32096. res(x,y,z,c) = min_val;
  32097. }
  32098. } _cimg_abort_catch_omp2
  32099. else
  32100. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel))
  32101. cimg_forYZ(res,y,z) _cimg_abort_try_omp2 {
  32102. cimg_abort_test2;
  32103. for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
  32104. Tt min_val = cimg::type<Tt>::max();
  32105. for (int zm = -mz1; zm<=mz2; ++zm)
  32106. for (int ym = -my1; ym<=my2; ++ym)
  32107. for (int xm = -mx1; xm<=mx2; ++xm)
  32108. if (K(mx1 + xm,my1 + ym,mz1 + zm)) {
  32109. const T cval = (Tt)img.atXYZ(x + xm,y + ym,z + zm,0,(T)0);
  32110. if (cval<min_val) min_val = cval;
  32111. }
  32112. res(x,y,z,c) = min_val;
  32113. }
  32114. } _cimg_abort_catch_omp2
  32115. }
  32116. } _cimg_abort_catch_omp
  32117. cimg_abort_test;
  32118. return res;
  32119. }
  32120. //! Erode image by a rectangular structuring element of specified size.
  32121. /**
  32122. \param sx Width of the structuring element.
  32123. \param sy Height of the structuring element.
  32124. \param sz Depth of the structuring element.
  32125. **/
  32126. CImg<T>& erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) {
  32127. if (is_empty() || (sx==1 && sy==1 && sz==1)) return *this;
  32128. if (sx>1 && _width>1) { // Along X-axis.
  32129. 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;
  32130. CImg<T> buf(L);
  32131. cimg_pragma_openmp(parallel for collapse(3) firstprivate(buf) if (size()>524288))
  32132. cimg_forYZC(*this,y,z,c) {
  32133. T *const ptrdb = buf._data, *ptrd = buf._data, *const ptrde = buf._data + L - 1;
  32134. const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
  32135. T cur = *ptrs; ptrs+=off; bool is_first = true;
  32136. for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
  32137. const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }}
  32138. *(ptrd++) = cur;
  32139. if (ptrs>=ptrse) {
  32140. T *pd = data(0,y,z,c); cur = std::min(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; }
  32141. } else {
  32142. for (int p = s1; p>0 && ptrd<=ptrde; --p) {
  32143. const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
  32144. *(ptrd++) = cur;
  32145. }
  32146. for (int p = L - s - 1; p>0; --p) {
  32147. const T val = *ptrs; ptrs+=off;
  32148. if (is_first) {
  32149. const T *nptrs = ptrs - off; cur = val;
  32150. for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
  32151. nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
  32152. } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
  32153. *(ptrd++) = cur;
  32154. }
  32155. ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
  32156. for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
  32157. const T val = *ptrs; ptrs-=off; if (val<cur) cur = val;
  32158. }
  32159. *(ptrd--) = cur;
  32160. for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
  32161. const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur;
  32162. }
  32163. T *pd = data(0,y,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
  32164. }
  32165. }
  32166. }
  32167. if (sy>1 && _height>1) { // Along Y-axis.
  32168. const int L = height(), off = width(), s = (int)sy, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1,
  32169. s2 = _s2>L?L:_s2;
  32170. CImg<T> buf(L);
  32171. cimg_pragma_openmp(parallel for collapse(3) firstprivate(buf) if (size()>524288))
  32172. cimg_forXZC(*this,x,z,c) {
  32173. T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
  32174. const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
  32175. T cur = *ptrs; ptrs+=off; bool is_first = true;
  32176. for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
  32177. const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
  32178. }
  32179. *(ptrd++) = cur;
  32180. if (ptrs>=ptrse) {
  32181. T *pd = data(x,0,z,c); cur = std::min(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; }
  32182. } else {
  32183. for (int p = s1; p>0 && ptrd<=ptrde; --p) {
  32184. const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
  32185. *(ptrd++) = cur;
  32186. }
  32187. for (int p = L - s - 1; p>0; --p) {
  32188. const T val = *ptrs; ptrs+=off;
  32189. if (is_first) {
  32190. const T *nptrs = ptrs - off; cur = val;
  32191. for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
  32192. nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
  32193. } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
  32194. *(ptrd++) = cur;
  32195. }
  32196. ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
  32197. for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
  32198. const T val = *ptrs; ptrs-=off; if (val<cur) cur = val;
  32199. }
  32200. *(ptrd--) = cur;
  32201. for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
  32202. const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur;
  32203. }
  32204. T *pd = data(x,0,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
  32205. }
  32206. }
  32207. }
  32208. if (sz>1 && _depth>1) { // Along Z-axis.
  32209. const int L = depth(), off = width()*height(), s = (int)sz, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1,
  32210. s2 = _s2>L?L:_s2;
  32211. CImg<T> buf(L);
  32212. cimg_pragma_openmp(parallel for collapse(3) firstprivate(buf) if (size()>524288))
  32213. cimg_forXYC(*this,x,y,c) {
  32214. T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
  32215. const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
  32216. T cur = *ptrs; ptrs+=off; bool is_first = true;
  32217. for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
  32218. const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
  32219. }
  32220. *(ptrd++) = cur;
  32221. if (ptrs>=ptrse) {
  32222. T *pd = data(x,y,0,c); cur = std::min(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; }
  32223. } else {
  32224. for (int p = s1; p>0 && ptrd<=ptrde; --p) {
  32225. const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
  32226. *(ptrd++) = cur;
  32227. }
  32228. for (int p = L - s - 1; p>0; --p) {
  32229. const T val = *ptrs; ptrs+=off;
  32230. if (is_first) {
  32231. const T *nptrs = ptrs - off; cur = val;
  32232. for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
  32233. nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
  32234. } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
  32235. *(ptrd++) = cur;
  32236. }
  32237. ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
  32238. for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
  32239. const T val = *ptrs; ptrs-=off; if (val<cur) cur = val;
  32240. }
  32241. *(ptrd--) = cur;
  32242. for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
  32243. const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur;
  32244. }
  32245. T *pd = data(x,y,0,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
  32246. }
  32247. }
  32248. }
  32249. return *this;
  32250. }
  32251. //! Erode image by a rectangular structuring element of specified size \newinstance.
  32252. CImg<T> get_erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const {
  32253. return (+*this).erode(sx,sy,sz);
  32254. }
  32255. //! Erode the image by a square structuring element of specified size.
  32256. /**
  32257. \param s Size of the structuring element.
  32258. **/
  32259. CImg<T>& erode(const unsigned int s) {
  32260. return erode(s,s,s);
  32261. }
  32262. //! Erode the image by a square structuring element of specified size \newinstance.
  32263. CImg<T> get_erode(const unsigned int s) const {
  32264. return (+*this).erode(s);
  32265. }
  32266. //! Dilate image by a structuring element.
  32267. /**
  32268. \param kernel Structuring element.
  32269. \param boundary_conditions Boundary conditions.
  32270. \param is_real Do the dilation in real (a.k.a 'non-flat') mode (\c true) rather than binary mode (\c false).
  32271. **/
  32272. template<typename t>
  32273. CImg<T>& dilate(const CImg<t>& kernel, const bool boundary_conditions=true,
  32274. const bool is_real=false) {
  32275. if (is_empty() || !kernel) return *this;
  32276. return get_dilate(kernel,boundary_conditions,is_real).move_to(*this);
  32277. }
  32278. //! Dilate image by a structuring element \newinstance.
  32279. template<typename t>
  32280. CImg<_cimg_Tt> get_dilate(const CImg<t>& kernel, const bool boundary_conditions=true,
  32281. const bool is_real=false) const {
  32282. if (is_empty() || !kernel || (!is_real && kernel==0)) return *this;
  32283. typedef _cimg_Tt Tt;
  32284. CImg<Tt> res(_width,_height,_depth,std::max(_spectrum,kernel._spectrum));
  32285. const int
  32286. mx1 = kernel.width()/2, my1 = kernel.height()/2, mz1 = kernel.depth()/2,
  32287. mx2 = kernel.width() - mx1 - 1, my2 = kernel.height() - my1 - 1, mz2 = kernel.depth() - mz1 - 1,
  32288. mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2;
  32289. const bool
  32290. is_inner_parallel = _width*_height*_depth>=32768,
  32291. is_outer_parallel = res.size()>=32768;
  32292. cimg::unused(is_inner_parallel,is_outer_parallel);
  32293. _cimg_abort_init_omp;
  32294. cimg_abort_init;
  32295. cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel))
  32296. cimg_forC(res,c) _cimg_abort_try_omp {
  32297. cimg_abort_test;
  32298. const CImg<T> img = get_shared_channel(c%_spectrum);
  32299. const CImg<t> K = kernel.get_shared_channel(c%kernel._spectrum);
  32300. if (is_real) { // Real dilation
  32301. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(is_inner_parallel))
  32302. for (int z = mz1; z<mze; ++z)
  32303. for (int y = my1; y<mye; ++y)
  32304. for (int x = mx1; x<mxe; ++x) _cimg_abort_try_omp2 {
  32305. cimg_abort_test2;
  32306. Tt max_val = cimg::type<Tt>::min();
  32307. for (int zm = -mz1; zm<=mz2; ++zm)
  32308. for (int ym = -my1; ym<=my2; ++ym)
  32309. for (int xm = -mx1; xm<=mx2; ++xm) {
  32310. const t mval = K(mx2 - xm,my2 - ym,mz2 - zm);
  32311. const Tt cval = (Tt)(img(x + xm,y + ym,z + zm) + mval);
  32312. if (cval>max_val) max_val = cval;
  32313. }
  32314. res(x,y,z,c) = max_val;
  32315. } _cimg_abort_catch_omp2
  32316. if (boundary_conditions)
  32317. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel))
  32318. cimg_forYZ(res,y,z) _cimg_abort_try_omp2 {
  32319. cimg_abort_test2;
  32320. for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
  32321. Tt max_val = cimg::type<Tt>::min();
  32322. for (int zm = -mz1; zm<=mz2; ++zm)
  32323. for (int ym = -my1; ym<=my2; ++ym)
  32324. for (int xm = -mx1; xm<=mx2; ++xm) {
  32325. const t mval = K(mx2 - xm,my2 - ym,mz2 - zm);
  32326. const Tt cval = (Tt)(img._atXYZ(x + xm,y + ym,z + zm) + mval);
  32327. if (cval>max_val) max_val = cval;
  32328. }
  32329. res(x,y,z,c) = max_val;
  32330. }
  32331. } _cimg_abort_catch_omp2
  32332. else
  32333. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel))
  32334. cimg_forYZ(*this,y,z) _cimg_abort_try_omp2 {
  32335. cimg_abort_test2;
  32336. for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
  32337. Tt max_val = cimg::type<Tt>::min();
  32338. for (int zm = -mz1; zm<=mz2; ++zm)
  32339. for (int ym = -my1; ym<=my2; ++ym)
  32340. for (int xm = -mx1; xm<=mx2; ++xm) {
  32341. const t mval = K(mx2 - xm,my2 - ym,mz2 - zm);
  32342. const Tt cval = (Tt)(img.atXYZ(x + xm,y + ym,z + zm,0,(T)0) + mval);
  32343. if (cval>max_val) max_val = cval;
  32344. }
  32345. res(x,y,z,c) = max_val;
  32346. }
  32347. } _cimg_abort_catch_omp2
  32348. } else { // Binary dilation
  32349. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(is_inner_parallel))
  32350. for (int z = mz1; z<mze; ++z)
  32351. for (int y = my1; y<mye; ++y)
  32352. for (int x = mx1; x<mxe; ++x) _cimg_abort_try_omp2 {
  32353. cimg_abort_test2;
  32354. Tt max_val = cimg::type<Tt>::min();
  32355. for (int zm = -mz1; zm<=mz2; ++zm)
  32356. for (int ym = -my1; ym<=my2; ++ym)
  32357. for (int xm = -mx1; xm<=mx2; ++xm)
  32358. if (K(mx2 - xm,my2 - ym,mz2 - zm)) {
  32359. const Tt cval = (Tt)img(x + xm,y + ym,z + zm);
  32360. if (cval>max_val) max_val = cval;
  32361. }
  32362. res(x,y,z,c) = max_val;
  32363. } _cimg_abort_catch_omp2
  32364. if (boundary_conditions)
  32365. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel))
  32366. cimg_forYZ(res,y,z) _cimg_abort_try_omp2 {
  32367. cimg_abort_test2;
  32368. for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
  32369. Tt max_val = cimg::type<Tt>::min();
  32370. for (int zm = -mz1; zm<=mz2; ++zm)
  32371. for (int ym = -my1; ym<=my2; ++ym)
  32372. for (int xm = -mx1; xm<=mx2; ++xm)
  32373. if (K(mx2 - xm,my2 - ym,mz2 - zm)) {
  32374. const T cval = (Tt)img._atXYZ(x + xm,y + ym,z + zm);
  32375. if (cval>max_val) max_val = cval;
  32376. }
  32377. res(x,y,z,c) = max_val;
  32378. }
  32379. } _cimg_abort_catch_omp2
  32380. else
  32381. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel))
  32382. cimg_forYZ(res,y,z) _cimg_abort_try_omp2 {
  32383. cimg_abort_test2;
  32384. for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
  32385. Tt max_val = cimg::type<Tt>::min();
  32386. for (int zm = -mz1; zm<=mz2; ++zm)
  32387. for (int ym = -my1; ym<=my2; ++ym)
  32388. for (int xm = -mx1; xm<=mx2; ++xm)
  32389. if (K(mx2 - xm,my2 - ym,mz2 - zm)) {
  32390. const T cval = (Tt)img.atXYZ(x + xm,y + ym,z + zm,0,(T)0);
  32391. if (cval>max_val) max_val = cval;
  32392. }
  32393. res(x,y,z,c) = max_val;
  32394. }
  32395. } _cimg_abort_catch_omp2
  32396. }
  32397. } _cimg_abort_catch_omp
  32398. cimg_abort_test;
  32399. return res;
  32400. }
  32401. //! Dilate image by a rectangular structuring element of specified size.
  32402. /**
  32403. \param sx Width of the structuring element.
  32404. \param sy Height of the structuring element.
  32405. \param sz Depth of the structuring element.
  32406. **/
  32407. CImg<T>& dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) {
  32408. if (is_empty() || (sx==1 && sy==1 && sz==1)) return *this;
  32409. if (sx>1 && _width>1) { // Along X-axis.
  32410. 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;
  32411. CImg<T> buf(L);
  32412. cimg_pragma_openmp(parallel for collapse(3) firstprivate(buf) if (size()>524288))
  32413. cimg_forYZC(*this,y,z,c) {
  32414. T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
  32415. const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
  32416. T cur = *ptrs; ptrs+=off; bool is_first = true;
  32417. for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
  32418. const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
  32419. }
  32420. *(ptrd++) = cur;
  32421. if (ptrs>=ptrse) {
  32422. T *pd = data(0,y,z,c); cur = std::max(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; }
  32423. } else {
  32424. for (int p = s1; p>0 && ptrd<=ptrde; --p) {
  32425. const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
  32426. *(ptrd++) = cur;
  32427. }
  32428. for (int p = L - s - 1; p>0; --p) {
  32429. const T val = *ptrs; ptrs+=off;
  32430. if (is_first) {
  32431. const T *nptrs = ptrs - off; cur = val;
  32432. for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
  32433. nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
  32434. } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
  32435. *(ptrd++) = cur;
  32436. }
  32437. ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
  32438. for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
  32439. const T val = *ptrs; ptrs-=off; if (val>cur) cur = val;
  32440. }
  32441. *(ptrd--) = cur;
  32442. for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
  32443. const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur;
  32444. }
  32445. T *pd = data(0,y,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
  32446. }
  32447. }
  32448. }
  32449. if (sy>1 && _height>1) { // Along Y-axis.
  32450. const int L = height(), off = width(), s = (int)sy, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1,
  32451. s2 = _s2>L?L:_s2;
  32452. CImg<T> buf(L);
  32453. cimg_pragma_openmp(parallel for collapse(3) firstprivate(buf) if (size()>524288))
  32454. cimg_forXZC(*this,x,z,c) {
  32455. T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
  32456. const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
  32457. T cur = *ptrs; ptrs+=off; bool is_first = true;
  32458. for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
  32459. const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
  32460. }
  32461. *(ptrd++) = cur;
  32462. if (ptrs>=ptrse) {
  32463. T *pd = data(x,0,z,c); cur = std::max(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; }
  32464. } else {
  32465. for (int p = s1; p>0 && ptrd<=ptrde; --p) {
  32466. const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
  32467. *(ptrd++) = cur;
  32468. }
  32469. for (int p = L - s - 1; p>0; --p) {
  32470. const T val = *ptrs; ptrs+=off;
  32471. if (is_first) {
  32472. const T *nptrs = ptrs - off; cur = val;
  32473. for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
  32474. nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
  32475. } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
  32476. *(ptrd++) = cur;
  32477. }
  32478. ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
  32479. for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
  32480. const T val = *ptrs; ptrs-=off; if (val>cur) cur = val;
  32481. }
  32482. *(ptrd--) = cur;
  32483. for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
  32484. const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur;
  32485. }
  32486. T *pd = data(x,0,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
  32487. }
  32488. }
  32489. }
  32490. if (sz>1 && _depth>1) { // Along Z-axis.
  32491. const int L = depth(), off = width()*height(), s = (int)sz, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1,
  32492. s2 = _s2>L?L:_s2;
  32493. CImg<T> buf(L);
  32494. cimg_pragma_openmp(parallel for collapse(3) firstprivate(buf) if (size()>524288))
  32495. cimg_forXYC(*this,x,y,c) {
  32496. T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
  32497. const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
  32498. T cur = *ptrs; ptrs+=off; bool is_first = true;
  32499. for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
  32500. const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
  32501. }
  32502. *(ptrd++) = cur;
  32503. if (ptrs>=ptrse) {
  32504. T *pd = data(x,y,0,c); cur = std::max(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; }
  32505. } else {
  32506. for (int p = s1; p>0 && ptrd<=ptrde; --p) {
  32507. const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
  32508. *(ptrd++) = cur;
  32509. }
  32510. for (int p = L - s - 1; p>0; --p) {
  32511. const T val = *ptrs; ptrs+=off;
  32512. if (is_first) {
  32513. const T *nptrs = ptrs - off; cur = val;
  32514. for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
  32515. nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
  32516. } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
  32517. *(ptrd++) = cur;
  32518. }
  32519. ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
  32520. for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
  32521. const T val = *ptrs; ptrs-=off; if (val>cur) cur = val;
  32522. }
  32523. *(ptrd--) = cur;
  32524. for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
  32525. const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur;
  32526. }
  32527. T *pd = data(x,y,0,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
  32528. }
  32529. }
  32530. }
  32531. return *this;
  32532. }
  32533. //! Dilate image by a rectangular structuring element of specified size \newinstance.
  32534. CImg<T> get_dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const {
  32535. return (+*this).dilate(sx,sy,sz);
  32536. }
  32537. //! Dilate image by a square structuring element of specified size.
  32538. /**
  32539. \param s Size of the structuring element.
  32540. **/
  32541. CImg<T>& dilate(const unsigned int s) {
  32542. return dilate(s,s,s);
  32543. }
  32544. //! Dilate image by a square structuring element of specified size \newinstance.
  32545. CImg<T> get_dilate(const unsigned int s) const {
  32546. return (+*this).dilate(s);
  32547. }
  32548. //! Compute watershed transform.
  32549. /**
  32550. \param priority Priority map.
  32551. \param is_high_connectivity Boolean that choose between 4(false)- or 8(true)-connectivity
  32552. in 2d case, and between 6(false)- or 26(true)-connectivity in 3d case.
  32553. \note Non-zero values of the instance instance are propagated to zero-valued ones according to
  32554. specified the priority map.
  32555. **/
  32556. template<typename t>
  32557. CImg<T>& watershed(const CImg<t>& priority, const bool is_high_connectivity=false) {
  32558. #define _cimg_watershed_init(cond,X,Y,Z) \
  32559. if (cond && !(*this)(X,Y,Z)) Q._priority_queue_insert(labels,sizeQ,priority(X,Y,Z),X,Y,Z,nb_seeds)
  32560. #define _cimg_watershed_propagate(cond,X,Y,Z) \
  32561. if (cond) { \
  32562. if ((*this)(X,Y,Z)) { \
  32563. ns = labels(X,Y,Z) - 1; xs = seeds(ns,0); ys = seeds(ns,1); zs = seeds(ns,2); \
  32564. d = cimg::sqr((float)x - xs) + cimg::sqr((float)y - ys) + cimg::sqr((float)z - zs); \
  32565. if (d<dmin) { dmin = d; nmin = ns; label = (*this)(xs,ys,zs); } \
  32566. } else Q._priority_queue_insert(labels,sizeQ,priority(X,Y,Z),X,Y,Z,n); \
  32567. }
  32568. if (is_empty()) return *this;
  32569. if (!is_sameXYZ(priority))
  32570. throw CImgArgumentException(_cimg_instance
  32571. "watershed(): image instance and specified priority (%u,%u,%u,%u,%p) "
  32572. "have different dimensions.",
  32573. cimg_instance,
  32574. priority._width,priority._height,priority._depth,priority._spectrum,priority._data);
  32575. if (_spectrum!=1) {
  32576. cimg_forC(*this,c)
  32577. get_shared_channel(c).watershed(priority.get_shared_channel(c%priority._spectrum));
  32578. return *this;
  32579. }
  32580. CImg<uintT> labels(_width,_height,_depth,1,0), seeds(64,3);
  32581. CImg<typename cimg::superset2<T,t,int>::type> Q;
  32582. unsigned int sizeQ = 0;
  32583. int px, nx, py, ny, pz, nz;
  32584. bool is_px, is_nx, is_py, is_ny, is_pz, is_nz;
  32585. const bool is_3d = _depth>1;
  32586. // Find seed points and insert them in priority queue.
  32587. unsigned int nb_seeds = 0;
  32588. const T *ptrs = _data;
  32589. cimg_forXYZ(*this,x,y,z) if (*(ptrs++)) { // 3d version
  32590. if (nb_seeds>=seeds._width) seeds.resize(2*seeds._width,3,1,1,0);
  32591. seeds(nb_seeds,0) = x; seeds(nb_seeds,1) = y; seeds(nb_seeds++,2) = z;
  32592. px = x - 1; nx = x + 1;
  32593. py = y - 1; ny = y + 1;
  32594. pz = z - 1; nz = z + 1;
  32595. is_px = px>=0; is_nx = nx<width();
  32596. is_py = py>=0; is_ny = ny<height();
  32597. is_pz = pz>=0; is_nz = nz<depth();
  32598. _cimg_watershed_init(is_px,px,y,z);
  32599. _cimg_watershed_init(is_nx,nx,y,z);
  32600. _cimg_watershed_init(is_py,x,py,z);
  32601. _cimg_watershed_init(is_ny,x,ny,z);
  32602. if (is_3d) {
  32603. _cimg_watershed_init(is_pz,x,y,pz);
  32604. _cimg_watershed_init(is_nz,x,y,nz);
  32605. }
  32606. if (is_high_connectivity) {
  32607. _cimg_watershed_init(is_px && is_py,px,py,z);
  32608. _cimg_watershed_init(is_nx && is_py,nx,py,z);
  32609. _cimg_watershed_init(is_px && is_ny,px,ny,z);
  32610. _cimg_watershed_init(is_nx && is_ny,nx,ny,z);
  32611. if (is_3d) {
  32612. _cimg_watershed_init(is_px && is_pz,px,y,pz);
  32613. _cimg_watershed_init(is_nx && is_pz,nx,y,pz);
  32614. _cimg_watershed_init(is_px && is_nz,px,y,nz);
  32615. _cimg_watershed_init(is_nx && is_nz,nx,y,nz);
  32616. _cimg_watershed_init(is_py && is_pz,x,py,pz);
  32617. _cimg_watershed_init(is_ny && is_pz,x,ny,pz);
  32618. _cimg_watershed_init(is_py && is_nz,x,py,nz);
  32619. _cimg_watershed_init(is_ny && is_nz,x,ny,nz);
  32620. _cimg_watershed_init(is_px && is_py && is_pz,px,py,pz);
  32621. _cimg_watershed_init(is_nx && is_py && is_pz,nx,py,pz);
  32622. _cimg_watershed_init(is_px && is_ny && is_pz,px,ny,pz);
  32623. _cimg_watershed_init(is_nx && is_ny && is_pz,nx,ny,pz);
  32624. _cimg_watershed_init(is_px && is_py && is_nz,px,py,nz);
  32625. _cimg_watershed_init(is_nx && is_py && is_nz,nx,py,nz);
  32626. _cimg_watershed_init(is_px && is_ny && is_nz,px,ny,nz);
  32627. _cimg_watershed_init(is_nx && is_ny && is_nz,nx,ny,nz);
  32628. }
  32629. }
  32630. labels(x,y,z) = nb_seeds;
  32631. }
  32632. // Start watershed computation.
  32633. while (sizeQ) {
  32634. // Get and remove point with maximal priority from the queue.
  32635. const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3);
  32636. const unsigned int n = labels(x,y,z);
  32637. px = x - 1; nx = x + 1;
  32638. py = y - 1; ny = y + 1;
  32639. pz = z - 1; nz = z + 1;
  32640. is_px = px>=0; is_nx = nx<width();
  32641. is_py = py>=0; is_ny = ny<height();
  32642. is_pz = pz>=0; is_nz = nz<depth();
  32643. // Check labels of the neighbors.
  32644. Q._priority_queue_remove(sizeQ);
  32645. unsigned int xs, ys, zs, ns, nmin = 0;
  32646. float d, dmin = cimg::type<float>::inf();
  32647. T label = (T)0;
  32648. _cimg_watershed_propagate(is_px,px,y,z);
  32649. _cimg_watershed_propagate(is_nx,nx,y,z);
  32650. _cimg_watershed_propagate(is_py,x,py,z);
  32651. _cimg_watershed_propagate(is_ny,x,ny,z);
  32652. if (is_3d) {
  32653. _cimg_watershed_propagate(is_pz,x,y,pz);
  32654. _cimg_watershed_propagate(is_nz,x,y,nz);
  32655. }
  32656. if (is_high_connectivity) {
  32657. _cimg_watershed_propagate(is_px && is_py,px,py,z);
  32658. _cimg_watershed_propagate(is_nx && is_py,nx,py,z);
  32659. _cimg_watershed_propagate(is_px && is_ny,px,ny,z);
  32660. _cimg_watershed_propagate(is_nx && is_ny,nx,ny,z);
  32661. if (is_3d) {
  32662. _cimg_watershed_propagate(is_px && is_pz,px,y,pz);
  32663. _cimg_watershed_propagate(is_nx && is_pz,nx,y,pz);
  32664. _cimg_watershed_propagate(is_px && is_nz,px,y,nz);
  32665. _cimg_watershed_propagate(is_nx && is_nz,nx,y,nz);
  32666. _cimg_watershed_propagate(is_py && is_pz,x,py,pz);
  32667. _cimg_watershed_propagate(is_ny && is_pz,x,ny,pz);
  32668. _cimg_watershed_propagate(is_py && is_nz,x,py,nz);
  32669. _cimg_watershed_propagate(is_ny && is_nz,x,ny,nz);
  32670. _cimg_watershed_propagate(is_px && is_py && is_pz,px,py,pz);
  32671. _cimg_watershed_propagate(is_nx && is_py && is_pz,nx,py,pz);
  32672. _cimg_watershed_propagate(is_px && is_ny && is_pz,px,ny,pz);
  32673. _cimg_watershed_propagate(is_nx && is_ny && is_pz,nx,ny,pz);
  32674. _cimg_watershed_propagate(is_px && is_py && is_nz,px,py,nz);
  32675. _cimg_watershed_propagate(is_nx && is_py && is_nz,nx,py,nz);
  32676. _cimg_watershed_propagate(is_px && is_ny && is_nz,px,ny,nz);
  32677. _cimg_watershed_propagate(is_nx && is_ny && is_nz,nx,ny,nz);
  32678. }
  32679. }
  32680. (*this)(x,y,z) = label;
  32681. labels(x,y,z) = ++nmin;
  32682. }
  32683. return *this;
  32684. }
  32685. //! Compute watershed transform \newinstance.
  32686. template<typename t>
  32687. CImg<T> get_watershed(const CImg<t>& priority, const bool is_high_connectivity=false) const {
  32688. return (+*this).watershed(priority,is_high_connectivity);
  32689. }
  32690. // [internal] Insert/Remove items in priority queue, for watershed/distance transforms.
  32691. template<typename tq, typename tv>
  32692. bool _priority_queue_insert(CImg<tq>& is_queued, unsigned int& siz, const tv value,
  32693. const unsigned int x, const unsigned int y, const unsigned int z,
  32694. const unsigned int n=1) {
  32695. if (is_queued(x,y,z)) return false;
  32696. is_queued(x,y,z) = (tq)n;
  32697. if (++siz>=_width) { if (!is_empty()) resize(_width*2,4,1,1,0); else assign(64,4); }
  32698. (*this)(siz - 1,0) = (T)value;
  32699. (*this)(siz - 1,1) = (T)x;
  32700. (*this)(siz - 1,2) = (T)y;
  32701. (*this)(siz - 1,3) = (T)z;
  32702. for (unsigned int pos = siz - 1, par = 0; pos && value>(*this)(par=(pos + 1)/2 - 1,0); pos = par) {
  32703. cimg::swap((*this)(pos,0),(*this)(par,0));
  32704. cimg::swap((*this)(pos,1),(*this)(par,1));
  32705. cimg::swap((*this)(pos,2),(*this)(par,2));
  32706. cimg::swap((*this)(pos,3),(*this)(par,3));
  32707. }
  32708. return true;
  32709. }
  32710. CImg<T>& _priority_queue_remove(unsigned int& siz) {
  32711. (*this)(0,0) = (*this)(--siz,0);
  32712. (*this)(0,1) = (*this)(siz,1);
  32713. (*this)(0,2) = (*this)(siz,2);
  32714. (*this)(0,3) = (*this)(siz,3);
  32715. const float value = (*this)(0,0);
  32716. for (unsigned int pos = 0, left = 0, right = 0;
  32717. ((right=2*(pos + 1),(left=right - 1))<siz && value<(*this)(left,0)) ||
  32718. (right<siz && value<(*this)(right,0));) {
  32719. if (right<siz) {
  32720. if ((*this)(left,0)>(*this)(right,0)) {
  32721. cimg::swap((*this)(pos,0),(*this)(left,0));
  32722. cimg::swap((*this)(pos,1),(*this)(left,1));
  32723. cimg::swap((*this)(pos,2),(*this)(left,2));
  32724. cimg::swap((*this)(pos,3),(*this)(left,3));
  32725. pos = left;
  32726. } else {
  32727. cimg::swap((*this)(pos,0),(*this)(right,0));
  32728. cimg::swap((*this)(pos,1),(*this)(right,1));
  32729. cimg::swap((*this)(pos,2),(*this)(right,2));
  32730. cimg::swap((*this)(pos,3),(*this)(right,3));
  32731. pos = right;
  32732. }
  32733. } else {
  32734. cimg::swap((*this)(pos,0),(*this)(left,0));
  32735. cimg::swap((*this)(pos,1),(*this)(left,1));
  32736. cimg::swap((*this)(pos,2),(*this)(left,2));
  32737. cimg::swap((*this)(pos,3),(*this)(left,3));
  32738. pos = left;
  32739. }
  32740. }
  32741. return *this;
  32742. }
  32743. //! Apply recursive Deriche filter.
  32744. /**
  32745. \param sigma Standard deviation of the filter.
  32746. \param order Order of the filter. Can be <tt>{ 0=smooth-filter | 1=1st-derivative | 2=2nd-derivative }</tt>.
  32747. \param axis Axis along which the filter is computed. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
  32748. \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.
  32749. **/
  32750. CImg<T>& deriche(const float sigma, const unsigned int order=0, const char axis='x',
  32751. const bool boundary_conditions=true) {
  32752. #define _cimg_deriche_apply \
  32753. CImg<Tfloat> Y(N); \
  32754. Tfloat *ptrY = Y._data, yb = 0, yp = 0; \
  32755. T xp = (T)0; \
  32756. if (boundary_conditions) { xp = *ptrX; yb = yp = (Tfloat)(coefp*xp); } \
  32757. for (int m = 0; m<N; ++m) { \
  32758. const T xc = *ptrX; ptrX+=off; \
  32759. const Tfloat yc = *(ptrY++) = (Tfloat)(a0*xc + a1*xp - b1*yp - b2*yb); \
  32760. xp = xc; yb = yp; yp = yc; \
  32761. } \
  32762. T xn = (T)0, xa = (T)0; \
  32763. Tfloat yn = 0, ya = 0; \
  32764. if (boundary_conditions) { xn = xa = *(ptrX-off); yn = ya = (Tfloat)coefn*xn; } \
  32765. for (int n = N - 1; n>=0; --n) { \
  32766. const T xc = *(ptrX-=off); \
  32767. const Tfloat yc = (Tfloat)(a2*xn + a3*xa - b1*yn - b2*ya); \
  32768. xa = xn; xn = xc; ya = yn; yn = yc; \
  32769. *ptrX = (T)(*(--ptrY)+yc); \
  32770. }
  32771. const char naxis = cimg::lowercase(axis);
  32772. const float nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100;
  32773. if (is_empty() || (nsigma<0.1f && !order)) return *this;
  32774. const float
  32775. nnsigma = nsigma<0.1f?0.1f:nsigma,
  32776. alpha = 1.695f/nnsigma,
  32777. ema = (float)std::exp(-alpha),
  32778. ema2 = (float)std::exp(-2*alpha),
  32779. b1 = -2*ema,
  32780. b2 = ema2;
  32781. float a0 = 0, a1 = 0, a2 = 0, a3 = 0, coefp = 0, coefn = 0;
  32782. switch (order) {
  32783. case 0 : {
  32784. const float k = (1-ema)*(1-ema)/(1 + 2*alpha*ema-ema2);
  32785. a0 = k;
  32786. a1 = k*(alpha - 1)*ema;
  32787. a2 = k*(alpha + 1)*ema;
  32788. a3 = -k*ema2;
  32789. } break;
  32790. case 1 : {
  32791. const float k = -(1-ema)*(1-ema)*(1-ema)/(2*(ema + 1)*ema);
  32792. a0 = a3 = 0;
  32793. a1 = k*ema;
  32794. a2 = -a1;
  32795. } break;
  32796. case 2 : {
  32797. const float
  32798. ea = (float)std::exp(-alpha),
  32799. k = -(ema2 - 1)/(2*alpha*ema),
  32800. kn = (-2*(-1 + 3*ea - 3*ea*ea + ea*ea*ea)/(3*ea + 1 + 3*ea*ea + ea*ea*ea));
  32801. a0 = kn;
  32802. a1 = -kn*(1 + k*alpha)*ema;
  32803. a2 = kn*(1 - k*alpha)*ema;
  32804. a3 = -kn*ema2;
  32805. } break;
  32806. default :
  32807. throw CImgArgumentException(_cimg_instance
  32808. "deriche(): Invalid specified filter order %u "
  32809. "(should be { 0=smoothing | 1=1st-derivative | 2=2nd-derivative }).",
  32810. cimg_instance,
  32811. order);
  32812. }
  32813. coefp = (a0 + a1)/(1 + b1 + b2);
  32814. coefn = (a2 + a3)/(1 + b1 + b2);
  32815. switch (naxis) {
  32816. case 'x' : {
  32817. const int N = width();
  32818. const ulongT off = 1U;
  32819. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
  32820. cimg_forYZC(*this,y,z,c) { T *ptrX = data(0,y,z,c); _cimg_deriche_apply; }
  32821. } break;
  32822. case 'y' : {
  32823. const int N = height();
  32824. const ulongT off = (ulongT)_width;
  32825. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
  32826. cimg_forXZC(*this,x,z,c) { T *ptrX = data(x,0,z,c); _cimg_deriche_apply; }
  32827. } break;
  32828. case 'z' : {
  32829. const int N = depth();
  32830. const ulongT off = (ulongT)_width*_height;
  32831. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
  32832. cimg_forXYC(*this,x,y,c) { T *ptrX = data(x,y,0,c); _cimg_deriche_apply; }
  32833. } break;
  32834. default : {
  32835. const int N = spectrum();
  32836. const ulongT off = (ulongT)_width*_height*_depth;
  32837. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
  32838. cimg_forXYZ(*this,x,y,z) { T *ptrX = data(x,y,z,0); _cimg_deriche_apply; }
  32839. }
  32840. }
  32841. return *this;
  32842. }
  32843. //! Apply recursive Deriche filter \newinstance.
  32844. CImg<Tfloat> get_deriche(const float sigma, const unsigned int order=0, const char axis='x',
  32845. const bool boundary_conditions=true) const {
  32846. return CImg<Tfloat>(*this,false).deriche(sigma,order,axis,boundary_conditions);
  32847. }
  32848. // [internal] Apply a recursive filter (used by CImg<T>::vanvliet()).
  32849. /*
  32850. \param ptr the pointer of the data
  32851. \param filter the coefficient of the filter in the following order [n,n - 1,n - 2,n - 3].
  32852. \param N size of the data
  32853. \param off the offset between two data point
  32854. \param order the order of the filter 0 (smoothing), 1st derivtive, 2nd derivative, 3rd derivative
  32855. \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.
  32856. \note Boundary condition using B. Triggs method (IEEE trans on Sig Proc 2005).
  32857. */
  32858. static void _cimg_recursive_apply(T *data, const double filter[], const int N, const ulongT off,
  32859. const unsigned int order, const bool boundary_conditions) {
  32860. double val[4] = { 0 }; // res[n,n - 1,n - 2,n - 3,..] or res[n,n + 1,n + 2,n + 3,..]
  32861. const double
  32862. sumsq = filter[0], sum = sumsq * sumsq,
  32863. a1 = filter[1], a2 = filter[2], a3 = filter[3],
  32864. scaleM = 1.0 / ( (1.0 + a1 - a2 + a3) * (1.0 - a1 - a2 - a3) * (1.0 + a2 + (a1 - a3) * a3) );
  32865. double M[9]; // Triggs matrix
  32866. M[0] = scaleM * (-a3 * a1 + 1.0 - a3 * a3 - a2);
  32867. M[1] = scaleM * (a3 + a1) * (a2 + a3 * a1);
  32868. M[2] = scaleM * a3 * (a1 + a3 * a2);
  32869. M[3] = scaleM * (a1 + a3 * a2);
  32870. M[4] = -scaleM * (a2 - 1.0) * (a2 + a3 * a1);
  32871. M[5] = -scaleM * a3 * (a3 * a1 + a3 * a3 + a2 - 1.0);
  32872. M[6] = scaleM * (a3 * a1 + a2 + a1 * a1 - a2 * a2);
  32873. M[7] = scaleM * (a1 * a2 + a3 * a2 * a2 - a1 * a3 * a3 - a3 * a3 * a3 - a3 * a2 + a3);
  32874. M[8] = scaleM * a3 * (a1 + a3 * a2);
  32875. switch (order) {
  32876. case 0 : {
  32877. const double iplus = (boundary_conditions?data[(N - 1)*off]:(T)0);
  32878. for (int pass = 0; pass<2; ++pass) {
  32879. if (!pass) {
  32880. for (int k = 1; k<4; ++k) val[k] = (boundary_conditions?*data/sumsq:0);
  32881. } else {
  32882. // apply Triggs boundary conditions
  32883. const double
  32884. uplus = iplus/(1.0 - a1 - a2 - a3), vplus = uplus/(1.0 - a1 - a2 - a3),
  32885. unp = val[1] - uplus, unp1 = val[2] - uplus, unp2 = val[3] - uplus;
  32886. val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2 + vplus) * sum;
  32887. val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2 + vplus) * sum;
  32888. val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2 + vplus) * sum;
  32889. *data = (T)val[0];
  32890. data -= off;
  32891. for (int k = 3; k>0; --k) val[k] = val[k - 1];
  32892. }
  32893. for (int n = pass; n<N; ++n) {
  32894. val[0] = (*data);
  32895. if (pass) val[0] *= sum;
  32896. for (int k = 1; k<4; ++k) val[0] += val[k] * filter[k];
  32897. *data = (T)val[0];
  32898. if (!pass) data += off; else data -= off;
  32899. for (int k = 3; k>0; --k) val[k] = val[k - 1];
  32900. }
  32901. if (!pass) data -= off;
  32902. }
  32903. } break;
  32904. case 1 : {
  32905. double x[3]; // [front,center,back]
  32906. for (int pass = 0; pass<2; ++pass) {
  32907. if (!pass) {
  32908. for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0);
  32909. for (int k = 0; k<4; ++k) val[k] = 0;
  32910. } else {
  32911. // apply Triggs boundary conditions
  32912. const double
  32913. unp = val[1], unp1 = val[2], unp2 = val[3];
  32914. val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum;
  32915. val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum;
  32916. val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum;
  32917. *data = (T)val[0];
  32918. data -= off;
  32919. for (int k = 3; k>0; --k) val[k] = val[k - 1];
  32920. }
  32921. for (int n = pass; n<N - 1; ++n) {
  32922. if (!pass) {
  32923. x[0] = *(data + off);
  32924. val[0] = 0.5f * (x[0] - x[2]);
  32925. } else val[0] = (*data) * sum;
  32926. for (int k = 1; k<4; ++k) val[0] += val[k] * filter[k];
  32927. *data = (T)val[0];
  32928. if (!pass) {
  32929. data += off;
  32930. for (int k = 2; k>0; --k) x[k] = x[k - 1];
  32931. } else { data-=off;}
  32932. for (int k = 3; k>0; --k) val[k] = val[k - 1];
  32933. }
  32934. *data = (T)0;
  32935. }
  32936. } break;
  32937. case 2: {
  32938. double x[3]; // [front,center,back]
  32939. for (int pass = 0; pass<2; ++pass) {
  32940. if (!pass) {
  32941. for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0);
  32942. for (int k = 0; k<4; ++k) val[k] = 0;
  32943. } else {
  32944. // apply Triggs boundary conditions
  32945. const double
  32946. unp = val[1], unp1 = val[2], unp2 = val[3];
  32947. val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum;
  32948. val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum;
  32949. val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum;
  32950. *data = (T)val[0];
  32951. data -= off;
  32952. for (int k = 3; k>0; --k) val[k] = val[k - 1];
  32953. }
  32954. for (int n = pass; n<N - 1; ++n) {
  32955. if (!pass) { x[0] = *(data + off); val[0] = (x[1] - x[2]); }
  32956. else { x[0] = *(data - off); val[0] = (x[2] - x[1]) * sum; }
  32957. for (int k = 1; k<4; ++k) val[0] += val[k]*filter[k];
  32958. *data = (T)val[0];
  32959. if (!pass) data += off; else data -= off;
  32960. for (int k = 2; k>0; --k) x[k] = x[k - 1];
  32961. for (int k = 3; k>0; --k) val[k] = val[k - 1];
  32962. }
  32963. *data = (T)0;
  32964. }
  32965. } break;
  32966. case 3: {
  32967. double x[3]; // [front,center,back]
  32968. for (int pass = 0; pass<2; ++pass) {
  32969. if (!pass) {
  32970. for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0);
  32971. for (int k = 0; k<4; ++k) val[k] = 0;
  32972. } else {
  32973. // apply Triggs boundary conditions
  32974. const double
  32975. unp = val[1], unp1 = val[2], unp2 = val[3];
  32976. val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum;
  32977. val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum;
  32978. val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum;
  32979. *data = (T)val[0];
  32980. data -= off;
  32981. for (int k = 3; k>0; --k) val[k] = val[k - 1];
  32982. }
  32983. for (int n = pass; n<N - 1; ++n) {
  32984. if (!pass) { x[0] = *(data + off); val[0] = (x[0] - 2*x[1] + x[2]); }
  32985. else { x[0] = *(data - off); val[0] = 0.5f * (x[2] - x[0]) * sum; }
  32986. for (int k = 1; k<4; ++k) val[0] += val[k] * filter[k];
  32987. *data = (T)val[0];
  32988. if (!pass) data += off; else data -= off;
  32989. for (int k = 2; k>0; --k) x[k] = x[k - 1];
  32990. for (int k = 3; k>0; --k) val[k] = val[k - 1];
  32991. }
  32992. *data = (T)0;
  32993. }
  32994. } break;
  32995. }
  32996. }
  32997. //! Van Vliet recursive Gaussian filter.
  32998. /**
  32999. \param sigma standard deviation of the Gaussian filter
  33000. \param order the order of the filter 0,1,2,3
  33001. \param axis Axis along which the filter is computed. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
  33002. \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.
  33003. \note dirichlet boundary condition has a strange behavior
  33004. I.T. Young, L.J. van Vliet, M. van Ginkel, Recursive Gabor filtering.
  33005. IEEE Trans. Sig. Proc., vol. 50, pp. 2799-2805, 2002.
  33006. (this is an improvement over Young-Van Vliet, Sig. Proc. 44, 1995)
  33007. Boundary conditions (only for order 0) using Triggs matrix, from
  33008. B. Triggs and M. Sdika. Boundary conditions for Young-van Vliet
  33009. recursive filtering. IEEE Trans. Signal Processing,
  33010. vol. 54, pp. 2365-2367, 2006.
  33011. **/
  33012. CImg<T>& vanvliet(const float sigma, const unsigned int order, const char axis='x',
  33013. const bool boundary_conditions=true) {
  33014. if (is_empty()) return *this;
  33015. if (!cimg::type<T>::is_float())
  33016. return CImg<Tfloat>(*this,false).vanvliet(sigma,order,axis,boundary_conditions).move_to(*this);
  33017. const char naxis = cimg::lowercase(axis);
  33018. const float nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100;
  33019. if (is_empty() || (nsigma<0.5f && !order)) return *this;
  33020. const double
  33021. nnsigma = nsigma<0.5f?0.5f:nsigma,
  33022. m0 = 1.16680, m1 = 1.10783, m2 = 1.40586,
  33023. m1sq = m1 * m1, m2sq = m2 * m2,
  33024. q = (nnsigma<3.556?-0.2568 + 0.5784*nnsigma + 0.0561*nnsigma*nnsigma:2.5091 + 0.9804*(nnsigma - 3.556)),
  33025. qsq = q * q,
  33026. scale = (m0 + q) * (m1sq + m2sq + 2 * m1 * q + qsq),
  33027. b1 = -q * (2 * m0 * m1 + m1sq + m2sq + (2 * m0 + 4 * m1) * q + 3 * qsq) / scale,
  33028. b2 = qsq * (m0 + 2 * m1 + 3 * q) / scale,
  33029. b3 = -qsq * q / scale,
  33030. B = ( m0 * (m1sq + m2sq) ) / scale;
  33031. double filter[4];
  33032. filter[0] = B; filter[1] = -b1; filter[2] = -b2; filter[3] = -b3;
  33033. switch (naxis) {
  33034. case 'x' : {
  33035. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
  33036. cimg_forYZC(*this,y,z,c)
  33037. _cimg_recursive_apply(data(0,y,z,c),filter,_width,1U,order,boundary_conditions);
  33038. } break;
  33039. case 'y' : {
  33040. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
  33041. cimg_forXZC(*this,x,z,c)
  33042. _cimg_recursive_apply(data(x,0,z,c),filter,_height,(ulongT)_width,order,boundary_conditions);
  33043. } break;
  33044. case 'z' : {
  33045. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
  33046. cimg_forXYC(*this,x,y,c)
  33047. _cimg_recursive_apply(data(x,y,0,c),filter,_depth,(ulongT)_width*_height,
  33048. order,boundary_conditions);
  33049. } break;
  33050. default : {
  33051. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
  33052. cimg_forXYZ(*this,x,y,z)
  33053. _cimg_recursive_apply(data(x,y,z,0),filter,_spectrum,(ulongT)_width*_height*_depth,
  33054. order,boundary_conditions);
  33055. }
  33056. }
  33057. return *this;
  33058. }
  33059. //! Blur image using Van Vliet recursive Gaussian filter. \newinstance.
  33060. CImg<Tfloat> get_vanvliet(const float sigma, const unsigned int order, const char axis='x',
  33061. const bool boundary_conditions=true) const {
  33062. return CImg<Tfloat>(*this,false).vanvliet(sigma,order,axis,boundary_conditions);
  33063. }
  33064. //! Blur image.
  33065. /**
  33066. \param sigma_x Standard deviation of the blur, along the X-axis.
  33067. \param sigma_y Standard deviation of the blur, along the Y-axis.
  33068. \param sigma_z Standard deviation of the blur, along the Z-axis.
  33069. \param boundary_conditions Boundary conditions. Can be <tt>{ false=dirichlet | true=neumann }</tt>.
  33070. \param is_gaussian Tells if the blur uses a gaussian (\c true) or quasi-gaussian (\c false) kernel.
  33071. \note
  33072. - The blur is computed as a 0-order Deriche filter. This is not a gaussian blur.
  33073. - This is a recursive algorithm, not depending on the values of the standard deviations.
  33074. \see deriche(), vanvliet().
  33075. **/
  33076. CImg<T>& blur(const float sigma_x, const float sigma_y, const float sigma_z,
  33077. const bool boundary_conditions=true, const bool is_gaussian=false) {
  33078. if (is_empty()) return *this;
  33079. if (is_gaussian) {
  33080. if (_width>1) vanvliet(sigma_x,0,'x',boundary_conditions);
  33081. if (_height>1) vanvliet(sigma_y,0,'y',boundary_conditions);
  33082. if (_depth>1) vanvliet(sigma_z,0,'z',boundary_conditions);
  33083. } else {
  33084. if (_width>1) deriche(sigma_x,0,'x',boundary_conditions);
  33085. if (_height>1) deriche(sigma_y,0,'y',boundary_conditions);
  33086. if (_depth>1) deriche(sigma_z,0,'z',boundary_conditions);
  33087. }
  33088. return *this;
  33089. }
  33090. //! Blur image \newinstance.
  33091. CImg<Tfloat> get_blur(const float sigma_x, const float sigma_y, const float sigma_z,
  33092. const bool boundary_conditions=true, const bool is_gaussian=false) const {
  33093. return CImg<Tfloat>(*this,false).blur(sigma_x,sigma_y,sigma_z,boundary_conditions,is_gaussian);
  33094. }
  33095. //! Blur image isotropically.
  33096. /**
  33097. \param sigma Standard deviation of the blur.
  33098. \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.a
  33099. \param is_gaussian Use a gaussian kernel (VanVliet) is set, a pseudo-gaussian (Deriche) otherwise.
  33100. \see deriche(), vanvliet().
  33101. **/
  33102. CImg<T>& blur(const float sigma, const bool boundary_conditions=true, const bool is_gaussian=false) {
  33103. const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100;
  33104. return blur(nsigma,nsigma,nsigma,boundary_conditions,is_gaussian);
  33105. }
  33106. //! Blur image isotropically \newinstance.
  33107. CImg<Tfloat> get_blur(const float sigma, const bool boundary_conditions=true, const bool is_gaussian=false) const {
  33108. return CImg<Tfloat>(*this,false).blur(sigma,boundary_conditions,is_gaussian);
  33109. }
  33110. //! Blur image anisotropically, directed by a field of diffusion tensors.
  33111. /**
  33112. \param G Field of square roots of diffusion tensors/vectors used to drive the smoothing.
  33113. \param amplitude Amplitude of the smoothing.
  33114. \param dl Spatial discretization.
  33115. \param da Angular discretization.
  33116. \param gauss_prec Precision of the diffusion process.
  33117. \param interpolation_type Interpolation scheme.
  33118. Can be <tt>{ 0=nearest-neighbor | 1=linear | 2=Runge-Kutta }</tt>.
  33119. \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not.
  33120. **/
  33121. template<typename t>
  33122. CImg<T>& blur_anisotropic(const CImg<t>& G,
  33123. const float amplitude=60, const float dl=0.8f, const float da=30,
  33124. const float gauss_prec=2, const unsigned int interpolation_type=0,
  33125. const bool is_fast_approx=1) {
  33126. // Check arguments and init variables
  33127. if (!is_sameXYZ(G) || (G._spectrum!=3 && G._spectrum!=6))
  33128. throw CImgArgumentException(_cimg_instance
  33129. "blur_anisotropic(): Invalid specified diffusion tensor field (%u,%u,%u,%u,%p).",
  33130. cimg_instance,
  33131. G._width,G._height,G._depth,G._spectrum,G._data);
  33132. if (is_empty() || dl<0) return *this;
  33133. const float namplitude = amplitude>=0?amplitude:-amplitude*cimg::max(_width,_height,_depth)/100;
  33134. unsigned int iamplitude = cimg::round(namplitude);
  33135. const bool is_3d = (G._spectrum==6);
  33136. T val_min, val_max = max_min(val_min);
  33137. _cimg_abort_init_omp;
  33138. cimg_abort_init;
  33139. if (da<=0) { // Iterated oriented Laplacians
  33140. CImg<Tfloat> velocity(_width,_height,_depth,_spectrum);
  33141. for (unsigned int iteration = 0; iteration<iamplitude; ++iteration) {
  33142. Tfloat *ptrd = velocity._data, veloc_max = 0;
  33143. if (is_3d) // 3d version
  33144. cimg_forC(*this,c) {
  33145. cimg_abort_test;
  33146. CImg_3x3x3(I,Tfloat);
  33147. cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
  33148. const Tfloat
  33149. ixx = Incc + Ipcc - 2*Iccc,
  33150. ixy = (Innc + Ippc - Inpc - Ipnc)/4,
  33151. ixz = (Incn + Ipcp - Incp - Ipcn)/4,
  33152. iyy = Icnc + Icpc - 2*Iccc,
  33153. iyz = (Icnn + Icpp - Icnp - Icpn)/4,
  33154. izz = Iccn + Iccp - 2*Iccc,
  33155. veloc = (Tfloat)(G(x,y,z,0)*ixx + 2*G(x,y,z,1)*ixy + 2*G(x,y,z,2)*ixz +
  33156. G(x,y,z,3)*iyy + 2*G(x,y,z,4)*iyz + G(x,y,z,5)*izz);
  33157. *(ptrd++) = veloc;
  33158. if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
  33159. }
  33160. }
  33161. else // 2d version
  33162. cimg_forZC(*this,z,c) {
  33163. cimg_abort_test;
  33164. CImg_3x3(I,Tfloat);
  33165. cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
  33166. const Tfloat
  33167. ixx = Inc + Ipc - 2*Icc,
  33168. ixy = (Inn + Ipp - Inp - Ipn)/4,
  33169. iyy = Icn + Icp - 2*Icc,
  33170. veloc = (Tfloat)(G(x,y,0,0)*ixx + 2*G(x,y,0,1)*ixy + G(x,y,0,2)*iyy);
  33171. *(ptrd++) = veloc;
  33172. if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
  33173. }
  33174. }
  33175. if (veloc_max>0) *this+=(velocity*=dl/veloc_max);
  33176. }
  33177. } else { // LIC-based smoothing.
  33178. const ulongT whd = (ulongT)_width*_height*_depth;
  33179. const float sqrt2amplitude = (float)std::sqrt(2*namplitude);
  33180. const int dx1 = width() - 1, dy1 = height() - 1, dz1 = depth() - 1;
  33181. CImg<Tfloat> res(_width,_height,_depth,_spectrum,0), W(_width,_height,_depth,is_3d?4:3), val(_spectrum,1,1,1,0);
  33182. int N = 0;
  33183. if (is_3d) { // 3d version
  33184. for (float phi = cimg::mod(180.0f,da)/2.0f; phi<=180; phi+=da) {
  33185. const float phir = (float)(phi*cimg::PI/180), datmp = (float)(da/std::cos(phir)),
  33186. da2 = datmp<1?360.0f:datmp;
  33187. for (float theta = 0; theta<360; (theta+=da2),++N) {
  33188. const float
  33189. thetar = (float)(theta*cimg::PI/180),
  33190. vx = (float)(std::cos(thetar)*std::cos(phir)),
  33191. vy = (float)(std::sin(thetar)*std::cos(phir)),
  33192. vz = (float)std::sin(phir);
  33193. const t
  33194. *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2),
  33195. *pd = G.data(0,0,0,3), *pe = G.data(0,0,0,4), *pf = G.data(0,0,0,5);
  33196. 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);
  33197. cimg_forXYZ(G,xg,yg,zg) {
  33198. const t a = *(pa++), b = *(pb++), c = *(pc++), d = *(pd++), e = *(pe++), f = *(pf++);
  33199. const float
  33200. u = (float)(a*vx + b*vy + c*vz),
  33201. v = (float)(b*vx + d*vy + e*vz),
  33202. w = (float)(c*vx + e*vy + f*vz),
  33203. n = 1e-5f + cimg::hypot(u,v,w),
  33204. dln = dl/n;
  33205. *(pd0++) = (Tfloat)(u*dln);
  33206. *(pd1++) = (Tfloat)(v*dln);
  33207. *(pd2++) = (Tfloat)(w*dln);
  33208. *(pd3++) = (Tfloat)n;
  33209. }
  33210. cimg_abort_test;
  33211. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=256 && _height*_depth>=2)
  33212. firstprivate(val))
  33213. cimg_forYZ(*this,y,z) _cimg_abort_try_omp2 {
  33214. cimg_abort_test2;
  33215. cimg_forX(*this,x) {
  33216. val.fill(0);
  33217. const float
  33218. n = (float)W(x,y,z,3),
  33219. fsigma = (float)(n*sqrt2amplitude),
  33220. fsigma2 = 2*fsigma*fsigma,
  33221. length = gauss_prec*fsigma;
  33222. float
  33223. S = 0,
  33224. X = (float)x,
  33225. Y = (float)y,
  33226. Z = (float)z;
  33227. switch (interpolation_type) {
  33228. case 0 : { // Nearest neighbor
  33229. for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
  33230. const int
  33231. cx = (int)(X + 0.5f),
  33232. cy = (int)(Y + 0.5f),
  33233. cz = (int)(Z + 0.5f);
  33234. const float
  33235. u = (float)W(cx,cy,cz,0),
  33236. v = (float)W(cx,cy,cz,1),
  33237. w = (float)W(cx,cy,cz,2);
  33238. if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,cz,c); ++S; }
  33239. else {
  33240. const float coef = (float)std::exp(-l*l/fsigma2);
  33241. cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,cz,c));
  33242. S+=coef;
  33243. }
  33244. X+=u; Y+=v; Z+=w;
  33245. }
  33246. } break;
  33247. case 1 : { // Linear interpolation
  33248. for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
  33249. const float
  33250. u = (float)(W._linear_atXYZ(X,Y,Z,0)),
  33251. v = (float)(W._linear_atXYZ(X,Y,Z,1)),
  33252. w = (float)(W._linear_atXYZ(X,Y,Z,2));
  33253. if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; }
  33254. else {
  33255. const float coef = (float)std::exp(-l*l/fsigma2);
  33256. cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c));
  33257. S+=coef;
  33258. }
  33259. X+=u; Y+=v; Z+=w;
  33260. }
  33261. } break;
  33262. default : { // 2nd order Runge Kutta
  33263. for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
  33264. const float
  33265. u0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,0)),
  33266. v0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,1)),
  33267. w0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,2)),
  33268. u = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,0)),
  33269. v = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,1)),
  33270. w = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,2));
  33271. if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; }
  33272. else {
  33273. const float coef = (float)std::exp(-l*l/fsigma2);
  33274. cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c));
  33275. S+=coef;
  33276. }
  33277. X+=u; Y+=v; Z+=w;
  33278. }
  33279. } break;
  33280. }
  33281. Tfloat *ptrd = res.data(x,y,z);
  33282. if (S>0) cimg_forC(res,c) { *ptrd+=val[c]/S; ptrd+=whd; }
  33283. else cimg_forC(res,c) { *ptrd+=(Tfloat)((*this)(x,y,z,c)); ptrd+=whd; }
  33284. }
  33285. } _cimg_abort_catch_omp2
  33286. }
  33287. }
  33288. } else { // 2d LIC algorithm
  33289. for (float theta = cimg::mod(360.0f,da)/2.0f; theta<360; (theta+=da),++N) {
  33290. const float thetar = (float)(theta*cimg::PI/180),
  33291. vx = (float)(std::cos(thetar)), vy = (float)(std::sin(thetar));
  33292. const t *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2);
  33293. Tfloat *pd0 = W.data(0,0,0,0), *pd1 = W.data(0,0,0,1), *pd2 = W.data(0,0,0,2);
  33294. cimg_forXY(G,xg,yg) {
  33295. const t a = *(pa++), b = *(pb++), c = *(pc++);
  33296. const float
  33297. u = (float)(a*vx + b*vy),
  33298. v = (float)(b*vx + c*vy),
  33299. n = std::max(1e-5f,cimg::hypot(u,v)),
  33300. dln = dl/n;
  33301. *(pd0++) = (Tfloat)(u*dln);
  33302. *(pd1++) = (Tfloat)(v*dln);
  33303. *(pd2++) = (Tfloat)n;
  33304. }
  33305. cimg_abort_test;
  33306. cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256 && _height>=2) firstprivate(val))
  33307. cimg_forY(*this,y) _cimg_abort_try_omp2 {
  33308. cimg_abort_test2;
  33309. cimg_forX(*this,x) {
  33310. val.fill(0);
  33311. const float
  33312. n = (float)W(x,y,0,2),
  33313. fsigma = (float)(n*sqrt2amplitude),
  33314. fsigma2 = 2*fsigma*fsigma,
  33315. length = gauss_prec*fsigma;
  33316. float
  33317. S = 0,
  33318. X = (float)x,
  33319. Y = (float)y;
  33320. switch (interpolation_type) {
  33321. case 0 : { // Nearest-neighbor
  33322. for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
  33323. const int
  33324. cx = (int)(X + 0.5f),
  33325. cy = (int)(Y + 0.5f);
  33326. const float
  33327. u = (float)W(cx,cy,0,0),
  33328. v = (float)W(cx,cy,0,1);
  33329. if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,0,c); ++S; }
  33330. else {
  33331. const float coef = (float)std::exp(-l*l/fsigma2);
  33332. cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,0,c));
  33333. S+=coef;
  33334. }
  33335. X+=u; Y+=v;
  33336. }
  33337. } break;
  33338. case 1 : { // Linear interpolation
  33339. for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
  33340. const float
  33341. u = (float)(W._linear_atXY(X,Y,0,0)),
  33342. v = (float)(W._linear_atXY(X,Y,0,1));
  33343. if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; }
  33344. else {
  33345. const float coef = (float)std::exp(-l*l/fsigma2);
  33346. cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c));
  33347. S+=coef;
  33348. }
  33349. X+=u; Y+=v;
  33350. }
  33351. } break;
  33352. default : { // 2nd-order Runge-kutta interpolation
  33353. for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
  33354. const float
  33355. u0 = (float)(0.5f*W._linear_atXY(X,Y,0,0)),
  33356. v0 = (float)(0.5f*W._linear_atXY(X,Y,0,1)),
  33357. u = (float)(W._linear_atXY(X + u0,Y + v0,0,0)),
  33358. v = (float)(W._linear_atXY(X + u0,Y + v0,0,1));
  33359. if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; }
  33360. else {
  33361. const float coef = (float)std::exp(-l*l/fsigma2);
  33362. cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c));
  33363. S+=coef;
  33364. }
  33365. X+=u; Y+=v;
  33366. }
  33367. }
  33368. }
  33369. Tfloat *ptrd = res.data(x,y);
  33370. if (S>0) cimg_forC(res,c) { *ptrd+=val[c]/S; ptrd+=whd; }
  33371. else cimg_forC(res,c) { *ptrd+=(Tfloat)((*this)(x,y,0,c)); ptrd+=whd; }
  33372. }
  33373. } _cimg_abort_catch_omp2
  33374. }
  33375. }
  33376. const Tfloat *ptrs = res._data;
  33377. cimg_for(*this,ptrd,T) {
  33378. const Tfloat val = *(ptrs++)/N;
  33379. *ptrd = val<val_min?val_min:(val>val_max?val_max:(T)val);
  33380. }
  33381. }
  33382. cimg_abort_test;
  33383. return *this;
  33384. }
  33385. //! Blur image anisotropically, directed by a field of diffusion tensors \newinstance.
  33386. template<typename t>
  33387. CImg<Tfloat> get_blur_anisotropic(const CImg<t>& G,
  33388. const float amplitude=60, const float dl=0.8f, const float da=30,
  33389. const float gauss_prec=2, const unsigned int interpolation_type=0,
  33390. const bool is_fast_approx=true) const {
  33391. return CImg<Tfloat>(*this,false).blur_anisotropic(G,amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx);
  33392. }
  33393. //! Blur image anisotropically, in an edge-preserving way.
  33394. /**
  33395. \param amplitude Amplitude of the smoothing.
  33396. \param sharpness Sharpness.
  33397. \param anisotropy Anisotropy.
  33398. \param alpha Standard deviation of the gradient blur.
  33399. \param sigma Standard deviation of the structure tensor blur.
  33400. \param dl Spatial discretization.
  33401. \param da Angular discretization.
  33402. \param gauss_prec Precision of the diffusion process.
  33403. \param interpolation_type Interpolation scheme.
  33404. Can be <tt>{ 0=nearest-neighbor | 1=linear | 2=Runge-Kutta }</tt>.
  33405. \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not.
  33406. **/
  33407. CImg<T>& blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f,
  33408. const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, const float da=30,
  33409. const float gauss_prec=2, const unsigned int interpolation_type=0,
  33410. const bool is_fast_approx=true) {
  33411. const float nalpha = alpha>=0?alpha:-alpha*cimg::max(_width,_height,_depth)/100;
  33412. const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100;
  33413. return blur_anisotropic(get_diffusion_tensors(sharpness,anisotropy,nalpha,nsigma,interpolation_type!=3),
  33414. amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx);
  33415. }
  33416. //! Blur image anisotropically, in an edge-preserving way \newinstance.
  33417. CImg<Tfloat> get_blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f,
  33418. const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f,
  33419. const float da=30, const float gauss_prec=2,
  33420. const unsigned int interpolation_type=0,
  33421. const bool is_fast_approx=true) const {
  33422. return CImg<Tfloat>(*this,false).blur_anisotropic(amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec,
  33423. interpolation_type,is_fast_approx);
  33424. }
  33425. //! Blur image, with the joint bilateral filter.
  33426. /**
  33427. \param guide Image used to model the smoothing weights.
  33428. \param sigma_x Amount of blur along the X-axis.
  33429. \param sigma_y Amount of blur along the Y-axis.
  33430. \param sigma_z Amount of blur along the Z-axis.
  33431. \param sigma_r Amount of blur along the value axis.
  33432. \param sampling_x Amount of downsampling along the X-axis used for the approximation.
  33433. Defaults (0) to sigma_x.
  33434. \param sampling_y Amount of downsampling along the Y-axis used for the approximation.
  33435. Defaults (0) to sigma_y.
  33436. \param sampling_z Amount of downsampling along the Z-axis used for the approximation.
  33437. Defaults (0) to sigma_z.
  33438. \param sampling_r Amount of downsampling along the value axis used for the approximation.
  33439. Defaults (0) to sigma_r.
  33440. \note This algorithm uses the optimisation technique proposed by S. Paris and F. Durand, in ECCV'2006
  33441. (extended for 3d volumetric images).
  33442. It is based on the reference implementation http://people.csail.mit.edu/jiawen/software/bilateralFilter.m
  33443. **/
  33444. template<typename t>
  33445. CImg<T>& blur_bilateral(const CImg<t>& guide,
  33446. const float sigma_x, const float sigma_y,
  33447. const float sigma_z, const float sigma_r,
  33448. const float sampling_x, const float sampling_y,
  33449. const float sampling_z, const float sampling_r) {
  33450. if (!is_sameXYZ(guide))
  33451. throw CImgArgumentException(_cimg_instance
  33452. "blur_bilateral(): Invalid size for specified guide image (%u,%u,%u,%u,%p).",
  33453. cimg_instance,
  33454. guide._width,guide._height,guide._depth,guide._spectrum,guide._data);
  33455. if (is_empty() || (!sigma_x && !sigma_y && !sigma_z)) return *this;
  33456. T edge_min, edge_max = guide.max_min(edge_min);
  33457. if (edge_min==edge_max) return blur(sigma_x,sigma_y,sigma_z);
  33458. const float
  33459. edge_delta = (float)(edge_max - edge_min),
  33460. _sigma_x = sigma_x>=0?sigma_x:-sigma_x*_width/100,
  33461. _sigma_y = sigma_y>=0?sigma_y:-sigma_y*_height/100,
  33462. _sigma_z = sigma_z>=0?sigma_z:-sigma_z*_depth/100,
  33463. _sigma_r = sigma_r>=0?sigma_r:-sigma_r*(edge_max - edge_min)/100,
  33464. _sampling_x = sampling_x?sampling_x:std::max(_sigma_x,1.0f),
  33465. _sampling_y = sampling_y?sampling_y:std::max(_sigma_y,1.0f),
  33466. _sampling_z = sampling_z?sampling_z:std::max(_sigma_z,1.0f),
  33467. _sampling_r = sampling_r?sampling_r:std::max(_sigma_r,edge_delta/256),
  33468. derived_sigma_x = _sigma_x / _sampling_x,
  33469. derived_sigma_y = _sigma_y / _sampling_y,
  33470. derived_sigma_z = _sigma_z / _sampling_z,
  33471. derived_sigma_r = _sigma_r / _sampling_r;
  33472. const int
  33473. padding_x = (int)(2*derived_sigma_x) + 1,
  33474. padding_y = (int)(2*derived_sigma_y) + 1,
  33475. padding_z = (int)(2*derived_sigma_z) + 1,
  33476. padding_r = (int)(2*derived_sigma_r) + 1;
  33477. const unsigned int
  33478. bx = (unsigned int)((_width - 1)/_sampling_x + 1 + 2*padding_x),
  33479. by = (unsigned int)((_height - 1)/_sampling_y + 1 + 2*padding_y),
  33480. bz = (unsigned int)((_depth - 1)/_sampling_z + 1 + 2*padding_z),
  33481. br = (unsigned int)(edge_delta/_sampling_r + 1 + 2*padding_r);
  33482. if (bx>0 || by>0 || bz>0 || br>0) {
  33483. const bool is_3d = (_depth>1);
  33484. if (is_3d) { // 3d version of the algorithm
  33485. CImg<floatT> bgrid(bx,by,bz,br), bgridw(bx,by,bz,br);
  33486. cimg_forC(*this,c) {
  33487. const CImg<t> _guide = guide.get_shared_channel(c%guide._spectrum);
  33488. bgrid.fill(0); bgridw.fill(0);
  33489. cimg_forXYZ(*this,x,y,z) {
  33490. const T val = (*this)(x,y,z,c);
  33491. const float edge = (float)_guide(x,y,z);
  33492. const int
  33493. X = (int)cimg::round(x/_sampling_x) + padding_x,
  33494. Y = (int)cimg::round(y/_sampling_y) + padding_y,
  33495. Z = (int)cimg::round(z/_sampling_z) + padding_z,
  33496. R = (int)cimg::round((edge - edge_min)/_sampling_r) + padding_r;
  33497. bgrid(X,Y,Z,R)+=(float)val;
  33498. bgridw(X,Y,Z,R)+=1;
  33499. }
  33500. bgrid.blur(derived_sigma_x,derived_sigma_y,derived_sigma_z,true).deriche(derived_sigma_r,0,'c',false);
  33501. bgridw.blur(derived_sigma_x,derived_sigma_y,derived_sigma_z,true).deriche(derived_sigma_r,0,'c',false);
  33502. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(size()>=4096))
  33503. cimg_forXYZ(*this,x,y,z) {
  33504. const float edge = (float)_guide(x,y,z);
  33505. const float
  33506. X = x/_sampling_x + padding_x,
  33507. Y = y/_sampling_y + padding_y,
  33508. Z = z/_sampling_z + padding_z,
  33509. R = (edge - edge_min)/_sampling_r + padding_r;
  33510. const float bval0 = bgrid._linear_atXYZC(X,Y,Z,R), bval1 = bgridw._linear_atXYZC(X,Y,Z,R);
  33511. (*this)(x,y,z,c) = (T)(bval0/bval1);
  33512. }
  33513. }
  33514. } else { // 2d version of the algorithm
  33515. CImg<floatT> bgrid(bx,by,br,2);
  33516. cimg_forC(*this,c) {
  33517. const CImg<t> _guide = guide.get_shared_channel(c%guide._spectrum);
  33518. bgrid.fill(0);
  33519. cimg_forXY(*this,x,y) {
  33520. const T val = (*this)(x,y,c);
  33521. const float edge = (float)_guide(x,y);
  33522. const int
  33523. X = (int)cimg::round(x/_sampling_x) + padding_x,
  33524. Y = (int)cimg::round(y/_sampling_y) + padding_y,
  33525. R = (int)cimg::round((edge - edge_min)/_sampling_r) + padding_r;
  33526. bgrid(X,Y,R,0)+=(float)val;
  33527. bgrid(X,Y,R,1)+=1;
  33528. }
  33529. bgrid.blur(derived_sigma_x,derived_sigma_y,0,true).blur(0,0,derived_sigma_r,false);
  33530. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(size()>=4096))
  33531. cimg_forXY(*this,x,y) {
  33532. const float edge = (float)_guide(x,y);
  33533. const float
  33534. X = x/_sampling_x + padding_x,
  33535. Y = y/_sampling_y + padding_y,
  33536. R = (edge - edge_min)/_sampling_r + padding_r;
  33537. const float bval0 = bgrid._linear_atXYZ(X,Y,R,0), bval1 = bgrid._linear_atXYZ(X,Y,R,1);
  33538. (*this)(x,y,c) = (T)(bval0/bval1);
  33539. }
  33540. }
  33541. }
  33542. }
  33543. return *this;
  33544. }
  33545. //! Blur image, with the joint bilateral filter \newinstance.
  33546. template<typename t>
  33547. CImg<Tfloat> get_blur_bilateral(const CImg<t>& guide,
  33548. const float sigma_x, const float sigma_y,
  33549. const float sigma_z, const float sigma_r,
  33550. const float sampling_x, const float sampling_y,
  33551. const float sampling_z, const float sampling_r) const {
  33552. return CImg<Tfloat>(*this,false).blur_bilateral(guide,sigma_x,sigma_y,sigma_z,sigma_r,
  33553. sampling_x,sampling_y,sampling_z,sampling_r);
  33554. }
  33555. //! Blur image using the joint bilateral filter.
  33556. /**
  33557. \param guide Image used to model the smoothing weights.
  33558. \param sigma_s Amount of blur along the XYZ-axes.
  33559. \param sigma_r Amount of blur along the value axis.
  33560. \param sampling_s Amount of downsampling along the XYZ-axes used for the approximation. Defaults to sigma_s.
  33561. \param sampling_r Amount of downsampling along the value axis used for the approximation. Defaults to sigma_r.
  33562. **/
  33563. template<typename t>
  33564. CImg<T>& blur_bilateral(const CImg<t>& guide,
  33565. const float sigma_s, const float sigma_r,
  33566. const float sampling_s=0, const float sampling_r=0) {
  33567. const float _sigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100;
  33568. return blur_bilateral(guide,_sigma_s,_sigma_s,_sigma_s,sigma_r,sampling_s,sampling_s,sampling_s,sampling_r);
  33569. }
  33570. //! Blur image using the bilateral filter \newinstance.
  33571. template<typename t>
  33572. CImg<Tfloat> get_blur_bilateral(const CImg<t>& guide,
  33573. const float sigma_s, const float sigma_r,
  33574. const float sampling_s=0, const float sampling_r=0) const {
  33575. return CImg<Tfloat>(*this,false).blur_bilateral(guide,sigma_s,sigma_r,sampling_s,sampling_r);
  33576. }
  33577. // [internal] Apply a box filter (used by CImg<T>::boxfilter() and CImg<T>::blur_box()).
  33578. /*
  33579. \param ptr the pointer of the data
  33580. \param N size of the data
  33581. \param boxsize Size of the box filter (can be subpixel).
  33582. \param off the offset between two data point
  33583. \param order the order of the filter 0 (smoothing), 1st derivtive and 2nd derivative.
  33584. \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.
  33585. */
  33586. static void _cimg_blur_box_apply(T *ptr, const float boxsize, const int N, const ulongT off,
  33587. const int order, const bool boundary_conditions,
  33588. const unsigned int nb_iter) {
  33589. // Smooth.
  33590. if (boxsize>1 && nb_iter) {
  33591. const int w2 = (int)(boxsize - 1)/2;
  33592. const unsigned int winsize = 2*w2 + 1U;
  33593. const double frac = (boxsize - winsize)/2.;
  33594. CImg<T> win(winsize);
  33595. for (unsigned int iter = 0; iter<nb_iter; ++iter) {
  33596. Tdouble sum = 0; // window sum
  33597. for (int x = -w2; x<=w2; ++x) {
  33598. win[x + w2] = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,x);
  33599. sum+=win[x + w2];
  33600. }
  33601. int ifirst = 0, ilast = 2*w2;
  33602. T
  33603. prev = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,-w2 - 1),
  33604. next = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,w2 + 1);
  33605. for (int x = 0; x < N - 1; ++x) {
  33606. const double sum2 = sum + frac * (prev + next);
  33607. ptr[x*off] = (T)(sum2/boxsize);
  33608. prev = win[ifirst];
  33609. sum-=prev;
  33610. ifirst = (int)((ifirst + 1)%winsize);
  33611. ilast = (int)((ilast + 1)%winsize);
  33612. win[ilast] = next;
  33613. sum+=next;
  33614. next = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,x + w2 + 2);
  33615. }
  33616. const double sum2 = sum + frac * (prev + next);
  33617. ptr[(N - 1)*off] = (T)(sum2/boxsize);
  33618. }
  33619. }
  33620. // Derive.
  33621. switch (order) {
  33622. case 0 :
  33623. break;
  33624. case 1 : {
  33625. Tfloat
  33626. p = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,-1),
  33627. c = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,0),
  33628. n = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,1);
  33629. for (int x = 0; x<N - 1; ++x) {
  33630. ptr[x*off] = (T)((n-p)/2.0);
  33631. p = c;
  33632. c = n;
  33633. n = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,x + 2);
  33634. }
  33635. ptr[(N - 1)*off] = (T)((n-p)/2.0);
  33636. } break;
  33637. case 2: {
  33638. Tfloat
  33639. p = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,-1),
  33640. c = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,0),
  33641. n = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,1);
  33642. for (int x = 0; x<N - 1; ++x) {
  33643. ptr[x*off] = (T)(n - 2*c + p);
  33644. p = c;
  33645. c = n;
  33646. n = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,x + 2);
  33647. }
  33648. ptr[(N - 1)*off] = (T)(n - 2*c + p);
  33649. } break;
  33650. }
  33651. }
  33652. static T __cimg_blur_box_apply(T *ptr, const int N, const ulongT off,
  33653. const bool boundary_conditions, const int x) {
  33654. if (x<0) return boundary_conditions?ptr[0]:T();
  33655. if (x>=N) return boundary_conditions?ptr[(N - 1)*off]:T();
  33656. return ptr[x*off];
  33657. }
  33658. // Apply box filter of order 0,1,2.
  33659. /**
  33660. \param boxsize Size of the box window (can be subpixel)
  33661. \param order the order of the filter 0,1 or 2.
  33662. \param axis Axis along which the filter is computed. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
  33663. \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.
  33664. \param nb_iter Number of filter iterations.
  33665. **/
  33666. CImg<T>& boxfilter(const float boxsize, const int order, const char axis='x',
  33667. const bool boundary_conditions=true,
  33668. const unsigned int nb_iter=1) {
  33669. if (is_empty() || !boxsize || (boxsize<=1 && !order)) return *this;
  33670. const char naxis = cimg::lowercase(axis);
  33671. const float nboxsize = boxsize>=0?boxsize:-boxsize*
  33672. (naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100;
  33673. switch (naxis) {
  33674. case 'x' : {
  33675. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
  33676. cimg_forYZC(*this,y,z,c)
  33677. _cimg_blur_box_apply(data(0,y,z,c),nboxsize,_width,1U,order,boundary_conditions,nb_iter);
  33678. } break;
  33679. case 'y' : {
  33680. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
  33681. cimg_forXZC(*this,x,z,c)
  33682. _cimg_blur_box_apply(data(x,0,z,c),nboxsize,_height,(ulongT)_width,order,boundary_conditions,nb_iter);
  33683. } break;
  33684. case 'z' : {
  33685. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
  33686. cimg_forXYC(*this,x,y,c)
  33687. _cimg_blur_box_apply(data(x,y,0,c),nboxsize,_depth,(ulongT)_width*_height,order,boundary_conditions,nb_iter);
  33688. } break;
  33689. default : {
  33690. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
  33691. cimg_forXYZ(*this,x,y,z)
  33692. _cimg_blur_box_apply(data(x,y,z,0),nboxsize,_spectrum,(ulongT)_width*_height*_depth,
  33693. order,boundary_conditions,nb_iter);
  33694. }
  33695. }
  33696. return *this;
  33697. }
  33698. // Apply box filter of order 0,1 or 2 \newinstance.
  33699. CImg<Tfloat> get_boxfilter(const float boxsize, const int order, const char axis='x',
  33700. const bool boundary_conditions=true,
  33701. const unsigned int nb_iter=1) const {
  33702. return CImg<Tfloat>(*this,false).boxfilter(boxsize,order,axis,boundary_conditions,nb_iter);
  33703. }
  33704. //! Blur image with a box filter.
  33705. /**
  33706. \param boxsize_x Size of the box window, along the X-axis (can be subpixel).
  33707. \param boxsize_y Size of the box window, along the Y-axis (can be subpixel).
  33708. \param boxsize_z Size of the box window, along the Z-axis (can be subpixel).
  33709. \param boundary_conditions Boundary conditions. Can be <tt>{ false=dirichlet | true=neumann }</tt>.
  33710. \param nb_iter Number of filter iterations.
  33711. \note
  33712. - This is a recursive algorithm, not depending on the values of the box kernel size.
  33713. \see blur().
  33714. **/
  33715. CImg<T>& blur_box(const float boxsize_x, const float boxsize_y, const float boxsize_z,
  33716. const bool boundary_conditions=true,
  33717. const unsigned int nb_iter=1) {
  33718. if (is_empty()) return *this;
  33719. if (_width>1) boxfilter(boxsize_x,0,'x',boundary_conditions,nb_iter);
  33720. if (_height>1) boxfilter(boxsize_y,0,'y',boundary_conditions,nb_iter);
  33721. if (_depth>1) boxfilter(boxsize_z,0,'z',boundary_conditions,nb_iter);
  33722. return *this;
  33723. }
  33724. //! Blur image with a box filter \newinstance.
  33725. CImg<Tfloat> get_blur_box(const float boxsize_x, const float boxsize_y, const float boxsize_z,
  33726. const bool boundary_conditions=true) const {
  33727. return CImg<Tfloat>(*this,false).blur_box(boxsize_x,boxsize_y,boxsize_z,boundary_conditions);
  33728. }
  33729. //! Blur image with a box filter.
  33730. /**
  33731. \param boxsize Size of the box window (can be subpixel).
  33732. \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.a
  33733. \see deriche(), vanvliet().
  33734. **/
  33735. CImg<T>& blur_box(const float boxsize, const bool boundary_conditions=true) {
  33736. const float nboxsize = boxsize>=0?boxsize:-boxsize*cimg::max(_width,_height,_depth)/100;
  33737. return blur_box(nboxsize,nboxsize,nboxsize,boundary_conditions);
  33738. }
  33739. //! Blur image with a box filter \newinstance.
  33740. CImg<Tfloat> get_blur_box(const float boxsize, const bool boundary_conditions=true) const {
  33741. return CImg<Tfloat>(*this,false).blur_box(boxsize,boundary_conditions);
  33742. }
  33743. //! Blur image, with the image guided filter.
  33744. /**
  33745. \param guide Image used to guide the smoothing process.
  33746. \param radius Spatial radius. If negative, it is expressed as a percentage of the largest image size.
  33747. \param regularization Regularization parameter.
  33748. If negative, it is expressed as a percentage of the guide value range.
  33749. \note This method implements the filtering algorithm described in:
  33750. He, Kaiming; Sun, Jian; Tang, Xiaoou, "Guided Image Filtering," Pattern Analysis and Machine Intelligence,
  33751. IEEE Transactions on , vol.35, no.6, pp.1397,1409, June 2013
  33752. **/
  33753. template<typename t>
  33754. CImg<T>& blur_guided(const CImg<t>& guide, const float radius, const float regularization) {
  33755. return get_blur_guided(guide,radius,regularization).move_to(*this);
  33756. }
  33757. //! Blur image, with the image guided filter \newinstance.
  33758. template<typename t>
  33759. CImg<Tfloat> get_blur_guided(const CImg<t>& guide, const float radius, const float regularization) const {
  33760. if (!is_sameXYZ(guide))
  33761. throw CImgArgumentException(_cimg_instance
  33762. "blur_guided(): Invalid size for specified guide image (%u,%u,%u,%u,%p).",
  33763. cimg_instance,
  33764. guide._width,guide._height,guide._depth,guide._spectrum,guide._data);
  33765. if (is_empty() || !radius) return *this;
  33766. const int _radius = radius>=0?(int)radius:(int)(-radius*cimg::max(_width,_height,_depth)/100);
  33767. float _regularization = regularization;
  33768. if (regularization<0) {
  33769. T edge_min, edge_max = guide.max_min(edge_min);
  33770. if (edge_min==edge_max) return *this;
  33771. _regularization = -regularization*(edge_max - edge_min)/100;
  33772. }
  33773. _regularization = std::max(_regularization,0.01f);
  33774. const unsigned int psize = (unsigned int)(1 + 2*_radius);
  33775. CImg<Tfloat>
  33776. mean_I = guide.get_blur_box(psize,true),
  33777. mean_p = get_blur_box(psize,true),
  33778. cov_Ip = (guide.get_mul(*this)).blur_box(psize,true)-=mean_I.get_mul(mean_p),
  33779. var_I = guide.get_sqr().blur_box(psize,true)-=mean_I.get_sqr(),
  33780. &a = cov_Ip.div(var_I+=_regularization),
  33781. &b = mean_p-=a.get_mul(mean_I);
  33782. a.blur_box(psize,true);
  33783. b.blur_box(psize,true);
  33784. return a.mul(guide)+=b;
  33785. }
  33786. //! Blur image using patch-based space.
  33787. /**
  33788. \param sigma_s Amount of blur along the XYZ-axes.
  33789. \param sigma_p Amount of blur along the value axis.
  33790. \param patch_size Size of the patchs.
  33791. \param lookup_size Size of the window to search similar patchs.
  33792. \param smoothness Smoothness for the patch comparison.
  33793. \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not.
  33794. **/
  33795. CImg<T>& blur_patch(const float sigma_s, const float sigma_p, const unsigned int patch_size=3,
  33796. const unsigned int lookup_size=4, const float smoothness=0, const bool is_fast_approx=true) {
  33797. if (is_empty() || !patch_size || !lookup_size) return *this;
  33798. return get_blur_patch(sigma_s,sigma_p,patch_size,lookup_size,smoothness,is_fast_approx).move_to(*this);
  33799. }
  33800. //! Blur image using patch-based space \newinstance.
  33801. CImg<Tfloat> get_blur_patch(const float sigma_s, const float sigma_p, const unsigned int patch_size=3,
  33802. const unsigned int lookup_size=4, const float smoothness=0,
  33803. const bool is_fast_approx=true) const {
  33804. #define _cimg_blur_patch3d_fast(N) \
  33805. cimg_for##N##XYZ(res,x,y,z) { \
  33806. T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,x,y,z,c,pP,T); pP+=N3; } \
  33807. const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, \
  33808. x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \
  33809. float sum_weights = 0; \
  33810. cimg_for_in##N##XYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) \
  33811. if (cimg::abs((Tfloat)img(x,y,z,0) - (Tfloat)img(p,q,r,0))<sigma_p3) { \
  33812. T *pQ = Q._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,p,q,r,c,pQ,T); pQ+=N3; } \
  33813. float distance2 = 0; \
  33814. pQ = Q._data; cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(pQ++); distance2+=dI*dI; } \
  33815. distance2/=Pnorm; \
  33816. const float dx = (float)p - x, dy = (float)q - y, dz = (float)r - z, \
  33817. alldist = distance2 + (dx*dx + dy*dy + dz*dz)/sigma_s2, weight = alldist>3?0.0f:1.0f; \
  33818. sum_weights+=weight; \
  33819. cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c); \
  33820. } \
  33821. if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights; \
  33822. else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \
  33823. }
  33824. #define _cimg_blur_patch3d(N) \
  33825. cimg_for##N##XYZ(res,x,y,z) { \
  33826. T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,x,y,z,c,pP,T); pP+=N3; } \
  33827. const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, \
  33828. x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \
  33829. float sum_weights = 0, weight_max = 0; \
  33830. cimg_for_in##N##XYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (p!=x || q!=y || r!=z) { \
  33831. T *pQ = Q._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,p,q,r,c,pQ,T); pQ+=N3; } \
  33832. float distance2 = 0; \
  33833. pQ = Q._data; cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(pQ++); distance2+=dI*dI; } \
  33834. distance2/=Pnorm; \
  33835. const float dx = (float)p - x, dy = (float)q - y, dz = (float)r - z, \
  33836. alldist = distance2 + (dx*dx + dy*dy + dz*dz)/sigma_s2, weight = (float)std::exp(-alldist); \
  33837. if (weight>weight_max) weight_max = weight; \
  33838. sum_weights+=weight; \
  33839. cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c); \
  33840. } \
  33841. sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=weight_max*(*this)(x,y,z,c); \
  33842. if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights; \
  33843. else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \
  33844. }
  33845. #define _cimg_blur_patch2d_fast(N) \
  33846. cimg_for##N##XY(res,x,y) { \
  33847. T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N(img,x,y,0,c,pP,T); pP+=N2; } \
  33848. const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \
  33849. float sum_weights = 0; \
  33850. cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) \
  33851. if (cimg::abs((Tfloat)img(x,y,0,0) - (Tfloat)img(p,q,0,0))<sigma_p3) { \
  33852. T *pQ = Q._data; cimg_forC(res,c) { cimg_get##N##x##N(img,p,q,0,c,pQ,T); pQ+=N2; } \
  33853. float distance2 = 0; \
  33854. pQ = Q._data; cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(pQ++); distance2+=dI*dI; } \
  33855. distance2/=Pnorm; \
  33856. const float dx = (float)p - x, dy = (float)q - y, \
  33857. alldist = distance2 + (dx*dx+dy*dy)/sigma_s2, weight = alldist>3?0.0f:1.0f; \
  33858. sum_weights+=weight; \
  33859. cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c); \
  33860. } \
  33861. if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights; \
  33862. else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \
  33863. }
  33864. #define _cimg_blur_patch2d(N) \
  33865. cimg_for##N##XY(res,x,y) { \
  33866. T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N(img,x,y,0,c,pP,T); pP+=N2; } \
  33867. const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \
  33868. float sum_weights = 0, weight_max = 0; \
  33869. cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) if (p!=x || q!=y) { \
  33870. T *pQ = Q._data; cimg_forC(res,c) { cimg_get##N##x##N(img,p,q,0,c,pQ,T); pQ+=N2; } \
  33871. float distance2 = 0; \
  33872. pQ = Q._data; cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(pQ++); distance2+=dI*dI; } \
  33873. distance2/=Pnorm; \
  33874. const float dx = (float)p - x, dy = (float)q - y, \
  33875. alldist = distance2 + (dx*dx+dy*dy)/sigma_s2, weight = (float)std::exp(-alldist); \
  33876. if (weight>weight_max) weight_max = weight; \
  33877. sum_weights+=weight; \
  33878. cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c); \
  33879. } \
  33880. sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=weight_max*(*this)(x,y,c); \
  33881. if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights; \
  33882. else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \
  33883. }
  33884. if (is_empty() || !patch_size || !lookup_size) return +*this;
  33885. CImg<Tfloat> res(_width,_height,_depth,_spectrum,0);
  33886. const CImg<T> _img = smoothness>0?get_blur(smoothness):CImg<Tfloat>(),&img = smoothness>0?_img:*this;
  33887. CImg<T> P(patch_size*patch_size*_spectrum), Q(P);
  33888. const float
  33889. nsigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100,
  33890. sigma_s2 = nsigma_s*nsigma_s, sigma_p2 = sigma_p*sigma_p, sigma_p3 = 3*sigma_p,
  33891. Pnorm = P.size()*sigma_p2;
  33892. const int rsize2 = (int)lookup_size/2, rsize1 = (int)lookup_size - rsize2 - 1;
  33893. const unsigned int N2 = patch_size*patch_size, N3 = N2*patch_size;
  33894. cimg::unused(N2,N3);
  33895. if (_depth>1) switch (patch_size) { // 3d
  33896. case 2 : if (is_fast_approx) _cimg_blur_patch3d_fast(2) else _cimg_blur_patch3d(2) break;
  33897. case 3 : if (is_fast_approx) _cimg_blur_patch3d_fast(3) else _cimg_blur_patch3d(3) break;
  33898. default : {
  33899. const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1;
  33900. if (is_fast_approx)
  33901. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res._width>=32 && res._height*res._depth>=4)
  33902. private(P,Q))
  33903. cimg_forXYZ(res,x,y,z) { // Fast
  33904. P = img.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,true);
  33905. const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1,
  33906. x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2;
  33907. float sum_weights = 0;
  33908. cimg_for_inXYZ(res,x0,y0,z0,x1,y1,z1,p,q,r)
  33909. if (cimg::abs((Tfloat)img(x,y,z,0) - (Tfloat)img(p,q,r,0))<sigma_p3) {
  33910. (Q = img.get_crop(p - psize1,q - psize1,r - psize1,p + psize2,q + psize2,r + psize2,true))-=P;
  33911. const float
  33912. dx = (float)x - p, dy = (float)y - q, dz = (float)z - r,
  33913. distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy + dz*dz)/sigma_s2),
  33914. weight = distance2>3?0.0f:1.0f;
  33915. sum_weights+=weight;
  33916. cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c);
  33917. }
  33918. if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights;
  33919. else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c));
  33920. } else
  33921. cimg_pragma_openmp(parallel for collapse(2)
  33922. if (res._width>=32 && res._height*res._depth>=4) firstprivate(P,Q))
  33923. cimg_forXYZ(res,x,y,z) { // Exact
  33924. P = img.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,true);
  33925. const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1,
  33926. x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2;
  33927. float sum_weights = 0, weight_max = 0;
  33928. cimg_for_inXYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (p!=x || q!=y || r!=z) {
  33929. (Q = img.get_crop(p - psize1,q - psize1,r - psize1,p + psize2,q + psize2,r + psize2,true))-=P;
  33930. const float
  33931. dx = (float)x - p, dy = (float)y - q, dz = (float)z - r,
  33932. distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy + dz*dz)/sigma_s2),
  33933. weight = (float)std::exp(-distance2);
  33934. if (weight>weight_max) weight_max = weight;
  33935. sum_weights+=weight;
  33936. cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c);
  33937. }
  33938. sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=weight_max*(*this)(x,y,z,c);
  33939. if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights;
  33940. else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c));
  33941. }
  33942. }
  33943. } else switch (patch_size) { // 2d
  33944. case 2 : if (is_fast_approx) _cimg_blur_patch2d_fast(2) else _cimg_blur_patch2d(2) break;
  33945. case 3 : if (is_fast_approx) _cimg_blur_patch2d_fast(3) else _cimg_blur_patch2d(3) break;
  33946. case 4 : if (is_fast_approx) _cimg_blur_patch2d_fast(4) else _cimg_blur_patch2d(4) break;
  33947. case 5 : if (is_fast_approx) _cimg_blur_patch2d_fast(5) else _cimg_blur_patch2d(5) break;
  33948. case 6 : if (is_fast_approx) _cimg_blur_patch2d_fast(6) else _cimg_blur_patch2d(6) break;
  33949. case 7 : if (is_fast_approx) _cimg_blur_patch2d_fast(7) else _cimg_blur_patch2d(7) break;
  33950. case 8 : if (is_fast_approx) _cimg_blur_patch2d_fast(8) else _cimg_blur_patch2d(8) break;
  33951. case 9 : if (is_fast_approx) _cimg_blur_patch2d_fast(9) else _cimg_blur_patch2d(9) break;
  33952. default : { // Fast
  33953. const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1;
  33954. if (is_fast_approx)
  33955. cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=32 && res._height>=4) firstprivate(P,Q))
  33956. cimg_forXY(res,x,y) { // 2d fast approximation.
  33957. P = img.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true);
  33958. const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2;
  33959. float sum_weights = 0;
  33960. cimg_for_inXY(res,x0,y0,x1,y1,p,q)
  33961. if ((Tfloat)cimg::abs(img(x,y,0) - (Tfloat)img(p,q,0))<sigma_p3) {
  33962. (Q = img.get_crop(p - psize1,q - psize1,p + psize2,q + psize2,true))-=P;
  33963. const float
  33964. dx = (float)x - p, dy = (float)y - q,
  33965. distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy)/sigma_s2),
  33966. weight = distance2>3?0.0f:1.0f;
  33967. sum_weights+=weight;
  33968. cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c);
  33969. }
  33970. if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights;
  33971. else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c));
  33972. } else
  33973. cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=32 && res._height>=4) firstprivate(P,Q))
  33974. cimg_forXY(res,x,y) { // 2d exact algorithm.
  33975. P = img.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true);
  33976. const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2;
  33977. float sum_weights = 0, weight_max = 0;
  33978. cimg_for_inXY(res,x0,y0,x1,y1,p,q) if (p!=x || q!=y) {
  33979. (Q = img.get_crop(p - psize1,q - psize1,p + psize2,q + psize2,true))-=P;
  33980. const float
  33981. dx = (float)x - p, dy = (float)y - q,
  33982. distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy)/sigma_s2),
  33983. weight = (float)std::exp(-distance2);
  33984. if (weight>weight_max) weight_max = weight;
  33985. sum_weights+=weight;
  33986. cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c);
  33987. }
  33988. sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=weight_max*(*this)(x,y,c);
  33989. if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights;
  33990. else cimg_forC(res,c) res(x,y,0,c) = (Tfloat)((*this)(x,y,c));
  33991. }
  33992. }
  33993. }
  33994. return res;
  33995. }
  33996. //! Blur image with the median filter.
  33997. /**
  33998. \param n Size of the median filter.
  33999. \param threshold Threshold used to discard pixels too far from the current pixel value in the median computation.
  34000. **/
  34001. CImg<T>& blur_median(const unsigned int n, const float threshold=0) {
  34002. if (!n) return *this;
  34003. return get_blur_median(n,threshold).move_to(*this);
  34004. }
  34005. //! Blur image with the median filter \newinstance.
  34006. CImg<T> get_blur_median(const unsigned int n, const float threshold=0) const {
  34007. if (is_empty() || n<=1) return +*this;
  34008. CImg<T> res(_width,_height,_depth,_spectrum);
  34009. T *ptrd = res._data;
  34010. cimg::unused(ptrd);
  34011. const int hr = (int)n/2, hl = n - hr - 1;
  34012. if (res._depth!=1) { // 3d
  34013. if (threshold>0)
  34014. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=16 && _height*_depth*_spectrum>=4))
  34015. cimg_forXYZC(*this,x,y,z,c) { // With threshold.
  34016. const int
  34017. x0 = x - hl, y0 = y - hl, z0 = z - hl, x1 = x + hr, y1 = y + hr, z1 = z + hr,
  34018. nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, nz0 = z0<0?0:z0,
  34019. nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1, nz1 = z1>=depth()?depth() - 1:z1;
  34020. const Tfloat val0 = (Tfloat)(*this)(x,y,z,c);
  34021. CImg<T> values(n*n*n);
  34022. unsigned int nb_values = 0;
  34023. T *ptrd = values.data();
  34024. cimg_for_inXYZ(*this,nx0,ny0,nz0,nx1,ny1,nz1,p,q,r)
  34025. if (cimg::abs((*this)(p,q,r,c) - val0)<=threshold) { *(ptrd++) = (*this)(p,q,r,c); ++nb_values; }
  34026. res(x,y,z,c) = nb_values?values.get_shared_points(0,nb_values - 1).median():(*this)(x,y,z,c);
  34027. }
  34028. else
  34029. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=16 && _height*_depth*_spectrum>=4))
  34030. cimg_forXYZC(*this,x,y,z,c) { // Without threshold.
  34031. const int
  34032. x0 = x - hl, y0 = y - hl, z0 = z - hl, x1 = x + hr, y1 = y + hr, z1 = z + hr,
  34033. nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, nz0 = z0<0?0:z0,
  34034. nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1, nz1 = z1>=depth()?depth() - 1:z1;
  34035. res(x,y,z,c) = get_crop(nx0,ny0,nz0,c,nx1,ny1,nz1,c).median();
  34036. }
  34037. } else {
  34038. if (threshold>0)
  34039. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=16 && _height*_spectrum>=4))
  34040. cimg_forXYC(*this,x,y,c) { // With threshold.
  34041. const int
  34042. x0 = x - hl, y0 = y - hl, x1 = x + hr, y1 = y + hr,
  34043. nx0 = x0<0?0:x0, ny0 = y0<0?0:y0,
  34044. nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1;
  34045. const Tfloat val0 = (Tfloat)(*this)(x,y,c);
  34046. CImg<T> values(n*n);
  34047. unsigned int nb_values = 0;
  34048. T *ptrd = values.data();
  34049. cimg_for_inXY(*this,nx0,ny0,nx1,ny1,p,q)
  34050. if (cimg::abs((*this)(p,q,c) - val0)<=threshold) { *(ptrd++) = (*this)(p,q,c); ++nb_values; }
  34051. res(x,y,c) = nb_values?values.get_shared_points(0,nb_values - 1).median():(*this)(x,y,c);
  34052. }
  34053. else {
  34054. const int
  34055. w1 = width() - 1, h1 = height() - 1,
  34056. w2 = width() - 2, h2 = height() - 2,
  34057. w3 = width() - 3, h3 = height() - 3,
  34058. w4 = width() - 4, h4 = height() - 4;
  34059. switch (n) { // Without threshold.
  34060. case 3 : {
  34061. cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
  34062. cimg_forC(*this,c) {
  34063. CImg<T> I(9);
  34064. cimg_for_in3x3(*this,1,1,w2,h2,x,y,0,c,I,T)
  34065. res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4],I[5],I[6],I[7],I[8]);
  34066. cimg_for_borderXY(*this,x,y,1)
  34067. res(x,y,c) = get_crop(std::max(0,x - 1),std::max(0,y - 1),0,c,
  34068. std::min(w1,x + 1),std::min(h1,y + 1),0,c).median();
  34069. }
  34070. } break;
  34071. case 5 : {
  34072. cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
  34073. cimg_forC(*this,c) {
  34074. CImg<T> I(25);
  34075. cimg_for_in5x5(*this,2,2,w3,h3,x,y,0,c,I,T)
  34076. res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4],
  34077. I[5],I[6],I[7],I[8],I[9],
  34078. I[10],I[11],I[12],I[13],I[14],
  34079. I[15],I[16],I[17],I[18],I[19],
  34080. I[20],I[21],I[22],I[23],I[24]);
  34081. cimg_for_borderXY(*this,x,y,2)
  34082. res(x,y,c) = get_crop(std::max(0,x - 2),std::max(0,y - 2),0,c,
  34083. std::min(w1,x + 2),std::min(h1,y + 2),0,c).median();
  34084. }
  34085. } break;
  34086. case 7 : {
  34087. cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
  34088. cimg_forC(*this,c) {
  34089. CImg<T> I(49);
  34090. cimg_for_in7x7(*this,3,3,w4,h4,x,y,0,c,I,T)
  34091. res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4],I[5],I[6],
  34092. I[7],I[8],I[9],I[10],I[11],I[12],I[13],
  34093. I[14],I[15],I[16],I[17],I[18],I[19],I[20],
  34094. I[21],I[22],I[23],I[24],I[25],I[26],I[27],
  34095. I[28],I[29],I[30],I[31],I[32],I[33],I[34],
  34096. I[35],I[36],I[37],I[38],I[39],I[40],I[41],
  34097. I[42],I[43],I[44],I[45],I[46],I[47],I[48]);
  34098. cimg_for_borderXY(*this,x,y,3)
  34099. res(x,y,c) = get_crop(std::max(0,x - 3),std::max(0,y - 3),0,c,
  34100. std::min(w1,x + 3),std::min(h1,y + 3),0,c).median();
  34101. }
  34102. } break;
  34103. default : {
  34104. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=16 && _height*_spectrum>=4))
  34105. cimg_forXYC(*this,x,y,c) {
  34106. const int
  34107. x0 = x - hl, y0 = y - hl, x1 = x + hr, y1 = y + hr,
  34108. nx0 = x0<0?0:x0, ny0 = y0<0?0:y0,
  34109. nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1;
  34110. res(x,y,c) = get_crop(nx0,ny0,0,c,nx1,ny1,0,c).median();
  34111. }
  34112. }
  34113. }
  34114. }
  34115. }
  34116. return res;
  34117. }
  34118. //! Sharpen image.
  34119. /**
  34120. \param amplitude Sharpening amplitude
  34121. \param sharpen_type Select sharpening method. Can be <tt>{ false=inverse diffusion | true=shock filters }</tt>.
  34122. \param edge Edge threshold (shock filters only).
  34123. \param alpha Gradient smoothness (shock filters only).
  34124. \param sigma Tensor smoothness (shock filters only).
  34125. **/
  34126. CImg<T>& sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1,
  34127. const float alpha=0, const float sigma=0) {
  34128. if (is_empty()) return *this;
  34129. T val_min, val_max = max_min(val_min);
  34130. const float nedge = edge/2;
  34131. CImg<Tfloat> velocity(_width,_height,_depth,_spectrum), _veloc_max(_spectrum);
  34132. if (_depth>1) { // 3d
  34133. if (sharpen_type) { // Shock filters.
  34134. CImg<Tfloat> G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors());
  34135. if (sigma>0) G.blur(sigma);
  34136. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=32 && _height*_depth>=16))
  34137. cimg_forYZ(G,y,z) {
  34138. Tfloat *ptrG0 = G.data(0,y,z,0), *ptrG1 = G.data(0,y,z,1),
  34139. *ptrG2 = G.data(0,y,z,2), *ptrG3 = G.data(0,y,z,3);
  34140. CImg<Tfloat> val, vec;
  34141. cimg_forX(G,x) {
  34142. G.get_tensor_at(x,y,z).symmetric_eigen(val,vec);
  34143. if (val[0]<0) val[0] = 0;
  34144. if (val[1]<0) val[1] = 0;
  34145. if (val[2]<0) val[2] = 0;
  34146. *(ptrG0++) = vec(0,0);
  34147. *(ptrG1++) = vec(0,1);
  34148. *(ptrG2++) = vec(0,2);
  34149. *(ptrG3++) = 1 - (Tfloat)std::pow(1 + val[0] + val[1] + val[2],-(Tfloat)nedge);
  34150. }
  34151. }
  34152. cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=512 && _spectrum>=2))
  34153. cimg_forC(*this,c) {
  34154. Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0;
  34155. CImg_3x3x3(I,Tfloat);
  34156. cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
  34157. const Tfloat
  34158. u = G(x,y,z,0),
  34159. v = G(x,y,z,1),
  34160. w = G(x,y,z,2),
  34161. amp = G(x,y,z,3),
  34162. ixx = Incc + Ipcc - 2*Iccc,
  34163. ixy = (Innc + Ippc - Inpc - Ipnc)/4,
  34164. ixz = (Incn + Ipcp - Incp - Ipcn)/4,
  34165. iyy = Icnc + Icpc - 2*Iccc,
  34166. iyz = (Icnn + Icpp - Icnp - Icpn)/4,
  34167. izz = Iccn + Iccp - 2*Iccc,
  34168. ixf = Incc - Iccc,
  34169. ixb = Iccc - Ipcc,
  34170. iyf = Icnc - Iccc,
  34171. iyb = Iccc - Icpc,
  34172. izf = Iccn - Iccc,
  34173. izb = Iccc - Iccp,
  34174. itt = u*u*ixx + v*v*iyy + w*w*izz + 2*u*v*ixy + 2*u*w*ixz + 2*v*w*iyz,
  34175. it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb) + w*cimg::minmod(izf,izb),
  34176. veloc = -amp*cimg::sign(itt)*cimg::abs(it);
  34177. *(ptrd++) = veloc;
  34178. if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
  34179. }
  34180. _veloc_max[c] = veloc_max;
  34181. }
  34182. } else // Inverse diffusion.
  34183. cimg_forC(*this,c) {
  34184. Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0;
  34185. CImg_3x3x3(I,Tfloat);
  34186. cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
  34187. const Tfloat veloc = -Ipcc - Incc - Icpc - Icnc - Iccp - Iccn + 6*Iccc;
  34188. *(ptrd++) = veloc;
  34189. if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
  34190. }
  34191. _veloc_max[c] = veloc_max;
  34192. }
  34193. } else { // 2d.
  34194. if (sharpen_type) { // Shock filters.
  34195. CImg<Tfloat> G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors());
  34196. if (sigma>0) G.blur(sigma);
  34197. cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=32 && _height>=16))
  34198. cimg_forY(G,y) {
  34199. CImg<Tfloat> val, vec;
  34200. Tfloat *ptrG0 = G.data(0,y,0,0), *ptrG1 = G.data(0,y,0,1), *ptrG2 = G.data(0,y,0,2);
  34201. cimg_forX(G,x) {
  34202. G.get_tensor_at(x,y).symmetric_eigen(val,vec);
  34203. if (val[0]<0) val[0] = 0;
  34204. if (val[1]<0) val[1] = 0;
  34205. *(ptrG0++) = vec(0,0);
  34206. *(ptrG1++) = vec(0,1);
  34207. *(ptrG2++) = 1 - (Tfloat)std::pow(1 + val[0] + val[1],-(Tfloat)nedge);
  34208. }
  34209. }
  34210. cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=512 && _spectrum>=2))
  34211. cimg_forC(*this,c) {
  34212. Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0;
  34213. CImg_3x3(I,Tfloat);
  34214. cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
  34215. const Tfloat
  34216. u = G(x,y,0),
  34217. v = G(x,y,1),
  34218. amp = G(x,y,2),
  34219. ixx = Inc + Ipc - 2*Icc,
  34220. ixy = (Inn + Ipp - Inp - Ipn)/4,
  34221. iyy = Icn + Icp - 2*Icc,
  34222. ixf = Inc - Icc,
  34223. ixb = Icc - Ipc,
  34224. iyf = Icn - Icc,
  34225. iyb = Icc - Icp,
  34226. itt = u*u*ixx + v*v*iyy + 2*u*v*ixy,
  34227. it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb),
  34228. veloc = -amp*cimg::sign(itt)*cimg::abs(it);
  34229. *(ptrd++) = veloc;
  34230. if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
  34231. }
  34232. _veloc_max[c] = veloc_max;
  34233. }
  34234. } else // Inverse diffusion.
  34235. cimg_forC(*this,c) {
  34236. Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0;
  34237. CImg_3x3(I,Tfloat);
  34238. cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
  34239. const Tfloat veloc = -Ipc - Inc - Icp - Icn + 4*Icc;
  34240. *(ptrd++) = veloc;
  34241. if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
  34242. }
  34243. _veloc_max[c] = veloc_max;
  34244. }
  34245. }
  34246. const Tfloat veloc_max = _veloc_max.max();
  34247. if (veloc_max<=0) return *this;
  34248. return ((velocity*=amplitude/veloc_max)+=*this).cut(val_min,val_max).move_to(*this);
  34249. }
  34250. //! Sharpen image \newinstance.
  34251. CImg<T> get_sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1,
  34252. const float alpha=0, const float sigma=0) const {
  34253. return (+*this).sharpen(amplitude,sharpen_type,edge,alpha,sigma);
  34254. }
  34255. //! Return image gradient.
  34256. /**
  34257. \param axes Axes considered for the gradient computation, as a C-string (e.g "xy").
  34258. \param scheme = Numerical scheme used for the gradient computation:
  34259. - -1 = Backward finite differences
  34260. - 0 = Centered finite differences
  34261. - 1 = Forward finite differences
  34262. - 2 = Using Sobel kernels
  34263. - 3 = Using rotation invariant kernels
  34264. - 4 = Using Deriche recusrsive filter.
  34265. - 5 = Using Van Vliet recusrsive filter.
  34266. **/
  34267. CImgList<Tfloat> get_gradient(const char *const axes=0, const int scheme=3) const {
  34268. CImgList<Tfloat> grad(2,_width,_height,_depth,_spectrum);
  34269. bool is_3d = false;
  34270. if (axes) {
  34271. for (unsigned int a = 0; axes[a]; ++a) {
  34272. const char axis = cimg::lowercase(axes[a]);
  34273. switch (axis) {
  34274. case 'x' : case 'y' : break;
  34275. case 'z' : is_3d = true; break;
  34276. default :
  34277. throw CImgArgumentException(_cimg_instance
  34278. "get_gradient(): Invalid specified axis '%c'.",
  34279. cimg_instance,
  34280. axis);
  34281. }
  34282. }
  34283. } else is_3d = (_depth>1);
  34284. if (is_3d) {
  34285. CImg<Tfloat>(_width,_height,_depth,_spectrum).move_to(grad);
  34286. switch (scheme) { // 3d.
  34287. case -1 : { // Backward finite differences.
  34288. cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2))
  34289. cimg_forC(*this,c) {
  34290. const ulongT off = (ulongT)c*_width*_height*_depth;
  34291. Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off, *ptrd2 = grad[2]._data + off;
  34292. CImg_3x3x3(I,Tfloat);
  34293. cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
  34294. *(ptrd0++) = Iccc - Ipcc;
  34295. *(ptrd1++) = Iccc - Icpc;
  34296. *(ptrd2++) = Iccc - Iccp;
  34297. }
  34298. }
  34299. } break;
  34300. case 1 : { // Forward finite differences.
  34301. cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2))
  34302. cimg_forC(*this,c) {
  34303. const ulongT off = (ulongT)c*_width*_height*_depth;
  34304. Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off, *ptrd2 = grad[2]._data + off;
  34305. CImg_2x2x2(I,Tfloat);
  34306. cimg_for2x2x2(*this,x,y,z,c,I,Tfloat) {
  34307. *(ptrd0++) = Incc - Iccc;
  34308. *(ptrd1++) = Icnc - Iccc;
  34309. *(ptrd2++) = Iccn - Iccc;
  34310. }
  34311. }
  34312. } break;
  34313. case 4 : { // Deriche filter with low standard variation.
  34314. grad[0] = get_deriche(0,1,'x');
  34315. grad[1] = get_deriche(0,1,'y');
  34316. grad[2] = get_deriche(0,1,'z');
  34317. } break;
  34318. case 5 : { // Van Vliet filter with low standard variation.
  34319. grad[0] = get_vanvliet(0,1,'x');
  34320. grad[1] = get_vanvliet(0,1,'y');
  34321. grad[2] = get_vanvliet(0,1,'z');
  34322. } break;
  34323. default : { // Central finite differences.
  34324. cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2))
  34325. cimg_forC(*this,c) {
  34326. const ulongT off = (ulongT)c*_width*_height*_depth;
  34327. Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off, *ptrd2 = grad[2]._data + off;
  34328. CImg_3x3x3(I,Tfloat);
  34329. cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
  34330. *(ptrd0++) = (Incc - Ipcc)/2;
  34331. *(ptrd1++) = (Icnc - Icpc)/2;
  34332. *(ptrd2++) = (Iccn - Iccp)/2;
  34333. }
  34334. }
  34335. }
  34336. }
  34337. } else switch (scheme) { // 2d.
  34338. case -1 : { // Backward finite differences.
  34339. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width*_height>=1048576 && _depth*_spectrum>=2))
  34340. cimg_forZC(*this,z,c) {
  34341. const ulongT off = (ulongT)c*_width*_height*_depth + z*_width*_height;
  34342. Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off;
  34343. CImg_3x3(I,Tfloat);
  34344. cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
  34345. *(ptrd0++) = Icc - Ipc;
  34346. *(ptrd1++) = Icc - Icp;
  34347. }
  34348. }
  34349. } break;
  34350. case 1 : { // Forward finite differences.
  34351. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width*_height>=1048576 && _depth*_spectrum>=2))
  34352. cimg_forZC(*this,z,c) {
  34353. const ulongT off = (ulongT)c*_width*_height*_depth + z*_width*_height;
  34354. Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off;
  34355. CImg_2x2(I,Tfloat);
  34356. cimg_for2x2(*this,x,y,z,c,I,Tfloat) {
  34357. *(ptrd0++) = Inc - Icc;
  34358. *(ptrd1++) = Icn - Icc;
  34359. }
  34360. }
  34361. } break;
  34362. case 2 : { // Sobel scheme.
  34363. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width*_height>=1048576 && _depth*_spectrum>=2))
  34364. cimg_forZC(*this,z,c) {
  34365. const ulongT off = (ulongT)c*_width*_height*_depth + z*_width*_height;
  34366. Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off;
  34367. CImg_3x3(I,Tfloat);
  34368. cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
  34369. *(ptrd0++) = -Ipp - 2*Ipc - Ipn + Inp + 2*Inc + Inn;
  34370. *(ptrd1++) = -Ipp - 2*Icp - Inp + Ipn + 2*Icn + Inn;
  34371. }
  34372. }
  34373. } break;
  34374. case 3 : { // Rotation invariant kernel.
  34375. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width*_height>=1048576 && _depth*_spectrum>=2))
  34376. cimg_forZC(*this,z,c) {
  34377. const ulongT off = (ulongT)c*_width*_height*_depth + z*_width*_height;
  34378. Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off;
  34379. CImg_3x3(I,Tfloat);
  34380. const Tfloat a = (Tfloat)(0.25f*(2 - std::sqrt(2.0f))), b = (Tfloat)(0.5f*(std::sqrt(2.0f) - 1));
  34381. cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
  34382. *(ptrd0++) = -a*Ipp - b*Ipc - a*Ipn + a*Inp + b*Inc + a*Inn;
  34383. *(ptrd1++) = -a*Ipp - b*Icp - a*Inp + a*Ipn + b*Icn + a*Inn;
  34384. }
  34385. }
  34386. } break;
  34387. case 4 : { // Van Vliet filter with low standard variation
  34388. grad[0] = get_deriche(0,1,'x');
  34389. grad[1] = get_deriche(0,1,'y');
  34390. } break;
  34391. case 5 : { // Deriche filter with low standard variation
  34392. grad[0] = get_vanvliet(0,1,'x');
  34393. grad[1] = get_vanvliet(0,1,'y');
  34394. } break;
  34395. default : { // Central finite differences
  34396. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width*_height>=1048576 && _depth*_spectrum>=2))
  34397. cimg_forZC(*this,z,c) {
  34398. const ulongT off = (ulongT)c*_width*_height*_depth + z*_width*_height;
  34399. Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off;
  34400. CImg_3x3(I,Tfloat);
  34401. cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
  34402. *(ptrd0++) = (Inc - Ipc)/2;
  34403. *(ptrd1++) = (Icn - Icp)/2;
  34404. }
  34405. }
  34406. }
  34407. }
  34408. if (!axes) return grad;
  34409. CImgList<Tfloat> res;
  34410. for (unsigned int l = 0; axes[l]; ++l) {
  34411. const char axis = cimg::lowercase(axes[l]);
  34412. switch (axis) {
  34413. case 'x' : res.insert(grad[0]); break;
  34414. case 'y' : res.insert(grad[1]); break;
  34415. case 'z' : res.insert(grad[2]); break;
  34416. }
  34417. }
  34418. grad.assign();
  34419. return res;
  34420. }
  34421. //! Return image hessian.
  34422. /**
  34423. \param axes Axes considered for the hessian computation, as a C-string (e.g "xy").
  34424. **/
  34425. CImgList<Tfloat> get_hessian(const char *const axes=0) const {
  34426. CImgList<Tfloat> res;
  34427. const char *naxes = axes, *const def_axes2d = "xxxyyy", *const def_axes3d = "xxxyxzyyyzzz";
  34428. if (!axes) naxes = _depth>1?def_axes3d:def_axes2d;
  34429. const unsigned int lmax = (unsigned int)std::strlen(naxes);
  34430. if (lmax%2)
  34431. throw CImgArgumentException(_cimg_instance
  34432. "get_hessian(): Invalid specified axes '%s'.",
  34433. cimg_instance,
  34434. naxes);
  34435. res.assign(lmax/2,_width,_height,_depth,_spectrum);
  34436. if (!cimg::strcasecmp(naxes,def_axes3d)) { // 3d
  34437. cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2))
  34438. cimg_forC(*this,c) {
  34439. const ulongT off = (ulongT)c*_width*_height*_depth;
  34440. Tfloat
  34441. *ptrd0 = res[0]._data + off, *ptrd1 = res[1]._data + off, *ptrd2 = res[2]._data + off,
  34442. *ptrd3 = res[3]._data + off, *ptrd4 = res[4]._data + off, *ptrd5 = res[5]._data + off;
  34443. CImg_3x3x3(I,Tfloat);
  34444. cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
  34445. *(ptrd0++) = Ipcc + Incc - 2*Iccc; // Ixx
  34446. *(ptrd1++) = (Ippc + Innc - Ipnc - Inpc)/4; // Ixy
  34447. *(ptrd2++) = (Ipcp + Incn - Ipcn - Incp)/4; // Ixz
  34448. *(ptrd3++) = Icpc + Icnc - 2*Iccc; // Iyy
  34449. *(ptrd4++) = (Icpp + Icnn - Icpn - Icnp)/4; // Iyz
  34450. *(ptrd5++) = Iccn + Iccp - 2*Iccc; // Izz
  34451. }
  34452. }
  34453. } else if (!cimg::strcasecmp(naxes,def_axes2d)) { // 2d
  34454. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width*_height>=1048576 && _depth*_spectrum>=2))
  34455. cimg_forZC(*this,z,c) {
  34456. const ulongT off = (ulongT)c*_width*_height*_depth + z*_width*_height;
  34457. Tfloat *ptrd0 = res[0]._data + off, *ptrd1 = res[1]._data + off, *ptrd2 = res[2]._data + off;
  34458. CImg_3x3(I,Tfloat);
  34459. cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
  34460. *(ptrd0++) = Ipc + Inc - 2*Icc; // Ixx
  34461. *(ptrd1++) = (Ipp + Inn - Ipn - Inp)/4; // Ixy
  34462. *(ptrd2++) = Icp + Icn - 2*Icc; // Iyy
  34463. }
  34464. }
  34465. } else for (unsigned int l = 0; l<lmax; ) { // Version with custom axes.
  34466. const unsigned int l2 = l/2;
  34467. char axis1 = naxes[l++], axis2 = naxes[l++];
  34468. if (axis1>axis2) cimg::swap(axis1,axis2);
  34469. bool valid_axis = false;
  34470. if (axis1=='x' && axis2=='x') { // Ixx
  34471. valid_axis = true;
  34472. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width*_height>=1048576 && _depth*_spectrum>=2))
  34473. cimg_forZC(*this,z,c) {
  34474. Tfloat *ptrd = res[l2].data(0,0,z,c);
  34475. CImg_3x3(I,Tfloat);
  34476. cimg_for3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Ipc + Inc - 2*Icc;
  34477. }
  34478. }
  34479. else if (axis1=='x' && axis2=='y') { // Ixy
  34480. valid_axis = true;
  34481. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width*_height>=1048576 && _depth*_spectrum>=2))
  34482. cimg_forZC(*this,z,c) {
  34483. Tfloat *ptrd = res[l2].data(0,0,z,c);
  34484. CImg_3x3(I,Tfloat);
  34485. cimg_for3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = (Ipp + Inn - Ipn - Inp)/4;
  34486. }
  34487. }
  34488. else if (axis1=='x' && axis2=='z') { // Ixz
  34489. valid_axis = true;
  34490. cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2))
  34491. cimg_forC(*this,c) {
  34492. Tfloat *ptrd = res[l2].data(0,0,0,c);
  34493. CImg_3x3x3(I,Tfloat);
  34494. cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = (Ipcp + Incn - Ipcn - Incp)/4;
  34495. }
  34496. }
  34497. else if (axis1=='y' && axis2=='y') { // Iyy
  34498. valid_axis = true;
  34499. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width*_height>=1048576 && _depth*_spectrum>=2))
  34500. cimg_forZC(*this,z,c) {
  34501. Tfloat *ptrd = res[l2].data(0,0,z,c);
  34502. CImg_3x3(I,Tfloat);
  34503. cimg_for3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Icp + Icn - 2*Icc;
  34504. }
  34505. }
  34506. else if (axis1=='y' && axis2=='z') { // Iyz
  34507. valid_axis = true;
  34508. cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2))
  34509. cimg_forC(*this,c) {
  34510. Tfloat *ptrd = res[l2].data(0,0,0,c);
  34511. CImg_3x3x3(I,Tfloat);
  34512. cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = (Icpp + Icnn - Icpn - Icnp)/4;
  34513. }
  34514. }
  34515. else if (axis1=='z' && axis2=='z') { // Izz
  34516. valid_axis = true;
  34517. cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2))
  34518. cimg_forC(*this,c) {
  34519. Tfloat *ptrd = res[l2].data(0,0,0,c);
  34520. CImg_3x3x3(I,Tfloat);
  34521. cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Iccn + Iccp - 2*Iccc;
  34522. }
  34523. }
  34524. else if (!valid_axis)
  34525. throw CImgArgumentException(_cimg_instance
  34526. "get_hessian(): Invalid specified axes '%s'.",
  34527. cimg_instance,
  34528. naxes);
  34529. }
  34530. return res;
  34531. }
  34532. //! Compute image laplacian.
  34533. CImg<T>& laplacian() {
  34534. return get_laplacian().move_to(*this);
  34535. }
  34536. //! Compute image laplacian \newinstance.
  34537. CImg<Tfloat> get_laplacian() const {
  34538. if (is_empty()) return CImg<Tfloat>();
  34539. CImg<Tfloat> res(_width,_height,_depth,_spectrum);
  34540. if (_depth>1) { // 3d
  34541. cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2))
  34542. cimg_forC(*this,c) {
  34543. Tfloat *ptrd = res.data(0,0,0,c);
  34544. CImg_3x3x3(I,Tfloat);
  34545. cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Incc + Ipcc + Icnc + Icpc + Iccn + Iccp - 6*Iccc;
  34546. }
  34547. } else if (_height>1) { // 2d
  34548. cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=1048576 && _depth*_spectrum>=2))
  34549. cimg_forC(*this,c) {
  34550. Tfloat *ptrd = res.data(0,0,0,c);
  34551. CImg_3x3(I,Tfloat);
  34552. cimg_for3x3(*this,x,y,0,c,I,Tfloat) *(ptrd++) = Inc + Ipc + Icn + Icp - 4*Icc;
  34553. }
  34554. } else { // 1d
  34555. cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=1048576 && _height*_depth*_spectrum>=2))
  34556. cimg_forC(*this,c) {
  34557. Tfloat *ptrd = res.data(0,0,0,c);
  34558. CImg_3x3(I,Tfloat);
  34559. cimg_for3x3(*this,x,y,0,c,I,Tfloat) *(ptrd++) = Inc + Ipc - 2*Icc;
  34560. }
  34561. }
  34562. return res;
  34563. }
  34564. //! Compute the structure tensor field of an image.
  34565. /**
  34566. \param is_fwbw_scheme scheme. Can be <tt>{ false=centered | true=forward-backward }</tt>
  34567. **/
  34568. CImg<T>& structure_tensors(const bool is_fwbw_scheme=false) {
  34569. return get_structure_tensors(is_fwbw_scheme).move_to(*this);
  34570. }
  34571. //! Compute the structure tensor field of an image \newinstance.
  34572. CImg<Tfloat> get_structure_tensors(const bool is_fwbw_scheme=false) const {
  34573. if (is_empty()) return *this;
  34574. CImg<Tfloat> res;
  34575. if (_depth>1) { // 3d
  34576. res.assign(_width,_height,_depth,6,0);
  34577. if (!is_fwbw_scheme) { // Classical central finite differences
  34578. cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2))
  34579. cimg_forC(*this,c) {
  34580. Tfloat
  34581. *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2),
  34582. *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5);
  34583. CImg_3x3x3(I,Tfloat);
  34584. cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
  34585. const Tfloat
  34586. ix = (Incc - Ipcc)/2,
  34587. iy = (Icnc - Icpc)/2,
  34588. iz = (Iccn - Iccp)/2;
  34589. *(ptrd0++)+=ix*ix;
  34590. *(ptrd1++)+=ix*iy;
  34591. *(ptrd2++)+=ix*iz;
  34592. *(ptrd3++)+=iy*iy;
  34593. *(ptrd4++)+=iy*iz;
  34594. *(ptrd5++)+=iz*iz;
  34595. }
  34596. }
  34597. } else { // Forward/backward finite differences.
  34598. cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2))
  34599. cimg_forC(*this,c) {
  34600. Tfloat
  34601. *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2),
  34602. *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5);
  34603. CImg_3x3x3(I,Tfloat);
  34604. cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
  34605. const Tfloat
  34606. ixf = Incc - Iccc, ixb = Iccc - Ipcc,
  34607. iyf = Icnc - Iccc, iyb = Iccc - Icpc,
  34608. izf = Iccn - Iccc, izb = Iccc - Iccp;
  34609. *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2;
  34610. *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4;
  34611. *(ptrd2++)+=(ixf*izf + ixf*izb + ixb*izf + ixb*izb)/4;
  34612. *(ptrd3++)+=(iyf*iyf + iyb*iyb)/2;
  34613. *(ptrd4++)+=(iyf*izf + iyf*izb + iyb*izf + iyb*izb)/4;
  34614. *(ptrd5++)+=(izf*izf + izb*izb)/2;
  34615. }
  34616. }
  34617. }
  34618. } else { // 2d
  34619. res.assign(_width,_height,_depth,3,0);
  34620. if (!is_fwbw_scheme) { // Classical central finite differences
  34621. cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=1048576 && _depth*_spectrum>=2))
  34622. cimg_forC(*this,c) {
  34623. Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2);
  34624. CImg_3x3(I,Tfloat);
  34625. cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
  34626. const Tfloat
  34627. ix = (Inc - Ipc)/2,
  34628. iy = (Icn - Icp)/2;
  34629. *(ptrd0++)+=ix*ix;
  34630. *(ptrd1++)+=ix*iy;
  34631. *(ptrd2++)+=iy*iy;
  34632. }
  34633. }
  34634. } else { // Forward/backward finite differences (version 2).
  34635. cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=1048576 && _depth*_spectrum>=2))
  34636. cimg_forC(*this,c) {
  34637. Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2);
  34638. CImg_3x3(I,Tfloat);
  34639. cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
  34640. const Tfloat
  34641. ixf = Inc - Icc, ixb = Icc - Ipc,
  34642. iyf = Icn - Icc, iyb = Icc - Icp;
  34643. *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2;
  34644. *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4;
  34645. *(ptrd2++)+=(iyf*iyf + iyb*iyb)/2;
  34646. }
  34647. }
  34648. }
  34649. }
  34650. return res;
  34651. }
  34652. //! Compute field of diffusion tensors for edge-preserving smoothing.
  34653. /**
  34654. \param sharpness Sharpness
  34655. \param anisotropy Anisotropy
  34656. \param alpha Standard deviation of the gradient blur.
  34657. \param sigma Standard deviation of the structure tensor blur.
  34658. \param is_sqrt Tells if the square root of the tensor field is computed instead.
  34659. **/
  34660. CImg<T>& diffusion_tensors(const float sharpness=0.7f, const float anisotropy=0.6f,
  34661. const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) {
  34662. CImg<Tfloat> res;
  34663. const float
  34664. nsharpness = std::max(sharpness,1e-5f),
  34665. power1 = (is_sqrt?0.5f:1)*nsharpness,
  34666. power2 = power1/(1e-7f + 1 - anisotropy);
  34667. blur(alpha).normalize(0,(T)255);
  34668. if (_depth>1) { // 3d
  34669. get_structure_tensors().move_to(res).blur(sigma);
  34670. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=256 && _height*_depth>=256))
  34671. cimg_forYZ(*this,y,z) {
  34672. Tfloat
  34673. *ptrd0 = res.data(0,y,z,0), *ptrd1 = res.data(0,y,z,1), *ptrd2 = res.data(0,y,z,2),
  34674. *ptrd3 = res.data(0,y,z,3), *ptrd4 = res.data(0,y,z,4), *ptrd5 = res.data(0,y,z,5);
  34675. CImg<floatT> val(3), vec(3,3);
  34676. cimg_forX(*this,x) {
  34677. res.get_tensor_at(x,y,z).symmetric_eigen(val,vec);
  34678. const float
  34679. _l1 = val[2], _l2 = val[1], _l3 = val[0],
  34680. l1 = _l1>0?_l1:0, l2 = _l2>0?_l2:0, l3 = _l3>0?_l3:0,
  34681. ux = vec(0,0), uy = vec(0,1), uz = vec(0,2),
  34682. vx = vec(1,0), vy = vec(1,1), vz = vec(1,2),
  34683. wx = vec(2,0), wy = vec(2,1), wz = vec(2,2),
  34684. n1 = (float)std::pow(1 + l1 + l2 + l3,-power1),
  34685. n2 = (float)std::pow(1 + l1 + l2 + l3,-power2);
  34686. *(ptrd0++) = n1*(ux*ux + vx*vx) + n2*wx*wx;
  34687. *(ptrd1++) = n1*(ux*uy + vx*vy) + n2*wx*wy;
  34688. *(ptrd2++) = n1*(ux*uz + vx*vz) + n2*wx*wz;
  34689. *(ptrd3++) = n1*(uy*uy + vy*vy) + n2*wy*wy;
  34690. *(ptrd4++) = n1*(uy*uz + vy*vz) + n2*wy*wz;
  34691. *(ptrd5++) = n1*(uz*uz + vz*vz) + n2*wz*wz;
  34692. }
  34693. }
  34694. } else { // for 2d images
  34695. get_structure_tensors().move_to(res).blur(sigma);
  34696. cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256 && _height>=256))
  34697. cimg_forY(*this,y) {
  34698. Tfloat *ptrd0 = res.data(0,y,0,0), *ptrd1 = res.data(0,y,0,1), *ptrd2 = res.data(0,y,0,2);
  34699. CImg<floatT> val(2), vec(2,2);
  34700. cimg_forX(*this,x) {
  34701. res.get_tensor_at(x,y).symmetric_eigen(val,vec);
  34702. const float
  34703. _l1 = val[1], _l2 = val[0],
  34704. l1 = _l1>0?_l1:0, l2 = _l2>0?_l2:0,
  34705. ux = vec(1,0), uy = vec(1,1),
  34706. vx = vec(0,0), vy = vec(0,1),
  34707. n1 = (float)std::pow(1 + l1 + l2,-power1),
  34708. n2 = (float)std::pow(1 + l1 + l2,-power2);
  34709. *(ptrd0++) = n1*ux*ux + n2*vx*vx;
  34710. *(ptrd1++) = n1*ux*uy + n2*vx*vy;
  34711. *(ptrd2++) = n1*uy*uy + n2*vy*vy;
  34712. }
  34713. }
  34714. }
  34715. return res.move_to(*this);
  34716. }
  34717. //! Compute field of diffusion tensors for edge-preserving smoothing \newinstance.
  34718. CImg<Tfloat> get_diffusion_tensors(const float sharpness=0.7f, const float anisotropy=0.6f,
  34719. const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) const {
  34720. return CImg<Tfloat>(*this,false).diffusion_tensors(sharpness,anisotropy,alpha,sigma,is_sqrt);
  34721. }
  34722. //! Estimate displacement field between two images.
  34723. /**
  34724. \param source Reference image.
  34725. \param smoothness Smoothness of estimated displacement field.
  34726. \param precision Precision required for algorithm convergence.
  34727. \param nb_scales Number of scales used to estimate the displacement field.
  34728. \param iteration_max Maximum number of iterations allowed for one scale.
  34729. \param is_backward If false, match I2(X + U(X)) = I1(X), else match I2(X) = I1(X - U(X)).
  34730. \param guide Image used as the initial correspondence estimate for the algorithm.
  34731. 'guide' may have a last channel with boolean values (0=false | other=true) that
  34732. tells for each pixel if its correspondence vector is constrained to its initial value (constraint mask).
  34733. **/
  34734. CImg<T>& displacement(const CImg<T>& source, const float smoothness=0.1f, const float precision=5.0f,
  34735. const unsigned int nb_scales=0, const unsigned int iteration_max=10000,
  34736. const bool is_backward=false,
  34737. const CImg<floatT>& guide=CImg<floatT>::const_empty()) {
  34738. return get_displacement(source,smoothness,precision,nb_scales,iteration_max,is_backward,guide).
  34739. move_to(*this);
  34740. }
  34741. //! Estimate displacement field between two images \newinstance.
  34742. CImg<floatT> get_displacement(const CImg<T>& source,
  34743. const float smoothness=0.1f, const float precision=5.0f,
  34744. const unsigned int nb_scales=0, const unsigned int iteration_max=10000,
  34745. const bool is_backward=false,
  34746. const CImg<floatT>& guide=CImg<floatT>::const_empty()) const {
  34747. if (is_empty() || !source) return +*this;
  34748. if (!is_sameXYZC(source))
  34749. throw CImgArgumentException(_cimg_instance
  34750. "displacement(): Instance and source image (%u,%u,%u,%u,%p) have "
  34751. "different dimensions.",
  34752. cimg_instance,
  34753. source._width,source._height,source._depth,source._spectrum,source._data);
  34754. if (precision<0)
  34755. throw CImgArgumentException(_cimg_instance
  34756. "displacement(): Invalid specified precision %g "
  34757. "(should be >=0)",
  34758. cimg_instance,
  34759. precision);
  34760. const bool is_3d = source._depth>1;
  34761. const unsigned int constraint = is_3d?3:2;
  34762. if (guide &&
  34763. (guide._width!=_width || guide._height!=_height || guide._depth!=_depth || guide._spectrum<constraint))
  34764. throw CImgArgumentException(_cimg_instance
  34765. "displacement(): Specified guide (%u,%u,%u,%u,%p) "
  34766. "has invalid dimensions.",
  34767. cimg_instance,
  34768. guide._width,guide._height,guide._depth,guide._spectrum,guide._data);
  34769. const unsigned int
  34770. mins = is_3d?cimg::min(_width,_height,_depth):std::min(_width,_height),
  34771. _nb_scales = nb_scales>0?nb_scales:
  34772. (unsigned int)cimg::round(std::log(mins/8.0)/std::log(1.5),1,1);
  34773. const float _precision = (float)std::pow(10.0,-(double)precision);
  34774. float sm, sM = source.max_min(sm), tm, tM = max_min(tm);
  34775. const float sdelta = sm==sM?1:(sM - sm), tdelta = tm==tM?1:(tM - tm);
  34776. CImg<floatT> U, V;
  34777. floatT bound = 0;
  34778. for (int scale = (int)_nb_scales - 1; scale>=0; --scale) {
  34779. const float factor = (float)std::pow(1.5,(double)scale);
  34780. const unsigned int
  34781. _sw = (unsigned int)(_width/factor), sw = _sw?_sw:1,
  34782. _sh = (unsigned int)(_height/factor), sh = _sh?_sh:1,
  34783. _sd = (unsigned int)(_depth/factor), sd = _sd?_sd:1;
  34784. if (sw<5 && sh<5 && (!is_3d || sd<5)) continue; // skip too small scales.
  34785. const CImg<Tfloat>
  34786. I1 = (source.get_resize(sw,sh,sd,-100,2)-=sm)/=sdelta,
  34787. I2 = (get_resize(I1,2)-=tm)/=tdelta;
  34788. if (guide._spectrum>constraint) guide.get_resize(I2._width,I2._height,I2._depth,-100,1).move_to(V);
  34789. if (U) (U*=1.5f).resize(I2._width,I2._height,I2._depth,-100,3);
  34790. else {
  34791. if (guide)
  34792. guide.get_shared_channels(0,is_3d?2:1).get_resize(I2._width,I2._height,I2._depth,-100,2).move_to(U);
  34793. else U.assign(I2._width,I2._height,I2._depth,is_3d?3:2,0);
  34794. }
  34795. float dt = 2, energy = cimg::type<float>::max();
  34796. const CImgList<Tfloat> dI = is_backward?I1.get_gradient():I2.get_gradient();
  34797. cimg_abort_init;
  34798. for (unsigned int iteration = 0; iteration<iteration_max; ++iteration) {
  34799. cimg_abort_test;
  34800. float _energy = 0;
  34801. if (is_3d) { // 3d version.
  34802. if (smoothness>=0) // Isotropic regularization.
  34803. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_height*_depth>=8 && _width>=16)
  34804. reduction(+:_energy))
  34805. cimg_forYZ(U,y,z) {
  34806. const int
  34807. _p1y = y?y - 1:0, _n1y = y<U.height() - 1?y + 1:y,
  34808. _p1z = z?z - 1:0, _n1z = z<U.depth() - 1?z + 1:z;
  34809. cimg_for3X(U,x) {
  34810. const float
  34811. X = is_backward?x - U(x,y,z,0):x + U(x,y,z,0),
  34812. Y = is_backward?y - U(x,y,z,1):y + U(x,y,z,1),
  34813. Z = is_backward?z - U(x,y,z,2):z + U(x,y,z,2);
  34814. float delta_I = 0, _energy_regul = 0;
  34815. if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1._linear_atXYZ(X,Y,Z,c) - I2(x,y,z,c));
  34816. else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,z,c) - I2._linear_atXYZ(X,Y,Z,c));
  34817. cimg_forC(U,c) {
  34818. const float
  34819. Ux = 0.5f*(U(_n1x,y,z,c) - U(_p1x,y,z,c)),
  34820. Uy = 0.5f*(U(x,_n1y,z,c) - U(x,_p1y,z,c)),
  34821. Uz = 0.5f*(U(x,y,_n1z,c) - U(x,y,_p1z,c)),
  34822. Uxx = U(_n1x,y,z,c) + U(_p1x,y,z,c),
  34823. Uyy = U(x,_n1y,z,c) + U(x,_p1y,z,c),
  34824. Uzz = U(x,y,_n1z,c) + U(x,y,_p1z,c);
  34825. U(x,y,z,c) = (float)(U(x,y,z,c) + dt*(delta_I*dI[c]._linear_atXYZ(X,Y,Z) +
  34826. smoothness* ( Uxx + Uyy + Uzz)))/(1 + 6*smoothness*dt);
  34827. _energy_regul+=Ux*Ux + Uy*Uy + Uz*Uz;
  34828. }
  34829. if (is_backward) { // Constraint displacement vectors to stay in image.
  34830. if (U(x,y,z,0)>x) U(x,y,z,0) = (float)x;
  34831. if (U(x,y,z,1)>y) U(x,y,z,1) = (float)y;
  34832. if (U(x,y,z,2)>z) U(x,y,z,2) = (float)z;
  34833. bound = (float)x - _width; if (U(x,y,z,0)<=bound) U(x,y,z,0) = bound;
  34834. bound = (float)y - _height; if (U(x,y,z,1)<=bound) U(x,y,z,1) = bound;
  34835. bound = (float)z - _depth; if (U(x,y,z,2)<=bound) U(x,y,z,2) = bound;
  34836. } else {
  34837. if (U(x,y,z,0)<-x) U(x,y,z,0) = -(float)x;
  34838. if (U(x,y,z,1)<-y) U(x,y,z,1) = -(float)y;
  34839. if (U(x,y,z,2)<-z) U(x,y,z,2) = -(float)z;
  34840. bound = (float)_width - x; if (U(x,y,z,0)>=bound) U(x,y,z,0) = bound;
  34841. bound = (float)_height - y; if (U(x,y,z,1)>=bound) U(x,y,z,1) = bound;
  34842. bound = (float)_depth - z; if (U(x,y,z,2)>=bound) U(x,y,z,2) = bound;
  34843. }
  34844. _energy+=delta_I*delta_I + smoothness*_energy_regul;
  34845. }
  34846. if (V) cimg_forXYZ(V,x,y,z) if (V(x,y,z,3)) { // Apply constraints.
  34847. U(x,y,z,0) = V(x,y,z,0)/factor;
  34848. U(x,y,z,1) = V(x,y,z,1)/factor;
  34849. U(x,y,z,2) = V(x,y,z,2)/factor;
  34850. }
  34851. } else { // Anisotropic regularization.
  34852. const float nsmoothness = -smoothness;
  34853. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_height*_depth>=8 && _width>=16)
  34854. reduction(+:_energy))
  34855. cimg_forYZ(U,y,z) {
  34856. const int
  34857. _p1y = y?y - 1:0, _n1y = y<U.height() - 1?y + 1:y,
  34858. _p1z = z?z - 1:0, _n1z = z<U.depth() - 1?z + 1:z;
  34859. cimg_for3X(U,x) {
  34860. const float
  34861. X = is_backward?x - U(x,y,z,0):x + U(x,y,z,0),
  34862. Y = is_backward?y - U(x,y,z,1):y + U(x,y,z,1),
  34863. Z = is_backward?z - U(x,y,z,2):z + U(x,y,z,2);
  34864. float delta_I = 0, _energy_regul = 0;
  34865. if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1._linear_atXYZ(X,Y,Z,c) - I2(x,y,z,c));
  34866. else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,z,c) - I2._linear_atXYZ(X,Y,Z,c));
  34867. cimg_forC(U,c) {
  34868. const float
  34869. Ux = 0.5f*(U(_n1x,y,z,c) - U(_p1x,y,z,c)),
  34870. Uy = 0.5f*(U(x,_n1y,z,c) - U(x,_p1y,z,c)),
  34871. Uz = 0.5f*(U(x,y,_n1z,c) - U(x,y,_p1z,c)),
  34872. N2 = Ux*Ux + Uy*Uy + Uz*Uz,
  34873. N = std::sqrt(N2),
  34874. N3 = 1e-5f + N2*N,
  34875. coef_a = (1 - Ux*Ux/N2)/N,
  34876. coef_b = -2*Ux*Uy/N3,
  34877. coef_c = -2*Ux*Uz/N3,
  34878. coef_d = (1 - Uy*Uy/N2)/N,
  34879. coef_e = -2*Uy*Uz/N3,
  34880. coef_f = (1 - Uz*Uz/N2)/N,
  34881. Uxx = U(_n1x,y,z,c) + U(_p1x,y,z,c),
  34882. Uyy = U(x,_n1y,z,c) + U(x,_p1y,z,c),
  34883. Uzz = U(x,y,_n1z,c) + U(x,y,_p1z,c),
  34884. Uxy = 0.25f*(U(_n1x,_n1y,z,c) + U(_p1x,_p1y,z,c) - U(_n1x,_p1y,z,c) - U(_n1x,_p1y,z,c)),
  34885. Uxz = 0.25f*(U(_n1x,y,_n1z,c) + U(_p1x,y,_p1z,c) - U(_n1x,y,_p1z,c) - U(_n1x,y,_p1z,c)),
  34886. Uyz = 0.25f*(U(x,_n1y,_n1z,c) + U(x,_p1y,_p1z,c) - U(x,_n1y,_p1z,c) - U(x,_n1y,_p1z,c));
  34887. U(x,y,z,c) = (float)(U(x,y,z,c) + dt*(delta_I*dI[c]._linear_atXYZ(X,Y,Z) +
  34888. nsmoothness* ( coef_a*Uxx + coef_b*Uxy +
  34889. coef_c*Uxz + coef_d*Uyy +
  34890. coef_e*Uyz + coef_f*Uzz ))
  34891. )/(1 + 2*(coef_a + coef_d + coef_f)*nsmoothness*dt);
  34892. _energy_regul+=N;
  34893. }
  34894. if (is_backward) { // Constraint displacement vectors to stay in image.
  34895. if (U(x,y,z,0)>x) U(x,y,z,0) = (float)x;
  34896. if (U(x,y,z,1)>y) U(x,y,z,1) = (float)y;
  34897. if (U(x,y,z,2)>z) U(x,y,z,2) = (float)z;
  34898. bound = (float)x - _width; if (U(x,y,z,0)<=bound) U(x,y,z,0) = bound;
  34899. bound = (float)y - _height; if (U(x,y,z,1)<=bound) U(x,y,z,1) = bound;
  34900. bound = (float)z - _depth; if (U(x,y,z,2)<=bound) U(x,y,z,2) = bound;
  34901. } else {
  34902. if (U(x,y,z,0)<-x) U(x,y,z,0) = -(float)x;
  34903. if (U(x,y,z,1)<-y) U(x,y,z,1) = -(float)y;
  34904. if (U(x,y,z,2)<-z) U(x,y,z,2) = -(float)z;
  34905. bound = (float)_width - x; if (U(x,y,z,0)>=bound) U(x,y,z,0) = bound;
  34906. bound = (float)_height - y; if (U(x,y,z,1)>=bound) U(x,y,z,1) = bound;
  34907. bound = (float)_depth - z; if (U(x,y,z,2)>=bound) U(x,y,z,2) = bound;
  34908. }
  34909. _energy+=delta_I*delta_I + nsmoothness*_energy_regul;
  34910. }
  34911. if (V) cimg_forXYZ(V,x,y,z) if (V(x,y,z,3)) { // Apply constraints.
  34912. U(x,y,z,0) = V(x,y,z,0)/factor;
  34913. U(x,y,z,1) = V(x,y,z,1)/factor;
  34914. U(x,y,z,2) = V(x,y,z,2)/factor;
  34915. }
  34916. }
  34917. }
  34918. } else { // 2d version.
  34919. if (smoothness>=0) // Isotropic regularization.
  34920. cimg_pragma_openmp(parallel for cimg_openmp_if(_height>=8 && _width>=16) reduction(+:_energy))
  34921. cimg_forY(U,y) {
  34922. const int _p1y = y?y - 1:0, _n1y = y<U.height() - 1?y + 1:y;
  34923. cimg_for3X(U,x) {
  34924. const float
  34925. X = is_backward?x - U(x,y,0):x + U(x,y,0),
  34926. Y = is_backward?y - U(x,y,1):y + U(x,y,1);
  34927. float delta_I = 0, _energy_regul = 0;
  34928. if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1._linear_atXY(X,Y,c) - I2(x,y,c));
  34929. else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,c) - I2._linear_atXY(X,Y,c));
  34930. cimg_forC(U,c) {
  34931. const float
  34932. Ux = 0.5f*(U(_n1x,y,c) - U(_p1x,y,c)),
  34933. Uy = 0.5f*(U(x,_n1y,c) - U(x,_p1y,c)),
  34934. Uxx = U(_n1x,y,c) + U(_p1x,y,c),
  34935. Uyy = U(x,_n1y,c) + U(x,_p1y,c);
  34936. U(x,y,c) = (float)(U(x,y,c) + dt*(delta_I*dI[c]._linear_atXY(X,Y) +
  34937. smoothness*( Uxx + Uyy )))/(1 + 4*smoothness*dt);
  34938. _energy_regul+=Ux*Ux + Uy*Uy;
  34939. }
  34940. if (is_backward) { // Constraint displacement vectors to stay in image.
  34941. if (U(x,y,0)>x) U(x,y,0) = (float)x;
  34942. if (U(x,y,1)>y) U(x,y,1) = (float)y;
  34943. bound = (float)x - _width; if (U(x,y,0)<=bound) U(x,y,0) = bound;
  34944. bound = (float)y - _height; if (U(x,y,1)<=bound) U(x,y,1) = bound;
  34945. } else {
  34946. if (U(x,y,0)<-x) U(x,y,0) = -(float)x;
  34947. if (U(x,y,1)<-y) U(x,y,1) = -(float)y;
  34948. bound = (float)_width - x; if (U(x,y,0)>=bound) U(x,y,0) = bound;
  34949. bound = (float)_height - y; if (U(x,y,1)>=bound) U(x,y,1) = bound;
  34950. }
  34951. _energy+=delta_I*delta_I + smoothness*_energy_regul;
  34952. }
  34953. if (V) cimg_forX(V,x) if (V(x,y,2)) { // Apply constraints.
  34954. U(x,y,0) = V(x,y,0)/factor;
  34955. U(x,y,1) = V(x,y,1)/factor;
  34956. }
  34957. } else { // Anisotropic regularization.
  34958. const float nsmoothness = -smoothness;
  34959. cimg_pragma_openmp(parallel for cimg_openmp_if(_height>=8 && _width>=16) reduction(+:_energy))
  34960. cimg_forY(U,y) {
  34961. const int _p1y = y?y - 1:0, _n1y = y<U.height() - 1?y + 1:y;
  34962. cimg_for3X(U,x) {
  34963. const float
  34964. X = is_backward?x - U(x,y,0):x + U(x,y,0),
  34965. Y = is_backward?y - U(x,y,1):y + U(x,y,1);
  34966. float delta_I = 0, _energy_regul = 0;
  34967. if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1._linear_atXY(X,Y,c) - I2(x,y,c));
  34968. else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,c) - I2._linear_atXY(X,Y,c));
  34969. cimg_forC(U,c) {
  34970. const float
  34971. Ux = 0.5f*(U(_n1x,y,c) - U(_p1x,y,c)),
  34972. Uy = 0.5f*(U(x,_n1y,c) - U(x,_p1y,c)),
  34973. N2 = Ux*Ux + Uy*Uy,
  34974. N = std::sqrt(N2),
  34975. N3 = 1e-5f + N2*N,
  34976. coef_a = Uy*Uy/N3,
  34977. coef_b = -2*Ux*Uy/N3,
  34978. coef_c = Ux*Ux/N3,
  34979. Uxx = U(_n1x,y,c) + U(_p1x,y,c),
  34980. Uyy = U(x,_n1y,c) + U(x,_p1y,c),
  34981. Uxy = 0.25f*(U(_n1x,_n1y,c) + U(_p1x,_p1y,c) - U(_n1x,_p1y,c) - U(_n1x,_p1y,c));
  34982. U(x,y,c) = (float)(U(x,y,c) + dt*(delta_I*dI[c]._linear_atXY(X,Y) +
  34983. nsmoothness*( coef_a*Uxx + coef_b*Uxy + coef_c*Uyy )))/
  34984. (1 + 2*(coef_a + coef_c)*nsmoothness*dt);
  34985. _energy_regul+=N;
  34986. }
  34987. if (is_backward) { // Constraint displacement vectors to stay in image.
  34988. if (U(x,y,0)>x) U(x,y,0) = (float)x;
  34989. if (U(x,y,1)>y) U(x,y,1) = (float)y;
  34990. bound = (float)x - _width; if (U(x,y,0)<=bound) U(x,y,0) = bound;
  34991. bound = (float)y - _height; if (U(x,y,1)<=bound) U(x,y,1) = bound;
  34992. } else {
  34993. if (U(x,y,0)<-x) U(x,y,0) = -(float)x;
  34994. if (U(x,y,1)<-y) U(x,y,1) = -(float)y;
  34995. bound = (float)_width - x; if (U(x,y,0)>=bound) U(x,y,0) = bound;
  34996. bound = (float)_height - y; if (U(x,y,1)>=bound) U(x,y,1) = bound;
  34997. }
  34998. _energy+=delta_I*delta_I + nsmoothness*_energy_regul;
  34999. }
  35000. if (V) cimg_forX(V,x) if (V(x,y,2)) { // Apply constraints.
  35001. U(x,y,0) = V(x,y,0)/factor;
  35002. U(x,y,1) = V(x,y,1)/factor;
  35003. }
  35004. }
  35005. }
  35006. }
  35007. const float d_energy = (_energy - energy)/(sw*sh*sd);
  35008. if (d_energy<=0 && -d_energy<_precision) break;
  35009. if (d_energy>0) dt*=0.5f;
  35010. energy = _energy;
  35011. }
  35012. }
  35013. return U;
  35014. }
  35015. //! Compute correspondence map between two images, using the patch-match algorithm.
  35016. /**
  35017. \param patch_image The image containing the reference patches to match with the instance image.
  35018. \param patch_width Width of the patch used for matching.
  35019. \param patch_height Height of the patch used for matching.
  35020. \param patch_depth Depth of the patch used for matching.
  35021. \param nb_iterations Number of patch-match iterations.
  35022. \param nb_randoms Number of randomization attempts (per pixel).
  35023. \param guide Image used as the initial correspondence estimate for the algorithm.
  35024. 'guide' may have a last channel with boolean values (0=false | other=true) that
  35025. tells for each pixel if its correspondence vector is constrained to its initial value (constraint mask).
  35026. \param[out] matching_score Returned as the image of matching scores.
  35027. \note
  35028. The patch-match algorithm is described in this paper:
  35029. Connelly Barnes, Eli Shechtman, Adam Finkelstein, Dan B Goldman(2009),
  35030. PatchMatch: A Randomized Correspondence Algorithm for Structural Image Editing
  35031. **/
  35032. template<typename t1, typename t2>
  35033. CImg<T>& patchmatch(const CImg<T>& patch_image,
  35034. const unsigned int patch_width,
  35035. const unsigned int patch_height,
  35036. const unsigned int patch_depth,
  35037. const unsigned int nb_iterations,
  35038. const unsigned int nb_randoms,
  35039. const CImg<t1> &guide,
  35040. CImg<t2> &matching_score) {
  35041. return get_patchmatch(patch_image,patch_width,patch_height,patch_depth,
  35042. nb_iterations,nb_randoms,guide,matching_score).move_to(*this);
  35043. }
  35044. //! Compute correspondence map between two images, using the patch-match algorithm \newinstance.
  35045. template<typename t1, typename t2>
  35046. CImg<intT> get_patchmatch(const CImg<T>& patch_image,
  35047. const unsigned int patch_width,
  35048. const unsigned int patch_height,
  35049. const unsigned int patch_depth,
  35050. const unsigned int nb_iterations,
  35051. const unsigned int nb_randoms,
  35052. const CImg<t1> &guide,
  35053. CImg<t2> &matching_score) const {
  35054. return _patchmatch(patch_image,patch_width,patch_height,patch_depth,
  35055. nb_iterations,nb_randoms,
  35056. guide,true,matching_score);
  35057. }
  35058. //! Compute correspondence map between two images, using the patch-match algorithm \overloading.
  35059. template<typename t>
  35060. CImg<T>& patchmatch(const CImg<T>& patch_image,
  35061. const unsigned int patch_width,
  35062. const unsigned int patch_height,
  35063. const unsigned int patch_depth,
  35064. const unsigned int nb_iterations,
  35065. const unsigned int nb_randoms,
  35066. const CImg<t> &guide) {
  35067. return get_patchmatch(patch_image,patch_width,patch_height,patch_depth,
  35068. nb_iterations,nb_randoms,guide).move_to(*this);
  35069. }
  35070. //! Compute correspondence map between two images, using the patch-match algorithm \overloading.
  35071. template<typename t>
  35072. CImg<intT> get_patchmatch(const CImg<T>& patch_image,
  35073. const unsigned int patch_width,
  35074. const unsigned int patch_height,
  35075. const unsigned int patch_depth,
  35076. const unsigned int nb_iterations,
  35077. const unsigned int nb_randoms,
  35078. const CImg<t> &guide) const {
  35079. return _patchmatch(patch_image,patch_width,patch_height,patch_depth,
  35080. nb_iterations,nb_randoms,
  35081. guide,false,CImg<T>::empty());
  35082. }
  35083. //! Compute correspondence map between two images, using the patch-match algorithm \overloading.
  35084. CImg<T>& patchmatch(const CImg<T>& patch_image,
  35085. const unsigned int patch_width,
  35086. const unsigned int patch_height,
  35087. const unsigned int patch_depth=1,
  35088. const unsigned int nb_iterations=5,
  35089. const unsigned int nb_randoms=5) {
  35090. return get_patchmatch(patch_image,patch_width,patch_height,patch_depth,
  35091. nb_iterations,nb_randoms).move_to(*this);
  35092. }
  35093. //! Compute correspondence map between two images, using the patch-match algorithm \overloading.
  35094. CImg<intT> get_patchmatch(const CImg<T>& patch_image,
  35095. const unsigned int patch_width,
  35096. const unsigned int patch_height,
  35097. const unsigned int patch_depth=1,
  35098. const unsigned int nb_iterations=5,
  35099. const unsigned int nb_randoms=5) const {
  35100. return _patchmatch(patch_image,patch_width,patch_height,patch_depth,
  35101. nb_iterations,nb_randoms,
  35102. CImg<T>::const_empty(),
  35103. false,CImg<T>::empty());
  35104. }
  35105. template<typename t1, typename t2>
  35106. CImg<intT> _patchmatch(const CImg<T>& patch_image,
  35107. const unsigned int patch_width,
  35108. const unsigned int patch_height,
  35109. const unsigned int patch_depth,
  35110. const unsigned int nb_iterations,
  35111. const unsigned int nb_randoms,
  35112. const CImg<t1> &guide,
  35113. const bool is_matching_score,
  35114. CImg<t2> &matching_score) const {
  35115. if (is_empty()) return CImg<intT>::const_empty();
  35116. if (patch_image._spectrum!=_spectrum)
  35117. throw CImgArgumentException(_cimg_instance
  35118. "patchmatch(): Instance image and specified patch image (%u,%u,%u,%u,%p) "
  35119. "have different spectrums.",
  35120. cimg_instance,
  35121. patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum,
  35122. patch_image._data);
  35123. if (patch_width>_width || patch_height>_height || patch_depth>_depth)
  35124. throw CImgArgumentException(_cimg_instance
  35125. "patchmatch(): Specified patch size %ux%ux%u is bigger than the dimensions "
  35126. "of the instance image.",
  35127. cimg_instance,patch_width,patch_height,patch_depth);
  35128. if (patch_width>patch_image._width || patch_height>patch_image._height || patch_depth>patch_image._depth)
  35129. throw CImgArgumentException(_cimg_instance
  35130. "patchmatch(): Specified patch size %ux%ux%u is bigger than the dimensions "
  35131. "of the patch image image (%u,%u,%u,%u,%p).",
  35132. cimg_instance,patch_width,patch_height,patch_depth,
  35133. patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum,
  35134. patch_image._data);
  35135. const unsigned int
  35136. _constraint = patch_image._depth>1?3:2,
  35137. constraint = guide._spectrum>_constraint?_constraint:0;
  35138. if (guide &&
  35139. (guide._width!=_width || guide._height!=_height || guide._depth!=_depth || guide._spectrum<_constraint))
  35140. throw CImgArgumentException(_cimg_instance
  35141. "patchmatch(): Specified guide (%u,%u,%u,%u,%p) has invalid dimensions "
  35142. "considering instance and patch image image (%u,%u,%u,%u,%p).",
  35143. cimg_instance,
  35144. guide._width,guide._height,guide._depth,guide._spectrum,guide._data,
  35145. patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum,
  35146. patch_image._data);
  35147. CImg<intT> map(_width,_height,_depth,patch_image._depth>1?3:2);
  35148. CImg<floatT> score(_width,_height,_depth);
  35149. const int
  35150. psizew = (int)patch_width, psizew1 = psizew/2, psizew2 = psizew - psizew1 - 1,
  35151. psizeh = (int)patch_height, psizeh1 = psizeh/2, psizeh2 = psizeh - psizeh1 - 1,
  35152. psized = (int)patch_depth, psized1 = psized/2, psized2 = psized - psized1 - 1;
  35153. if (_depth>1 || patch_image._depth>1) { // 3d version.
  35154. // Initialize correspondence map.
  35155. if (guide) cimg_forXYZ(*this,x,y,z) { // User-defined initialization.
  35156. const int
  35157. cx1 = x<=psizew1?x:(x<width() - psizew2?psizew1:psizew + x - width()), cx2 = psizew - cx1 - 1,
  35158. cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
  35159. cz1 = z<=psized1?z:(z<depth() - psized2?psized1:psized + z - depth()), cz2 = psized - cz1 - 1,
  35160. u = std::min(std::max((int)guide(x,y,z,0),cx1),patch_image.width() - 1 - cx2),
  35161. v = std::min(std::max((int)guide(x,y,z,1),cy1),patch_image.height() - 1 - cy2),
  35162. w = std::min(std::max((int)guide(x,y,z,2),cz1),patch_image.depth() - 1 - cz2);
  35163. map(x,y,z,0) = u;
  35164. map(x,y,z,1) = v;
  35165. map(x,y,z,2) = w;
  35166. score(x,y,z) = _patchmatch(*this,patch_image,patch_width,patch_height,patch_depth,
  35167. x - cx1,y - cy1,z - cz1,
  35168. u - cx1,v - cy1,w - cz1,cimg::type<float>::inf());
  35169. } else cimg_forXYZ(*this,x,y,z) { // Random initialization.
  35170. const int
  35171. cx1 = x<=psizew1?x:(x<width() - psizew2?psizew1:psizew + x - width()), cx2 = psizew - cx1 - 1,
  35172. cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
  35173. cz1 = z<=psized1?z:(z<depth() - psized2?psized1:psized + z - depth()), cz2 = psized - cz1 - 1,
  35174. u = (int)cimg::round(cimg::rand(cx1,patch_image.width() - 1 - cx2)),
  35175. v = (int)cimg::round(cimg::rand(cy1,patch_image.height() - 1 - cy2)),
  35176. w = (int)cimg::round(cimg::rand(cz1,patch_image.depth() - 1 - cz2));
  35177. map(x,y,z,0) = u;
  35178. map(x,y,z,1) = v;
  35179. map(x,y,z,2) = w;
  35180. score(x,y,z) = _patchmatch(*this,patch_image,patch_width,patch_height,patch_depth,
  35181. x - cx1,y - cy1,z - cz1,
  35182. u - cx1,v - cy1,w - cz1,cimg::type<float>::inf());
  35183. }
  35184. // Start iteration loop.
  35185. cimg_abort_init;
  35186. for (unsigned int iter = 0; iter<nb_iterations; ++iter) {
  35187. cimg_abort_test;
  35188. const bool is_even = !(iter%2);
  35189. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>64 && iter<nb_iterations-2))
  35190. cimg_forXYZ(*this,X,Y,Z) {
  35191. const int
  35192. x = is_even?X:width() - 1 - X,
  35193. y = is_even?Y:height() - 1 - Y,
  35194. z = is_even?Z:depth() - 1 - Z;
  35195. if (score(x,y,z)<=1e-5 || (constraint && guide(x,y,z,constraint)!=0)) continue;
  35196. const int
  35197. cx1 = x<=psizew1?x:(x<width() - psizew2?psizew1:psizew + x - width()), cx2 = psizew - cx1 - 1,
  35198. cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
  35199. cz1 = z<=psized1?z:(z<depth() - psized2?psized1:psized + z - depth()), cz2 = psized - cz1 - 1,
  35200. xp = x - cx1,
  35201. yp = y - cy1,
  35202. zp = z - cz1;
  35203. // Propagation.
  35204. if (is_even) {
  35205. if (x>0) { // Compare with left neighbor.
  35206. const int u = map(x - 1,y,z,0), v = map(x - 1,y,z,1), w = map(x - 1,y,z,2);
  35207. if (u>=cx1 - 1 && u<patch_image.width() - 1 - cx2 &&
  35208. v>=cy1 && v<patch_image.height() - cy2 &&
  35209. w>=cz1 && w<patch_image.depth() - cz2) {
  35210. const float
  35211. current_score = score(x,y,z),
  35212. D = _patchmatch(*this,patch_image,patch_width,patch_height,patch_depth,
  35213. xp,yp,zp,u + 1 - cx1,v - cy1,w - cz1,current_score);
  35214. if (D<current_score) { score(x,y,z) = D; map(x,y,z,0) = u + 1; map(x,y,z,1) = v; map(x,y,z,2) = w; }
  35215. }
  35216. }
  35217. if (y>0) { // Compare with up neighbor.
  35218. const int u = map(x,y - 1,z,0), v = map(x,y - 1,z,1), w = map(x,y - 1,z,2);
  35219. if (u>=cx1 && u<patch_image.width() - cx2 &&
  35220. v>=cy1 - 1 && v<patch_image.height() - 1 - cy2 &&
  35221. w>=cz1 && w<patch_image.depth() - cx2) {
  35222. const float
  35223. current_score = score(x,y,z),
  35224. D = _patchmatch(*this,patch_image,patch_width,patch_height,patch_depth,
  35225. xp,yp,zp,u - cx1,v + 1 - cy1,w - cz1,current_score);
  35226. if (D<current_score) { score(x,y,z) = D; map(x,y,z,0) = u; map(x,y,z,1) = v + 1; map(x,y,z,2) = w; }
  35227. }
  35228. }
  35229. if (z>0) { // Compare with backward neighbor.
  35230. const int u = map(x,y,z - 1,0), v = map(x,y,z - 1,1), w = map(x,y,z - 1,2);
  35231. if (u>=cx1 && u<patch_image.width() - cx2 &&
  35232. v>=cy1 && v<patch_image.height() - cy2 &&
  35233. w>=cz1 - 1 && w<patch_image.depth() - 1 - cz2) {
  35234. const float
  35235. current_score = score(x,y,z),
  35236. D = _patchmatch(*this,patch_image,patch_width,patch_height,patch_depth,
  35237. xp,yp,zp,u - cx1,v - cy1,w + 1 - cz1,current_score);
  35238. if (D<current_score) { score(x,y,z) = D; map(x,y,z,0) = u; map(x,y,z,1) = v; map(x,y,z,2) = w + 1; }
  35239. }
  35240. }
  35241. } else {
  35242. if (x<width() - 1) { // Compare with right neighbor.
  35243. const int u = map(x + 1,y,z,0), v = map(x + 1,y,z,1), w = map(x + 1,y,z,2);
  35244. if (u>=cx1 + 1 && u<patch_image.width() + 1 - cx2 &&
  35245. v>=cy1 && v<patch_image.height() - cy2 &&
  35246. w>=cz1 && w<patch_image.depth() - cz2) {
  35247. const float
  35248. current_score = score(x,y,z),
  35249. D = _patchmatch(*this,patch_image,patch_width,patch_height,patch_depth,
  35250. xp,yp,zp,u - 1 - cx1,v - cy1,w - cz1,current_score);
  35251. if (D<current_score) { score(x,y,z) = D; map(x,y,z,0) = u - 1; map(x,y,z,1) = v; map(x,y,z,2) = w; }
  35252. }
  35253. }
  35254. if (y<height() - 1) { // Compare with bottom neighbor.
  35255. const int u = map(x,y + 1,z,0), v = map(x,y + 1,z,1), w = map(x,y + 1,z,2);
  35256. if (u>=cx1 && u<patch_image.width() - cx2 &&
  35257. v>=cy1 + 1 && v<patch_image.height() + 1 - cy2 &&
  35258. w>=cz1 && w<patch_image.depth() - cz2) {
  35259. const float
  35260. current_score = score(x,y,z),
  35261. D = _patchmatch(*this,patch_image,patch_width,patch_height,patch_depth,
  35262. xp,yp,zp,u - cx1,v - 1 - cy1,w - cz1,current_score);
  35263. if (D<current_score) { score(x,y,z) = D; map(x,y,z,0) = u; map(x,y,z,1) = v - 1; map(x,y,z,2) = w; }
  35264. }
  35265. }
  35266. if (z<depth() - 1) { // Compare with forward neighbor.
  35267. const int u = map(x,y,z + 1,0), v = map(x,y,z + 1,1), w = map(x,y,z + 1,2);
  35268. if (u>=cx1 && u<patch_image.width() - cx2 &&
  35269. v>=cy1 && v<patch_image.height() - cy2 &&
  35270. w>=cz1 + 1 && w<patch_image.depth() + 1 - cz2) {
  35271. const float
  35272. current_score = score(x,y,z),
  35273. D = _patchmatch(*this,patch_image,patch_width,patch_height,patch_depth,
  35274. xp,yp,zp,u - cx1,v - cy1,w - 1 - cz1,current_score);
  35275. if (D<current_score) { score(x,y,z) = D; map(x,y,z,0) = u; map(x,y,z,1) = v; map(x,y,z,2) = w - 1; }
  35276. }
  35277. }
  35278. }
  35279. // Randomization.
  35280. const int u = map(x,y,z,0), v = map(x,y,z,1), w = map(x,y,z,2);
  35281. float dw = (float)patch_image.width(), dh = (float)patch_image.height(), dd = (float)patch_image.depth();
  35282. for (unsigned int i = 0; i<nb_randoms; ++i) {
  35283. const int
  35284. ui = (int)cimg::round(cimg::rand(std::max((float)cx1,u - dw),
  35285. std::min(patch_image.width() - 1.0f - cx2,u + dw))),
  35286. vi = (int)cimg::round(cimg::rand(std::max((float)cy1,v - dh),
  35287. std::min(patch_image.height() - 1.0f - cy2,v + dh))),
  35288. wi = (int)cimg::round(cimg::rand(std::max((float)cz1,w - dd),
  35289. std::min(patch_image.depth() - 1.0f - cz2,w + dd)));
  35290. if (ui!=u || vi!=v || wi!=w) {
  35291. const float
  35292. current_score = score(x,y,z),
  35293. D = _patchmatch(*this,patch_image,patch_width,patch_height,patch_depth,
  35294. xp,yp,zp,ui - cx1,vi - cy1,wi - cz1,current_score);
  35295. if (D<current_score) { score(x,y,z) = D; map(x,y,z,0) = ui; map(x,y,z,1) = vi; map(x,y,z,2) = wi; }
  35296. dw = std::max(5.0f,dw*0.5f); dh = std::max(5.0f,dh*0.5f); dd = std::max(5.0f,dd*0.5f);
  35297. }
  35298. }
  35299. }
  35300. }
  35301. } else { // 2d version.
  35302. // Initialize correspondence map.
  35303. if (guide) cimg_forXY(*this,x,y) { // Random initialization.
  35304. const int
  35305. cx1 = x<=psizew1?x:(x<width() - psizew2?psizew1:psizew + x - width()), cx2 = psizew - cx1 - 1,
  35306. cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()) , cy2 = psizeh - cy1 - 1,
  35307. u = std::min(std::max((int)guide(x,y,0),cx1),patch_image.width() - 1 - cx2),
  35308. v = std::min(std::max((int)guide(x,y,1),cy1),patch_image.height() - 1 - cy2);
  35309. map(x,y,0) = u;
  35310. map(x,y,1) = v;
  35311. score(x,y) = _patchmatch(*this,patch_image,patch_width,patch_height,
  35312. x - cx1,y - cy1,u - cx1,v - cy1,cimg::type<float>::inf());
  35313. } else cimg_forXY(*this,x,y) { // Random initialization.
  35314. const int
  35315. cx1 = x<=psizew1?x:(x<width() - psizew2?psizew1:psizew + x - width()), cx2 = psizew - cx1 - 1,
  35316. cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()) , cy2 = psizeh - cy1 - 1,
  35317. u = (int)cimg::round(cimg::rand(cx1,patch_image.width() - 1 - cx2)),
  35318. v = (int)cimg::round(cimg::rand(cy1,patch_image.height() - 1 - cy2));
  35319. map(x,y,0) = u;
  35320. map(x,y,1) = v;
  35321. score(x,y) = _patchmatch(*this,patch_image,patch_width,patch_height,
  35322. x - cx1,y - cy1,u - cx1,v - cy1,cimg::type<float>::inf());
  35323. }
  35324. // Start iteration loop.
  35325. for (unsigned int iter = 0; iter<nb_iterations; ++iter) {
  35326. const bool is_even = !(iter%2);
  35327. cimg_pragma_openmp(parallel for cimg_openmp_if(_width>64 && iter<nb_iterations-2))
  35328. cimg_forXY(*this,X,Y) {
  35329. const int
  35330. x = is_even?X:width() - 1 - X,
  35331. y = is_even?Y:height() - 1 - Y;
  35332. if (score(x,y)<=1e-5 || (constraint && guide(x,y,constraint)!=0)) continue;
  35333. const int
  35334. cx1 = x<=psizew1?x:(x<width() - psizew2?psizew1:psizew + x - width()), cx2 = psizew - cx1 - 1,
  35335. cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()) , cy2 = psizeh - cy1 - 1,
  35336. xp = x - cx1,
  35337. yp = y - cy1;
  35338. // Propagation.
  35339. if (is_even) {
  35340. if (x>0) { // Compare with left neighbor.
  35341. const int u = map(x - 1,y,0), v = map(x - 1,y,1);
  35342. if (u>=cx1 - 1 && u<patch_image.width() - 1 - cx2 &&
  35343. v>=cy1 && v<patch_image.height() - cy2) {
  35344. const float
  35345. current_score = score(x,y),
  35346. D = _patchmatch(*this,patch_image,patch_width,patch_height,
  35347. xp,yp,u + 1 - cx1,v - cy1,current_score);
  35348. if (D<current_score) { score(x,y) = D; map(x,y,0) = u + 1; map(x,y,1) = v; }
  35349. }
  35350. }
  35351. if (y>0) { // Compare with up neighbor.
  35352. const int u = map(x,y - 1,0), v = map(x,y - 1,1);
  35353. if (u>=cx1 && u<patch_image.width() - cx2 &&
  35354. v>=cy1 - 1 && v<patch_image.height() - 1 - cy2) {
  35355. const float
  35356. current_score = score(x,y),
  35357. D = _patchmatch(*this,patch_image,patch_width,patch_height,
  35358. xp,yp,u - cx1,v + 1 - cy1,current_score);
  35359. if (D<current_score) { score(x,y) = D; map(x,y,0) = u; map(x,y,1) = v + 1; }
  35360. }
  35361. }
  35362. } else {
  35363. if (x<width() - 1) { // Compare with right neighbor.
  35364. const int u = map(x + 1,y,0), v = map(x + 1,y,1);
  35365. if (u>=cx1 + 1 && u<patch_image.width() + 1 - cx2 &&
  35366. v>=cy1 && v<patch_image.height() - cy2) {
  35367. const float
  35368. current_score = score(x,y),
  35369. D = _patchmatch(*this,patch_image,patch_width,patch_height,
  35370. xp,yp,u - 1 - cx1,v - cy1,current_score);
  35371. if (D<current_score) { score(x,y) = D; map(x,y,0) = u - 1; map(x,y,1) = v; }
  35372. }
  35373. }
  35374. if (y<height() - 1) { // Compare with bottom neighbor.
  35375. const int u = map(x,y + 1,0), v = map(x,y + 1,1);
  35376. if (u>=cx1 && u<patch_image.width() - cx2 &&
  35377. v>=cy1 + 1 && v<patch_image.height() + 1 - cy2) {
  35378. const float
  35379. current_score = score(x,y),
  35380. D = _patchmatch(*this,patch_image,patch_width,patch_height,
  35381. xp,yp,u - cx1,v - 1 - cy1,current_score);
  35382. if (D<current_score) { score(x,y) = D; map(x,y,0) = u; map(x,y,1) = v - 1; }
  35383. }
  35384. }
  35385. }
  35386. // Randomization.
  35387. const int u = map(x,y,0), v = map(x,y,1);
  35388. float dw = (float)patch_image.width(), dh = (float)patch_image.height();
  35389. for (unsigned int i = 0; i<nb_randoms; ++i) {
  35390. const int
  35391. ui = (int)cimg::round(cimg::rand(std::max((float)cx1,u - dw),
  35392. std::min(patch_image.width() - 1.0f - cx2,u + dw))),
  35393. vi = (int)cimg::round(cimg::rand(std::max((float)cy1,v - dh),
  35394. std::min(patch_image.height() - 1.0f - cy2,v + dh)));
  35395. if (ui!=u || vi!=v) {
  35396. const float
  35397. current_score = score(x,y),
  35398. D = _patchmatch(*this,patch_image,patch_width,patch_height,
  35399. xp,yp,ui - cx1,vi - cy1,current_score);
  35400. if (D<current_score) { score(x,y) = D; map(x,y,0) = ui; map(x,y,1) = vi; }
  35401. dw = std::max(5.0f,dw*0.5f); dh = std::max(5.0f,dh*0.5f);
  35402. }
  35403. }
  35404. }
  35405. }
  35406. }
  35407. if (is_matching_score) score.move_to(matching_score);
  35408. return map;
  35409. }
  35410. // Compute SSD between two patches in different images.
  35411. static float _patchmatch(const CImg<T>& img1, const CImg<T>& img2,
  35412. const unsigned int psizew, const unsigned int psizeh,
  35413. const int x1, const int y1,
  35414. const int x2, const int y2,
  35415. const float max_ssd) { // 2d version.
  35416. const T *p1 = img1.data(x1,y1), *p2 = img2.data(x2,y2);
  35417. const ulongT
  35418. offx1 = (ulongT)img1._width - psizew,
  35419. offx2 = (ulongT)img2._width - psizew,
  35420. offy1 = (ulongT)img1._width*img1._height - psizeh*img1._width,
  35421. offy2 = (ulongT)img2._width*img2._height - psizeh*img2._width;
  35422. float ssd = 0;
  35423. cimg_forC(img1,c) {
  35424. for (unsigned int j = 0; j<psizeh; ++j) {
  35425. for (unsigned int i = 0; i<psizew; ++i)
  35426. ssd += cimg::sqr(*(p1++) - *(p2++));
  35427. if (ssd>max_ssd) return max_ssd;
  35428. p1+=offx1; p2+=offx2;
  35429. }
  35430. p1+=offy1; p2+=offy2;
  35431. }
  35432. return ssd;
  35433. }
  35434. static float _patchmatch(const CImg<T>& img1, const CImg<T>& img2,
  35435. const unsigned int psizew, const unsigned int psizeh, const unsigned int psized,
  35436. const int x1, const int y1, const int z1,
  35437. const int x2, const int y2, const int z2,
  35438. const float max_ssd) { // 3d version.
  35439. const T *p1 = img1.data(x1,y1,z1), *p2 = img2.data(x2,y2,z2);
  35440. const ulongT
  35441. offx1 = (ulongT)img1._width - psizew,
  35442. offx2 = (ulongT)img2._width - psizew,
  35443. offy1 = (ulongT)img1._width*img1._height - psizeh*img1._width - psizew,
  35444. offy2 = (ulongT)img2._width*img2._height - psizeh*img2._width - psizew,
  35445. offz1 = (ulongT)img1._width*img1._height*img1._depth - psized*img1._width*img1._height -
  35446. psizeh*img1._width - psizew,
  35447. offz2 = (ulongT)img2._width*img2._height*img2._depth - psized*img2._width*img2._height -
  35448. psizeh*img2._width - psizew;
  35449. float ssd = 0;
  35450. cimg_forC(img1,c) {
  35451. for (unsigned int k = 0; k<psized; ++k) {
  35452. for (unsigned int j = 0; j<psizeh; ++j) {
  35453. for (unsigned int i = 0; i<psizew; ++i)
  35454. ssd += cimg::sqr(*(p1++) - *(p2++));
  35455. if (ssd>max_ssd) return max_ssd;
  35456. p1+=offx1; p2+=offx2;
  35457. }
  35458. p1+=offy1; p2+=offy2;
  35459. }
  35460. p1+=offz1; p2+=offz2;
  35461. }
  35462. return ssd;
  35463. }
  35464. //! Compute Euclidean distance function to a specified value.
  35465. /**
  35466. \param value Reference value.
  35467. \param metric Type of metric. Can be <tt>{ 0=Chebyshev | 1=Manhattan | 2=Euclidean | 3=Squared-euclidean }</tt>.
  35468. \note
  35469. The distance transform implementation has been submitted by A. Meijster, and implements
  35470. the article 'W.H. Hesselink, A. Meijster, J.B.T.M. Roerdink,
  35471. "A general algorithm for computing distance transforms in linear time.",
  35472. In: Mathematical Morphology and its Applications to Image and Signal Processing,
  35473. J. Goutsias, L. Vincent, and D.S. Bloomberg (eds.), Kluwer, 2000, pp. 331-340.'
  35474. The submitted code has then been modified to fit CImg coding style and constraints.
  35475. **/
  35476. CImg<T>& distance(const T& value, const unsigned int metric=2) {
  35477. if (is_empty()) return *this;
  35478. if (cimg::type<Tint>::string()!=cimg::type<T>::string()) // For datatype < int.
  35479. return CImg<Tint>(*this,false).distance((Tint)value,metric).
  35480. cut((Tint)cimg::type<T>::min(),(Tint)cimg::type<T>::max()).move_to(*this);
  35481. bool is_value = false;
  35482. cimg_for(*this,ptr,T) *ptr = *ptr==value?is_value=true,(T)0:(T)std::max(0,99999999); // (avoid VC++ warning)
  35483. if (!is_value) return fill(cimg::type<T>::max());
  35484. switch (metric) {
  35485. case 0 : return _distance_core(_distance_sep_cdt,_distance_dist_cdt); // Chebyshev.
  35486. case 1 : return _distance_core(_distance_sep_mdt,_distance_dist_mdt); // Manhattan.
  35487. case 3 : return _distance_core(_distance_sep_edt,_distance_dist_edt); // Squared Euclidean.
  35488. default : return _distance_core(_distance_sep_edt,_distance_dist_edt).sqrt(); // Euclidean.
  35489. }
  35490. return *this;
  35491. }
  35492. //! Compute distance to a specified value \newinstance.
  35493. CImg<Tfloat> get_distance(const T& value, const unsigned int metric=2) const {
  35494. return CImg<Tfloat>(*this,false).distance((Tfloat)value,metric);
  35495. }
  35496. static longT _distance_sep_edt(const longT i, const longT u, const longT *const g) {
  35497. return (u*u - i*i + g[u] - g[i])/(2*(u - i));
  35498. }
  35499. static longT _distance_dist_edt(const longT x, const longT i, const longT *const g) {
  35500. return (x - i)*(x - i) + g[i];
  35501. }
  35502. static longT _distance_sep_mdt(const longT i, const longT u, const longT *const g) {
  35503. return (u - i<=g[u] - g[i]?999999999:(g[u] - g[i] + u + i)/2);
  35504. }
  35505. static longT _distance_dist_mdt(const longT x, const longT i, const longT *const g) {
  35506. return (x<i?i - x:x - i) + g[i];
  35507. }
  35508. static longT _distance_sep_cdt(const longT i, const longT u, const longT *const g) {
  35509. const longT h = (i + u)/2;
  35510. if (g[i]<=g[u]) { return h<i + g[u]?i + g[u]:h; }
  35511. return h<u - g[i]?h:u - g[i];
  35512. }
  35513. static longT _distance_dist_cdt(const longT x, const longT i, const longT *const g) {
  35514. const longT d = x<i?i - x:x - i;
  35515. return d<g[i]?g[i]:d;
  35516. }
  35517. static void _distance_scan(const unsigned int len,
  35518. const longT *const g,
  35519. longT (*const sep)(const longT, const longT, const longT *const),
  35520. longT (*const f)(const longT, const longT, const longT *const),
  35521. longT *const s,
  35522. longT *const t,
  35523. longT *const dt) {
  35524. longT q = s[0] = t[0] = 0;
  35525. for (int u = 1; u<(int)len; ++u) { // Forward scan.
  35526. while ((q>=0) && f(t[q],s[q],g)>f(t[q],u,g)) { --q; }
  35527. if (q<0) { q = 0; s[0] = u; }
  35528. else { const longT w = 1 + sep(s[q], u, g); if (w<(longT)len) { ++q; s[q] = u; t[q] = w; }}
  35529. }
  35530. for (int u = (int)len - 1; u>=0; --u) { dt[u] = f(u,s[q],g); if (u==t[q]) --q; } // Backward scan.
  35531. }
  35532. CImg<T>& _distance_core(longT (*const sep)(const longT, const longT, const longT *const),
  35533. longT (*const f)(const longT, const longT, const longT *const)) {
  35534. // Check for g++ 4.9.X, as OpenMP seems to crash for this particular function. I have no clues why.
  35535. #define cimg_is_gcc49x (__GNUC__==4 && __GNUC_MINOR__==9)
  35536. const ulongT wh = (ulongT)_width*_height;
  35537. #if defined(cimg_use_openmp) && !cimg_is_gcc49x
  35538. cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
  35539. #endif
  35540. cimg_forC(*this,c) {
  35541. CImg<longT> g(_width), dt(_width), s(_width), t(_width);
  35542. CImg<T> img = get_shared_channel(c);
  35543. #if defined(cimg_use_openmp) && !cimg_is_gcc49x
  35544. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=512 && _height*_depth>=16)
  35545. firstprivate(g,dt,s,t))
  35546. #endif
  35547. cimg_forYZ(*this,y,z) { // Over X-direction.
  35548. cimg_forX(*this,x) g[x] = (longT)img(x,y,z,0,wh);
  35549. _distance_scan(_width,g,sep,f,s,t,dt);
  35550. cimg_forX(*this,x) img(x,y,z,0,wh) = (T)dt[x];
  35551. }
  35552. if (_height>1) {
  35553. g.assign(_height); dt.assign(_height); s.assign(_height); t.assign(_height);
  35554. #if defined(cimg_use_openmp) && !cimg_is_gcc49x
  35555. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_height>=512 && _width*_depth>=16)
  35556. firstprivate(g,dt,s,t))
  35557. #endif
  35558. cimg_forXZ(*this,x,z) { // Over Y-direction.
  35559. cimg_forY(*this,y) g[y] = (longT)img(x,y,z,0,wh);
  35560. _distance_scan(_height,g,sep,f,s,t,dt);
  35561. cimg_forY(*this,y) img(x,y,z,0,wh) = (T)dt[y];
  35562. }
  35563. }
  35564. if (_depth>1) {
  35565. g.assign(_depth); dt.assign(_depth); s.assign(_depth); t.assign(_depth);
  35566. #if defined(cimg_use_openmp) && !cimg_is_gcc49x
  35567. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_depth>=512 && _width*_height>=16)
  35568. firstprivate(g,dt,s,t))
  35569. #endif
  35570. cimg_forXY(*this,x,y) { // Over Z-direction.
  35571. cimg_forZ(*this,z) g[z] = (longT)img(x,y,z,0,wh);
  35572. _distance_scan(_depth,g,sep,f,s,t,dt);
  35573. cimg_forZ(*this,z) img(x,y,z,0,wh) = (T)dt[z];
  35574. }
  35575. }
  35576. }
  35577. return *this;
  35578. }
  35579. //! Compute chamfer distance to a specified value, with a custom metric.
  35580. /**
  35581. \param value Reference value.
  35582. \param metric_mask Metric mask.
  35583. \note The algorithm code has been initially proposed by A. Meijster, and modified by D. Tschumperlé.
  35584. **/
  35585. template<typename t>
  35586. CImg<T>& distance(const T& value, const CImg<t>& metric_mask) {
  35587. if (is_empty()) return *this;
  35588. bool is_value = false;
  35589. cimg_for(*this,ptr,T) *ptr = *ptr==value?is_value=true,0:(T)999999999;
  35590. if (!is_value) return fill(cimg::type<T>::max());
  35591. const ulongT wh = (ulongT)_width*_height;
  35592. cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
  35593. cimg_forC(*this,c) {
  35594. CImg<T> img = get_shared_channel(c);
  35595. cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width*_height*_depth>=1024))
  35596. cimg_forXYZ(metric_mask,dx,dy,dz) {
  35597. const t weight = metric_mask(dx,dy,dz);
  35598. if (weight) {
  35599. for (int z = dz, nz = 0; z<depth(); ++z,++nz) { // Forward scan.
  35600. for (int y = dy , ny = 0; y<height(); ++y,++ny) {
  35601. for (int x = dx, nx = 0; x<width(); ++x,++nx) {
  35602. const T dd = img(nx,ny,nz,0,wh) + weight;
  35603. if (dd<img(x,y,z,0,wh)) img(x,y,z,0,wh) = dd;
  35604. }
  35605. }
  35606. }
  35607. for (int z = depth() - 1 - dz, nz = depth() - 1; z>=0; --z,--nz) { // Backward scan.
  35608. for (int y = height() - 1 - dy, ny = height() - 1; y>=0; --y,--ny) {
  35609. for (int x = width() - 1 - dx, nx = width() - 1; x>=0; --x,--nx) {
  35610. const T dd = img(nx,ny,nz,0,wh) + weight;
  35611. if (dd<img(x,y,z,0,wh)) img(x,y,z,0,wh) = dd;
  35612. }
  35613. }
  35614. }
  35615. }
  35616. }
  35617. }
  35618. return *this;
  35619. }
  35620. //! Compute chamfer distance to a specified value, with a custom metric \newinstance.
  35621. template<typename t>
  35622. CImg<Tfloat> get_distance(const T& value, const CImg<t>& metric_mask) const {
  35623. return CImg<Tfloat>(*this,false).distance(value,metric_mask);
  35624. }
  35625. //! Compute distance to a specified value, according to a custom metric (use dijkstra algorithm).
  35626. /**
  35627. \param value Reference value.
  35628. \param metric Field of distance potentials.
  35629. \param is_high_connectivity Tells if the algorithm uses low or high connectivity.
  35630. \param[out] return_path An image containing the nodes of the minimal path.
  35631. **/
  35632. template<typename t, typename to>
  35633. CImg<T>& distance_dijkstra(const T& value, const CImg<t>& metric, const bool is_high_connectivity,
  35634. CImg<to>& return_path) {
  35635. return get_distance_dijkstra(value,metric,is_high_connectivity,return_path).move_to(*this);
  35636. }
  35637. //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm) \newinstance.
  35638. template<typename t, typename to>
  35639. CImg<typename cimg::superset<t,long>::type>
  35640. get_distance_dijkstra(const T& value, const CImg<t>& metric, const bool is_high_connectivity,
  35641. CImg<to>& return_path) const {
  35642. if (is_empty()) return return_path.assign();
  35643. if (!is_sameXYZ(metric))
  35644. throw CImgArgumentException(_cimg_instance
  35645. "distance_dijkstra(): image instance and metric map (%u,%u,%u,%u) "
  35646. "have incompatible dimensions.",
  35647. cimg_instance,
  35648. metric._width,metric._height,metric._depth,metric._spectrum);
  35649. typedef typename cimg::superset<t,long>::type td; // Type used for computing cumulative distances.
  35650. CImg<td> result(_width,_height,_depth,_spectrum), Q;
  35651. CImg<boolT> is_queued(_width,_height,_depth,1);
  35652. if (return_path) return_path.assign(_width,_height,_depth,_spectrum);
  35653. cimg_forC(*this,c) {
  35654. const CImg<T> img = get_shared_channel(c);
  35655. const CImg<t> met = metric.get_shared_channel(c%metric._spectrum);
  35656. CImg<td> res = result.get_shared_channel(c);
  35657. CImg<to> path = return_path?return_path.get_shared_channel(c):CImg<to>();
  35658. unsigned int sizeQ = 0;
  35659. // Detect initial seeds.
  35660. is_queued.fill(0);
  35661. cimg_forXYZ(img,x,y,z) if (img(x,y,z)==value) {
  35662. Q._priority_queue_insert(is_queued,sizeQ,0,x,y,z);
  35663. res(x,y,z) = 0;
  35664. if (path) path(x,y,z) = (to)0;
  35665. }
  35666. // Start distance propagation.
  35667. while (sizeQ) {
  35668. // Get and remove point with minimal potential from the queue.
  35669. const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3);
  35670. const td P = (td)-Q(0,0);
  35671. Q._priority_queue_remove(sizeQ);
  35672. // Update neighbors.
  35673. td npot = 0;
  35674. if (x - 1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x - 1,y,z) + P),x - 1,y,z)) {
  35675. res(x - 1,y,z) = npot; if (path) path(x - 1,y,z) = (to)2;
  35676. }
  35677. if (x + 1<width() && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x + 1,y,z) + P),x + 1,y,z)) {
  35678. res(x + 1,y,z) = npot; if (path) path(x + 1,y,z) = (to)1;
  35679. }
  35680. if (y - 1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y - 1,z) + P),x,y - 1,z)) {
  35681. res(x,y - 1,z) = npot; if (path) path(x,y - 1,z) = (to)8;
  35682. }
  35683. if (y + 1<height() && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y + 1,z) + P),x,y + 1,z)) {
  35684. res(x,y + 1,z) = npot; if (path) path(x,y + 1,z) = (to)4;
  35685. }
  35686. if (z - 1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y,z - 1) + P),x,y,z - 1)) {
  35687. res(x,y,z - 1) = npot; if (path) path(x,y,z - 1) = (to)32;
  35688. }
  35689. if (z + 1<depth() && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y,z + 1) + P),x,y,z + 1)) {
  35690. res(x,y,z + 1) = npot; if (path) path(x,y,z + 1) = (to)16;
  35691. }
  35692. if (is_high_connectivity) {
  35693. const float sqrt2 = std::sqrt(2.0f), sqrt3 = std::sqrt(3.0f);
  35694. // Diagonal neighbors on slice z.
  35695. if (x - 1>=0 && y - 1>=0 &&
  35696. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y - 1,z) + P)),x - 1,y - 1,z)) {
  35697. res(x - 1,y - 1,z) = npot; if (path) path(x - 1,y - 1,z) = (to)10;
  35698. }
  35699. if (x + 1<width() && y - 1>=0 &&
  35700. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y - 1,z) + P)),x + 1,y - 1,z)) {
  35701. res(x + 1,y - 1,z) = npot; if (path) path(x + 1,y - 1,z) = (to)9;
  35702. }
  35703. if (x - 1>=0 && y + 1<height() &&
  35704. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y + 1,z) + P)),x - 1,y + 1,z)) {
  35705. res(x - 1,y + 1,z) = npot; if (path) path(x - 1,y + 1,z) = (to)6;
  35706. }
  35707. if (x + 1<width() && y + 1<height() &&
  35708. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y + 1,z) + P)),x + 1,y + 1,z)) {
  35709. res(x + 1,y + 1,z) = npot; if (path) path(x + 1,y + 1,z) = (to)5;
  35710. }
  35711. if (z - 1>=0) { // Diagonal neighbors on slice z - 1.
  35712. if (x - 1>=0 &&
  35713. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y,z - 1) + P)),x - 1,y,z - 1)) {
  35714. res(x - 1,y,z - 1) = npot; if (path) path(x - 1,y,z - 1) = (to)34;
  35715. }
  35716. if (x + 1<width() &&
  35717. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y,z - 1) + P)),x + 1,y,z - 1)) {
  35718. res(x + 1,y,z - 1) = npot; if (path) path(x + 1,y,z - 1) = (to)33;
  35719. }
  35720. if (y - 1>=0 &&
  35721. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y - 1,z - 1) + P)),x,y - 1,z - 1)) {
  35722. res(x,y - 1,z - 1) = npot; if (path) path(x,y - 1,z - 1) = (to)40;
  35723. }
  35724. if (y + 1<height() &&
  35725. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y + 1,z - 1) + P)),x,y + 1,z - 1)) {
  35726. res(x,y + 1,z - 1) = npot; if (path) path(x,y + 1,z - 1) = (to)36;
  35727. }
  35728. if (x - 1>=0 && y - 1>=0 &&
  35729. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y - 1,z - 1) + P)),
  35730. x - 1,y - 1,z - 1)) {
  35731. res(x - 1,y - 1,z - 1) = npot; if (path) path(x - 1,y - 1,z - 1) = (to)42;
  35732. }
  35733. if (x + 1<width() && y - 1>=0 &&
  35734. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y - 1,z - 1) + P)),
  35735. x + 1,y - 1,z - 1)) {
  35736. res(x + 1,y - 1,z - 1) = npot; if (path) path(x + 1,y - 1,z - 1) = (to)41;
  35737. }
  35738. if (x - 1>=0 && y + 1<height() &&
  35739. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y + 1,z - 1) + P)),
  35740. x - 1,y + 1,z - 1)) {
  35741. res(x - 1,y + 1,z - 1) = npot; if (path) path(x - 1,y + 1,z - 1) = (to)38;
  35742. }
  35743. if (x + 1<width() && y + 1<height() &&
  35744. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y + 1,z - 1) + P)),
  35745. x + 1,y + 1,z - 1)) {
  35746. res(x + 1,y + 1,z - 1) = npot; if (path) path(x + 1,y + 1,z - 1) = (to)37;
  35747. }
  35748. }
  35749. if (z + 1<depth()) { // Diagonal neighbors on slice z + 1.
  35750. if (x - 1>=0 &&
  35751. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y,z + 1) + P)),x - 1,y,z + 1)) {
  35752. res(x - 1,y,z + 1) = npot; if (path) path(x - 1,y,z + 1) = (to)18;
  35753. }
  35754. if (x + 1<width() &&
  35755. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y,z + 1) + P)),x + 1,y,z + 1)) {
  35756. res(x + 1,y,z + 1) = npot; if (path) path(x + 1,y,z + 1) = (to)17;
  35757. }
  35758. if (y - 1>=0 &&
  35759. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y - 1,z + 1) + P)),x,y - 1,z + 1)) {
  35760. res(x,y - 1,z + 1) = npot; if (path) path(x,y - 1,z + 1) = (to)24;
  35761. }
  35762. if (y + 1<height() &&
  35763. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y + 1,z + 1) + P)),x,y + 1,z + 1)) {
  35764. res(x,y + 1,z + 1) = npot; if (path) path(x,y + 1,z + 1) = (to)20;
  35765. }
  35766. if (x - 1>=0 && y - 1>=0 &&
  35767. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y - 1,z + 1) + P)),
  35768. x - 1,y - 1,z + 1)) {
  35769. res(x - 1,y - 1,z + 1) = npot; if (path) path(x - 1,y - 1,z + 1) = (to)26;
  35770. }
  35771. if (x + 1<width() && y - 1>=0 &&
  35772. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y - 1,z + 1) + P)),
  35773. x + 1,y - 1,z + 1)) {
  35774. res(x + 1,y - 1,z + 1) = npot; if (path) path(x + 1,y - 1,z + 1) = (to)25;
  35775. }
  35776. if (x - 1>=0 && y + 1<height() &&
  35777. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y + 1,z + 1) + P)),
  35778. x - 1,y + 1,z + 1)) {
  35779. res(x - 1,y + 1,z + 1) = npot; if (path) path(x - 1,y + 1,z + 1) = (to)22;
  35780. }
  35781. if (x + 1<width() && y + 1<height() &&
  35782. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y + 1,z + 1) + P)),
  35783. x + 1,y + 1,z + 1)) {
  35784. res(x + 1,y + 1,z + 1) = npot; if (path) path(x + 1,y + 1,z + 1) = (to)21;
  35785. }
  35786. }
  35787. }
  35788. }
  35789. }
  35790. return result;
  35791. }
  35792. //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm). \overloading.
  35793. template<typename t>
  35794. CImg<T>& distance_dijkstra(const T& value, const CImg<t>& metric,
  35795. const bool is_high_connectivity=false) {
  35796. return get_distance_dijkstra(value,metric,is_high_connectivity).move_to(*this);
  35797. }
  35798. //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm). \newinstance.
  35799. template<typename t>
  35800. CImg<Tfloat> get_distance_dijkstra(const T& value, const CImg<t>& metric,
  35801. const bool is_high_connectivity=false) const {
  35802. CImg<T> return_path;
  35803. return get_distance_dijkstra(value,metric,is_high_connectivity,return_path);
  35804. }
  35805. //! Compute distance map to one source point, according to a custom metric (use fast marching algorithm).
  35806. /**
  35807. \param value Reference value.
  35808. \param metric Field of distance potentials.
  35809. **/
  35810. template<typename t>
  35811. CImg<T>& distance_eikonal(const T& value, const CImg<t>& metric) {
  35812. return get_distance_eikonal(value,metric).move_to(*this);
  35813. }
  35814. //! Compute distance map to one source point, according to a custom metric (use fast marching algorithm).
  35815. template<typename t>
  35816. CImg<Tfloat> get_distance_eikonal(const T& value, const CImg<t>& metric) const {
  35817. if (is_empty()) return *this;
  35818. if (!is_sameXYZ(metric))
  35819. throw CImgArgumentException(_cimg_instance
  35820. "distance_eikonal(): image instance and metric map (%u,%u,%u,%u) have "
  35821. "incompatible dimensions.",
  35822. cimg_instance,
  35823. metric._width,metric._height,metric._depth,metric._spectrum);
  35824. CImg<Tfloat> result(_width,_height,_depth,_spectrum,cimg::type<Tfloat>::max()), Q;
  35825. CImg<charT> state(_width,_height,_depth); // -1=far away, 0=narrow, 1=frozen.
  35826. cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2) firstprivate(Q,state))
  35827. cimg_forC(*this,c) {
  35828. const CImg<T> img = get_shared_channel(c);
  35829. const CImg<t> met = metric.get_shared_channel(c%metric._spectrum);
  35830. CImg<Tfloat> res = result.get_shared_channel(c);
  35831. unsigned int sizeQ = 0;
  35832. state.fill(-1);
  35833. // Detect initial seeds.
  35834. Tfloat *ptr1 = res._data; char *ptr2 = state._data;
  35835. cimg_for(img,ptr0,T) { if (*ptr0==value) { *ptr1 = 0; *ptr2 = 1; } ++ptr1; ++ptr2; }
  35836. // Initialize seeds neighbors.
  35837. ptr2 = state._data;
  35838. cimg_forXYZ(img,x,y,z) if (*(ptr2++)==1) {
  35839. if (x - 1>=0 && state(x - 1,y,z)==-1) {
  35840. const Tfloat dist = res(x - 1,y,z) = __distance_eikonal(res,met(x - 1,y,z),x - 1,y,z);
  35841. Q._eik_priority_queue_insert(state,sizeQ,-dist,x - 1,y,z);
  35842. }
  35843. if (x + 1<width() && state(x + 1,y,z)==-1) {
  35844. const Tfloat dist = res(x + 1,y,z) = __distance_eikonal(res,met(x + 1,y,z),x + 1,y,z);
  35845. Q._eik_priority_queue_insert(state,sizeQ,-dist,x + 1,y,z);
  35846. }
  35847. if (y - 1>=0 && state(x,y - 1,z)==-1) {
  35848. const Tfloat dist = res(x,y - 1,z) = __distance_eikonal(res,met(x,y - 1,z),x,y - 1,z);
  35849. Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y - 1,z);
  35850. }
  35851. if (y + 1<height() && state(x,y + 1,z)==-1) {
  35852. const Tfloat dist = res(x,y + 1,z) = __distance_eikonal(res,met(x,y + 1,z),x,y + 1,z);
  35853. Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y + 1,z);
  35854. }
  35855. if (z - 1>=0 && state(x,y,z - 1)==-1) {
  35856. const Tfloat dist = res(x,y,z - 1) = __distance_eikonal(res,met(x,y,z - 1),x,y,z - 1);
  35857. Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z - 1);
  35858. }
  35859. if (z + 1<depth() && state(x,y,z + 1)==-1) {
  35860. const Tfloat dist = res(x,y,z + 1) = __distance_eikonal(res,met(x,y,z + 1),x,y,z + 1);
  35861. Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z + 1);
  35862. }
  35863. }
  35864. // Propagate front.
  35865. while (sizeQ) {
  35866. int x = -1, y = -1, z = -1;
  35867. while (sizeQ && x<0) {
  35868. x = (int)Q(0,1); y = (int)Q(0,2); z = (int)Q(0,3);
  35869. Q._priority_queue_remove(sizeQ);
  35870. if (state(x,y,z)==1) x = -1; else state(x,y,z) = 1;
  35871. }
  35872. if (x>=0) {
  35873. if (x - 1>=0 && state(x - 1,y,z)!=1) {
  35874. const Tfloat dist = __distance_eikonal(res,met(x - 1,y,z),x - 1,y,z);
  35875. if (dist<res(x - 1,y,z)) {
  35876. res(x - 1,y,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x - 1,y,z);
  35877. }
  35878. }
  35879. if (x + 1<width() && state(x + 1,y,z)!=1) {
  35880. const Tfloat dist = __distance_eikonal(res,met(x + 1,y,z),x + 1,y,z);
  35881. if (dist<res(x + 1,y,z)) {
  35882. res(x + 1,y,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x + 1,y,z);
  35883. }
  35884. }
  35885. if (y - 1>=0 && state(x,y - 1,z)!=1) {
  35886. const Tfloat dist = __distance_eikonal(res,met(x,y - 1,z),x,y - 1,z);
  35887. if (dist<res(x,y - 1,z)) {
  35888. res(x,y - 1,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y - 1,z);
  35889. }
  35890. }
  35891. if (y + 1<height() && state(x,y + 1,z)!=1) {
  35892. const Tfloat dist = __distance_eikonal(res,met(x,y + 1,z),x,y + 1,z);
  35893. if (dist<res(x,y + 1,z)) {
  35894. res(x,y + 1,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y + 1,z);
  35895. }
  35896. }
  35897. if (z - 1>=0 && state(x,y,z - 1)!=1) {
  35898. const Tfloat dist = __distance_eikonal(res,met(x,y,z - 1),x,y,z - 1);
  35899. if (dist<res(x,y,z - 1)) {
  35900. res(x,y,z - 1) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z - 1);
  35901. }
  35902. }
  35903. if (z + 1<depth() && state(x,y,z + 1)!=1) {
  35904. const Tfloat dist = __distance_eikonal(res,met(x,y,z + 1),x,y,z + 1);
  35905. if (dist<res(x,y,z + 1)) {
  35906. res(x,y,z + 1) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z + 1);
  35907. }
  35908. }
  35909. }
  35910. }
  35911. }
  35912. return result;
  35913. }
  35914. // Locally solve eikonal equation.
  35915. Tfloat __distance_eikonal(const CImg<Tfloat>& res, const Tfloat P,
  35916. const int x=0, const int y=0, const int z=0) const {
  35917. const Tfloat M = (Tfloat)cimg::type<T>::max();
  35918. T T1 = (T)std::min(x - 1>=0?res(x - 1,y,z):M,x + 1<width()?res(x + 1,y,z):M);
  35919. Tfloat root = 0;
  35920. if (_depth>1) { // 3d.
  35921. T
  35922. T2 = (T)std::min(y - 1>=0?res(x,y - 1,z):M,y + 1<height()?res(x,y + 1,z):M),
  35923. T3 = (T)std::min(z - 1>=0?res(x,y,z - 1):M,z + 1<depth()?res(x,y,z + 1):M);
  35924. if (T1>T2) cimg::swap(T1,T2);
  35925. if (T2>T3) cimg::swap(T2,T3);
  35926. if (T1>T2) cimg::swap(T1,T2);
  35927. if (P<=0) return (Tfloat)T1;
  35928. if (T3<M && ___distance_eikonal(3,-2*(T1 + T2 + T3),T1*T1 + T2*T2 + T3*T3 - P*P,root))
  35929. return std::max((Tfloat)T3,root);
  35930. if (T2<M && ___distance_eikonal(2,-2*(T1 + T2),T1*T1 + T2*T2 - P*P,root))
  35931. return std::max((Tfloat)T2,root);
  35932. return P + T1;
  35933. } else if (_height>1) { // 2d.
  35934. T T2 = (T)std::min(y - 1>=0?res(x,y - 1,z):M,y + 1<height()?res(x,y + 1,z):M);
  35935. if (T1>T2) cimg::swap(T1,T2);
  35936. if (P<=0) return (Tfloat)T1;
  35937. if (T2<M && ___distance_eikonal(2,-2*(T1 + T2),T1*T1 + T2*T2 - P*P,root))
  35938. return std::max((Tfloat)T2,root);
  35939. return P + T1;
  35940. } else { // 1d.
  35941. if (P<=0) return (Tfloat)T1;
  35942. return P + T1;
  35943. }
  35944. return 0;
  35945. }
  35946. // Find max root of a 2nd-order polynomial.
  35947. static bool ___distance_eikonal(const Tfloat a, const Tfloat b, const Tfloat c, Tfloat &root) {
  35948. const Tfloat delta = b*b - 4*a*c;
  35949. if (delta<0) return false;
  35950. root = 0.5f*(-b + std::sqrt(delta))/a;
  35951. return true;
  35952. }
  35953. // Insert new point in heap.
  35954. template<typename t>
  35955. void _eik_priority_queue_insert(CImg<charT>& state, unsigned int& siz, const t value,
  35956. const unsigned int x, const unsigned int y, const unsigned int z) {
  35957. if (state(x,y,z)>0) return;
  35958. state(x,y,z) = 0;
  35959. if (++siz>=_width) { if (!is_empty()) resize(_width*2,4,1,1,0); else assign(64,4); }
  35960. (*this)(siz - 1,0) = (T)value; (*this)(siz - 1,1) = (T)x; (*this)(siz - 1,2) = (T)y; (*this)(siz - 1,3) = (T)z;
  35961. for (unsigned int pos = siz - 1, par = 0; pos && value>(*this)(par=(pos + 1)/2 - 1,0); pos = par) {
  35962. cimg::swap((*this)(pos,0),(*this)(par,0)); cimg::swap((*this)(pos,1),(*this)(par,1));
  35963. cimg::swap((*this)(pos,2),(*this)(par,2)); cimg::swap((*this)(pos,3),(*this)(par,3));
  35964. }
  35965. }
  35966. //! Compute distance function to 0-valued isophotes, using the Eikonal PDE.
  35967. /**
  35968. \param nb_iterations Number of PDE iterations.
  35969. \param band_size Size of the narrow band.
  35970. \param time_step Time step of the PDE iterations.
  35971. **/
  35972. CImg<T>& distance_eikonal(const unsigned int nb_iterations, const float band_size=0, const float time_step=0.5f) {
  35973. if (is_empty()) return *this;
  35974. CImg<Tfloat> velocity(*this,false);
  35975. for (unsigned int iteration = 0; iteration<nb_iterations; ++iteration) {
  35976. Tfloat *ptrd = velocity._data, veloc_max = 0;
  35977. if (_depth>1) { // 3d
  35978. CImg_3x3x3(I,Tfloat);
  35979. cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) if (band_size<=0 || cimg::abs(Iccc)<band_size) {
  35980. const Tfloat
  35981. gx = (Incc - Ipcc)/2,
  35982. gy = (Icnc - Icpc)/2,
  35983. gz = (Iccn - Iccp)/2,
  35984. sgn = -cimg::sign(Iccc),
  35985. ix = gx*sgn>0?(Incc - Iccc):(Iccc - Ipcc),
  35986. iy = gy*sgn>0?(Icnc - Iccc):(Iccc - Icpc),
  35987. iz = gz*sgn>0?(Iccn - Iccc):(Iccc - Iccp),
  35988. ng = 1e-5f + cimg::hypot(gx,gy,gz),
  35989. ngx = gx/ng,
  35990. ngy = gy/ng,
  35991. ngz = gz/ng,
  35992. veloc = sgn*(ngx*ix + ngy*iy + ngz*iz - 1);
  35993. *(ptrd++) = veloc;
  35994. if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
  35995. } else *(ptrd++) = 0;
  35996. } else { // 2d version
  35997. CImg_3x3(I,Tfloat);
  35998. cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) if (band_size<=0 || cimg::abs(Icc)<band_size) {
  35999. const Tfloat
  36000. gx = (Inc - Ipc)/2,
  36001. gy = (Icn - Icp)/2,
  36002. sgn = -cimg::sign(Icc),
  36003. ix = gx*sgn>0?(Inc - Icc):(Icc - Ipc),
  36004. iy = gy*sgn>0?(Icn - Icc):(Icc - Icp),
  36005. ng = std::max((Tfloat)1e-5,cimg::hypot(gx,gy)),
  36006. ngx = gx/ng,
  36007. ngy = gy/ng,
  36008. veloc = sgn*(ngx*ix + ngy*iy - 1);
  36009. *(ptrd++) = veloc;
  36010. if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
  36011. } else *(ptrd++) = 0;
  36012. }
  36013. if (veloc_max>0) *this+=(velocity*=time_step/veloc_max);
  36014. }
  36015. return *this;
  36016. }
  36017. //! Compute distance function to 0-valued isophotes, using the Eikonal PDE \newinstance.
  36018. CImg<Tfloat> get_distance_eikonal(const unsigned int nb_iterations, const float band_size=0,
  36019. const float time_step=0.5f) const {
  36020. return CImg<Tfloat>(*this,false).distance_eikonal(nb_iterations,band_size,time_step);
  36021. }
  36022. //! Compute Haar multiscale wavelet transform.
  36023. /**
  36024. \param axis Axis considered for the transform.
  36025. \param invert Set inverse of direct transform.
  36026. \param nb_scales Number of scales used for the transform.
  36027. **/
  36028. CImg<T>& haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) {
  36029. return get_haar(axis,invert,nb_scales).move_to(*this);
  36030. }
  36031. //! Compute Haar multiscale wavelet transform \newinstance.
  36032. CImg<Tfloat> get_haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) const {
  36033. if (is_empty() || !nb_scales) return +*this;
  36034. CImg<Tfloat> res;
  36035. const Tfloat sqrt2 = std::sqrt(2.0f);
  36036. if (nb_scales==1) {
  36037. switch (cimg::lowercase(axis)) { // Single scale transform
  36038. case 'x' : {
  36039. const unsigned int w = _width/2;
  36040. if (w) {
  36041. if ((w%2) && w!=1)
  36042. throw CImgInstanceException(_cimg_instance
  36043. "haar(): Sub-image width %u is not even.",
  36044. cimg_instance,
  36045. w);
  36046. res.assign(_width,_height,_depth,_spectrum);
  36047. if (invert) cimg_forYZC(*this,y,z,c) { // Inverse transform along X
  36048. for (unsigned int x = 0, xw = w, x2 = 0; x<w; ++x, ++xw) {
  36049. const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(xw,y,z,c);
  36050. res(x2++,y,z,c) = (val0 - val1)/sqrt2;
  36051. res(x2++,y,z,c) = (val0 + val1)/sqrt2;
  36052. }
  36053. } else cimg_forYZC(*this,y,z,c) { // Direct transform along X
  36054. for (unsigned int x = 0, xw = w, x2 = 0; x<w; ++x, ++xw) {
  36055. const Tfloat val0 = (Tfloat)(*this)(x2++,y,z,c), val1 = (Tfloat)(*this)(x2++,y,z,c);
  36056. res(x,y,z,c) = (val0 + val1)/sqrt2;
  36057. res(xw,y,z,c) = (val1 - val0)/sqrt2;
  36058. }
  36059. }
  36060. } else return *this;
  36061. } break;
  36062. case 'y' : {
  36063. const unsigned int h = _height/2;
  36064. if (h) {
  36065. if ((h%2) && h!=1)
  36066. throw CImgInstanceException(_cimg_instance
  36067. "haar(): Sub-image height %u is not even.",
  36068. cimg_instance,
  36069. h);
  36070. res.assign(_width,_height,_depth,_spectrum);
  36071. if (invert) cimg_forXZC(*this,x,z,c) { // Inverse transform along Y
  36072. for (unsigned int y = 0, yh = h, y2 = 0; y<h; ++y, ++yh) {
  36073. const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(x,yh,z,c);
  36074. res(x,y2++,z,c) = (val0 - val1)/sqrt2;
  36075. res(x,y2++,z,c) = (val0 + val1)/sqrt2;
  36076. }
  36077. } else cimg_forXZC(*this,x,z,c) {
  36078. for (unsigned int y = 0, yh = h, y2 = 0; y<h; ++y, ++yh) { // Direct transform along Y
  36079. const Tfloat val0 = (Tfloat)(*this)(x,y2++,z,c), val1 = (Tfloat)(*this)(x,y2++,z,c);
  36080. res(x,y,z,c) = (val0 + val1)/sqrt2;
  36081. res(x,yh,z,c) = (val1 - val0)/sqrt2;
  36082. }
  36083. }
  36084. } else return *this;
  36085. } break;
  36086. case 'z' : {
  36087. const unsigned int d = _depth/2;
  36088. if (d) {
  36089. if ((d%2) && d!=1)
  36090. throw CImgInstanceException(_cimg_instance
  36091. "haar(): Sub-image depth %u is not even.",
  36092. cimg_instance,
  36093. d);
  36094. res.assign(_width,_height,_depth,_spectrum);
  36095. if (invert) cimg_forXYC(*this,x,y,c) { // Inverse transform along Z
  36096. for (unsigned int z = 0, zd = d, z2 = 0; z<d; ++z, ++zd) {
  36097. const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(x,y,zd,c);
  36098. res(x,y,z2++,c) = (val0 - val1)/sqrt2;
  36099. res(x,y,z2++,c) = (val0 + val1)/sqrt2;
  36100. }
  36101. } else cimg_forXYC(*this,x,y,c) {
  36102. for (unsigned int z = 0, zd = d, z2 = 0; z<d; ++z, ++zd) { // Direct transform along Z
  36103. const Tfloat val0 = (Tfloat)(*this)(x,y,z2++,c), val1 = (Tfloat)(*this)(x,y,z2++,c);
  36104. res(x,y,z,c) = (val0 + val1)/sqrt2;
  36105. res(x,y,zd,c) = (val1 - val0)/sqrt2;
  36106. }
  36107. }
  36108. } else return *this;
  36109. } break;
  36110. default :
  36111. throw CImgArgumentException(_cimg_instance
  36112. "haar(): Invalid specified axis '%c' "
  36113. "(should be { x | y | z }).",
  36114. cimg_instance,
  36115. axis);
  36116. }
  36117. } else { // Multi-scale version
  36118. if (invert) {
  36119. res.assign(*this,false);
  36120. switch (cimg::lowercase(axis)) {
  36121. case 'x' : {
  36122. unsigned int w = _width;
  36123. for (unsigned int s = 1; w && s<nb_scales; ++s) w/=2;
  36124. for (w = w?w:1; w<=_width; w*=2) res.draw_image(res.get_crop(0,w - 1).get_haar('x',true,1));
  36125. } break;
  36126. case 'y' : {
  36127. unsigned int h = _width;
  36128. for (unsigned int s = 1; h && s<nb_scales; ++s) h/=2;
  36129. 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));
  36130. } break;
  36131. case 'z' : {
  36132. unsigned int d = _depth;
  36133. for (unsigned int s = 1; d && s<nb_scales; ++s) d/=2;
  36134. for (d = d?d:1; d<=_depth; d*=2)
  36135. res.draw_image(res.get_crop(0,0,0,_width - 1,_height - 1,d - 1).get_haar('z',true,1));
  36136. } break;
  36137. default :
  36138. throw CImgArgumentException(_cimg_instance
  36139. "haar(): Invalid specified axis '%c' "
  36140. "(should be { x | y | z }).",
  36141. cimg_instance,
  36142. axis);
  36143. }
  36144. } else { // Direct transform
  36145. res = get_haar(axis,false,1);
  36146. switch (cimg::lowercase(axis)) {
  36147. case 'x' : {
  36148. for (unsigned int s = 1, w = _width/2; w && s<nb_scales; ++s, w/=2)
  36149. res.draw_image(res.get_crop(0,w - 1).get_haar('x',false,1));
  36150. } break;
  36151. case 'y' : {
  36152. for (unsigned int s = 1, h = _height/2; h && s<nb_scales; ++s, h/=2)
  36153. res.draw_image(res.get_crop(0,0,_width - 1,h - 1).get_haar('y',false,1));
  36154. } break;
  36155. case 'z' : {
  36156. for (unsigned int s = 1, d = _depth/2; d && s<nb_scales; ++s, d/=2)
  36157. res.draw_image(res.get_crop(0,0,0,_width - 1,_height - 1,d - 1).get_haar('z',false,1));
  36158. } break;
  36159. default :
  36160. throw CImgArgumentException(_cimg_instance
  36161. "haar(): Invalid specified axis '%c' "
  36162. "(should be { x | y | z }).",
  36163. cimg_instance,
  36164. axis);
  36165. }
  36166. }
  36167. }
  36168. return res;
  36169. }
  36170. //! Compute Haar multiscale wavelet transform \overloading.
  36171. /**
  36172. \param invert Set inverse of direct transform.
  36173. \param nb_scales Number of scales used for the transform.
  36174. **/
  36175. CImg<T>& haar(const bool invert=false, const unsigned int nb_scales=1) {
  36176. return get_haar(invert,nb_scales).move_to(*this);
  36177. }
  36178. //! Compute Haar multiscale wavelet transform \newinstance.
  36179. CImg<Tfloat> get_haar(const bool invert=false, const unsigned int nb_scales=1) const {
  36180. CImg<Tfloat> res;
  36181. if (nb_scales==1) { // Single scale transform
  36182. if (_width>1) get_haar('x',invert,1).move_to(res);
  36183. if (_height>1) { if (res) res.haar('y',invert,1); else get_haar('y',invert,1).move_to(res); }
  36184. if (_depth>1) { if (res) res.haar('z',invert,1); else get_haar('z',invert,1).move_to(res); }
  36185. if (res) return res;
  36186. } else { // Multi-scale transform
  36187. if (invert) { // Inverse transform
  36188. res.assign(*this,false);
  36189. if (_width>1) {
  36190. if (_height>1) {
  36191. if (_depth>1) {
  36192. unsigned int w = _width, h = _height, d = _depth;
  36193. for (unsigned int s = 1; w && h && d && s<nb_scales; ++s) { w/=2; h/=2; d/=2; }
  36194. 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)
  36195. res.draw_image(res.get_crop(0,0,0,w - 1,h - 1,d - 1).get_haar(true,1));
  36196. } else {
  36197. unsigned int w = _width, h = _height;
  36198. for (unsigned int s = 1; w && h && s<nb_scales; ++s) { w/=2; h/=2; }
  36199. for (w = w?w:1, h = h?h:1; w<=_width && h<=_height; w*=2, h*=2)
  36200. res.draw_image(res.get_crop(0,0,0,w - 1,h - 1,0).get_haar(true,1));
  36201. }
  36202. } else {
  36203. if (_depth>1) {
  36204. unsigned int w = _width, d = _depth;
  36205. for (unsigned int s = 1; w && d && s<nb_scales; ++s) { w/=2; d/=2; }
  36206. for (w = w?w:1, d = d?d:1; w<=_width && d<=_depth; w*=2, d*=2)
  36207. res.draw_image(res.get_crop(0,0,0,w - 1,0,d - 1).get_haar(true,1));
  36208. } else {
  36209. unsigned int w = _width;
  36210. for (unsigned int s = 1; w && s<nb_scales; ++s) w/=2;
  36211. for (w = w?w:1; w<=_width; w*=2)
  36212. res.draw_image(res.get_crop(0,0,0,w - 1,0,0).get_haar(true,1));
  36213. }
  36214. }
  36215. } else {
  36216. if (_height>1) {
  36217. if (_depth>1) {
  36218. unsigned int h = _height, d = _depth;
  36219. for (unsigned int s = 1; h && d && s<nb_scales; ++s) { h/=2; d/=2; }
  36220. for (h = h?h:1, d = d?d:1; h<=_height && d<=_depth; h*=2, d*=2)
  36221. res.draw_image(res.get_crop(0,0,0,0,h - 1,d - 1).get_haar(true,1));
  36222. } else {
  36223. unsigned int h = _height;
  36224. for (unsigned int s = 1; h && s<nb_scales; ++s) h/=2;
  36225. for (h = h?h:1; h<=_height; h*=2)
  36226. res.draw_image(res.get_crop(0,0,0,0,h - 1,0).get_haar(true,1));
  36227. }
  36228. } else {
  36229. if (_depth>1) {
  36230. unsigned int d = _depth;
  36231. for (unsigned int s = 1; d && s<nb_scales; ++s) d/=2;
  36232. for (d = d?d:1; d<=_depth; d*=2)
  36233. res.draw_image(res.get_crop(0,0,0,0,0,d - 1).get_haar(true,1));
  36234. } else return *this;
  36235. }
  36236. }
  36237. } else { // Direct transform
  36238. res = get_haar(false,1);
  36239. if (_width>1) {
  36240. if (_height>1) {
  36241. if (_depth>1)
  36242. for (unsigned int s = 1, w = _width/2, h = _height/2, d = _depth/2; w && h && d && s<nb_scales;
  36243. ++s, w/=2, h/=2, d/=2)
  36244. res.draw_image(res.get_crop(0,0,0,w - 1,h - 1,d - 1).haar(false,1));
  36245. else for (unsigned int s = 1, w = _width/2, h = _height/2; w && h && s<nb_scales; ++s, w/=2, h/=2)
  36246. res.draw_image(res.get_crop(0,0,0,w - 1,h - 1,0).haar(false,1));
  36247. } else {
  36248. if (_depth>1) for (unsigned int s = 1, w = _width/2, d = _depth/2; w && d && s<nb_scales; ++s, w/=2, d/=2)
  36249. res.draw_image(res.get_crop(0,0,0,w - 1,0,d - 1).haar(false,1));
  36250. else for (unsigned int s = 1, w = _width/2; w && s<nb_scales; ++s, w/=2)
  36251. res.draw_image(res.get_crop(0,0,0,w - 1,0,0).haar(false,1));
  36252. }
  36253. } else {
  36254. if (_height>1) {
  36255. if (_depth>1)
  36256. for (unsigned int s = 1, h = _height/2, d = _depth/2; h && d && s<nb_scales; ++s, h/=2, d/=2)
  36257. res.draw_image(res.get_crop(0,0,0,0,h - 1,d - 1).haar(false,1));
  36258. else for (unsigned int s = 1, h = _height/2; h && s<nb_scales; ++s, h/=2)
  36259. res.draw_image(res.get_crop(0,0,0,0,h - 1,0).haar(false,1));
  36260. } else {
  36261. if (_depth>1) for (unsigned int s = 1, d = _depth/2; d && s<nb_scales; ++s, d/=2)
  36262. res.draw_image(res.get_crop(0,0,0,0,0,d - 1).haar(false,1));
  36263. else return *this;
  36264. }
  36265. }
  36266. }
  36267. return res;
  36268. }
  36269. return *this;
  36270. }
  36271. //! Compute 1d Fast Fourier Transform, along a specified axis.
  36272. /**
  36273. \param axis Axis along which the FFT is computed.
  36274. \param is_invert Tells if the forward (\c false) or inverse (\c true) FFT is computed.
  36275. **/
  36276. CImgList<Tfloat> get_FFT(const char axis, const bool is_invert=false) const {
  36277. CImgList<Tfloat> res(*this,CImg<Tfloat>());
  36278. CImg<Tfloat>::FFT(res[0],res[1],axis,is_invert);
  36279. return res;
  36280. }
  36281. //! Compute n-d Fast Fourier Transform.
  36282. /*
  36283. \param is_invert Tells if the forward (\c false) or inverse (\c true) FFT is computed.
  36284. **/
  36285. CImgList<Tfloat> get_FFT(const bool is_invert=false) const {
  36286. CImgList<Tfloat> res(*this,CImg<Tfloat>());
  36287. CImg<Tfloat>::FFT(res[0],res[1],is_invert);
  36288. return res;
  36289. }
  36290. //! Compute 1d Fast Fourier Transform, along a specified axis.
  36291. /**
  36292. \param[in,out] real Real part of the pixel values.
  36293. \param[in,out] imag Imaginary part of the pixel values.
  36294. \param axis Axis along which the FFT is computed.
  36295. \param is_invert Tells if the forward (\c false) or inverse (\c true) FFT is computed.
  36296. **/
  36297. static void FFT(CImg<T>& real, CImg<T>& imag, const char axis, const bool is_invert=false) {
  36298. if (!real)
  36299. throw CImgInstanceException("CImg<%s>::FFT(): Specified real part is empty.",
  36300. pixel_type());
  36301. if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,(T)0);
  36302. if (!real.is_sameXYZC(imag))
  36303. throw CImgInstanceException("CImg<%s>::FFT(): Specified real part (%u,%u,%u,%u,%p) and "
  36304. "imaginary part (%u,%u,%u,%u,%p) have different dimensions.",
  36305. pixel_type(),
  36306. real._width,real._height,real._depth,real._spectrum,real._data,
  36307. imag._width,imag._height,imag._depth,imag._spectrum,imag._data);
  36308. #ifdef cimg_use_fftw3
  36309. cimg::mutex(12);
  36310. fftw_complex *data_in;
  36311. fftw_plan data_plan;
  36312. switch (cimg::lowercase(axis)) {
  36313. case 'x' : { // Fourier along X, using FFTW library.
  36314. data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width);
  36315. if (!data_in) throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) "
  36316. "for computing FFT of image (%u,%u,%u,%u) along the X-axis.",
  36317. pixel_type(),
  36318. cimg::strbuffersize(sizeof(fftw_complex)*real._width),
  36319. real._width,real._height,real._depth,real._spectrum);
  36320. data_plan = fftw_plan_dft_1d(real._width,data_in,data_in,is_invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
  36321. cimg_forYZC(real,y,z,c) {
  36322. T *ptrr = real.data(0,y,z,c), *ptri = imag.data(0,y,z,c);
  36323. double *ptrd = (double*)data_in;
  36324. cimg_forX(real,x) { *(ptrd++) = (double)*(ptrr++); *(ptrd++) = (double)*(ptri++); }
  36325. fftw_execute(data_plan);
  36326. const unsigned int fact = real._width;
  36327. if (is_invert) cimg_forX(real,x) { *(--ptri) = (T)(*(--ptrd)/fact); *(--ptrr) = (T)(*(--ptrd)/fact); }
  36328. else cimg_forX(real,x) { *(--ptri) = (T)*(--ptrd); *(--ptrr) = (T)*(--ptrd); }
  36329. }
  36330. } break;
  36331. case 'y' : { // Fourier along Y, using FFTW library.
  36332. data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * real._height);
  36333. if (!data_in) throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) "
  36334. "for computing FFT of image (%u,%u,%u,%u) along the Y-axis.",
  36335. pixel_type(),
  36336. cimg::strbuffersize(sizeof(fftw_complex)*real._height),
  36337. real._width,real._height,real._depth,real._spectrum);
  36338. data_plan = fftw_plan_dft_1d(real._height,data_in,data_in,is_invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
  36339. const unsigned int off = real._width;
  36340. cimg_forXZC(real,x,z,c) {
  36341. T *ptrr = real.data(x,0,z,c), *ptri = imag.data(x,0,z,c);
  36342. double *ptrd = (double*)data_in;
  36343. cimg_forY(real,y) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; }
  36344. fftw_execute(data_plan);
  36345. const unsigned int fact = real._height;
  36346. if (is_invert)
  36347. cimg_forY(real,y) { ptrr-=off; ptri-=off; *ptri = (T)(*(--ptrd)/fact); *ptrr = (T)(*(--ptrd)/fact); }
  36348. else cimg_forY(real,y) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); }
  36349. }
  36350. } break;
  36351. case 'z' : { // Fourier along Z, using FFTW library.
  36352. data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * real._depth);
  36353. if (!data_in) throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) "
  36354. "for computing FFT of image (%u,%u,%u,%u) along the Z-axis.",
  36355. pixel_type(),
  36356. cimg::strbuffersize(sizeof(fftw_complex)*real._depth),
  36357. real._width,real._height,real._depth,real._spectrum);
  36358. data_plan = fftw_plan_dft_1d(real._depth,data_in,data_in,is_invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
  36359. const ulongT off = (ulongT)real._width*real._height;
  36360. cimg_forXYC(real,x,y,c) {
  36361. T *ptrr = real.data(x,y,0,c), *ptri = imag.data(x,y,0,c);
  36362. double *ptrd = (double*)data_in;
  36363. cimg_forZ(real,z) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; }
  36364. fftw_execute(data_plan);
  36365. const unsigned int fact = real._depth;
  36366. if (is_invert)
  36367. cimg_forZ(real,z) { ptrr-=off; ptri-=off; *ptri = (T)(*(--ptrd)/fact); *ptrr = (T)(*(--ptrd)/fact); }
  36368. else cimg_forZ(real,z) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); }
  36369. }
  36370. } break;
  36371. default :
  36372. throw CImgArgumentException("CImgList<%s>::FFT(): Invalid specified axis '%c' for real and imaginary parts "
  36373. "(%u,%u,%u,%u) "
  36374. "(should be { x | y | z }).",
  36375. pixel_type(),axis,
  36376. real._width,real._height,real._depth,real._spectrum);
  36377. }
  36378. fftw_destroy_plan(data_plan);
  36379. fftw_free(data_in);
  36380. cimg::mutex(12,0);
  36381. #else
  36382. switch (cimg::lowercase(axis)) {
  36383. case 'x' : { // Fourier along X, using built-in functions.
  36384. const unsigned int N = real._width, N2 = N>>1;
  36385. if (((N - 1)&N) && N!=1)
  36386. throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) "
  36387. "have non 2^N dimension along the X-axis.",
  36388. pixel_type(),
  36389. real._width,real._height,real._depth,real._spectrum);
  36390. for (unsigned int i = 0, j = 0; i<N2; ++i) {
  36391. if (j>i) cimg_forYZC(real,y,z,c) {
  36392. cimg::swap(real(i,y,z,c),real(j,y,z,c));
  36393. cimg::swap(imag(i,y,z,c),imag(j,y,z,c));
  36394. if (j<N2) {
  36395. const unsigned int ri = N - 1 - i, rj = N - 1 - j;
  36396. cimg::swap(real(ri,y,z,c),real(rj,y,z,c));
  36397. cimg::swap(imag(ri,y,z,c),imag(rj,y,z,c));
  36398. }
  36399. }
  36400. for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
  36401. }
  36402. for (unsigned int delta = 2; delta<=N; delta<<=1) {
  36403. const unsigned int delta2 = delta>>1;
  36404. for (unsigned int i = 0; i<N; i+=delta) {
  36405. float wr = 1, wi = 0;
  36406. const float
  36407. angle = (float)((is_invert?+1:-1)*2*cimg::PI/delta),
  36408. ca = (float)std::cos(angle),
  36409. sa = (float)std::sin(angle);
  36410. for (unsigned int k = 0; k<delta2; ++k) {
  36411. const unsigned int j = i + k, nj = j + delta2;
  36412. cimg_forYZC(real,y,z,c) {
  36413. 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);
  36414. const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
  36415. nir = (T)(ir - tmpr);
  36416. nii = (T)(ii - tmpi);
  36417. ir+=(T)tmpr;
  36418. ii+=(T)tmpi;
  36419. }
  36420. const float nwr = wr*ca-wi*sa;
  36421. wi = wi*ca + wr*sa;
  36422. wr = nwr;
  36423. }
  36424. }
  36425. }
  36426. if (is_invert) { real/=N; imag/=N; }
  36427. } break;
  36428. case 'y' : { // Fourier along Y, using built-in functions.
  36429. const unsigned int N = real._height, N2 = N>>1;
  36430. if (((N - 1)&N) && N!=1)
  36431. throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) "
  36432. "have non 2^N dimension along the Y-axis.",
  36433. pixel_type(),
  36434. real._width,real._height,real._depth,real._spectrum);
  36435. for (unsigned int i = 0, j = 0; i<N2; ++i) {
  36436. if (j>i) cimg_forXZC(real,x,z,c) {
  36437. cimg::swap(real(x,i,z,c),real(x,j,z,c));
  36438. cimg::swap(imag(x,i,z,c),imag(x,j,z,c));
  36439. if (j<N2) {
  36440. const unsigned int ri = N - 1 - i, rj = N - 1 - j;
  36441. cimg::swap(real(x,ri,z,c),real(x,rj,z,c));
  36442. cimg::swap(imag(x,ri,z,c),imag(x,rj,z,c));
  36443. }
  36444. }
  36445. for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
  36446. }
  36447. for (unsigned int delta = 2; delta<=N; delta<<=1) {
  36448. const unsigned int delta2 = (delta>>1);
  36449. for (unsigned int i = 0; i<N; i+=delta) {
  36450. float wr = 1, wi = 0;
  36451. const float
  36452. angle = (float)((is_invert?+1:-1)*2*cimg::PI/delta),
  36453. ca = (float)std::cos(angle),
  36454. sa = (float)std::sin(angle);
  36455. for (unsigned int k = 0; k<delta2; ++k) {
  36456. const unsigned int j = i + k, nj = j + delta2;
  36457. cimg_forXZC(real,x,z,c) {
  36458. 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);
  36459. const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
  36460. nir = (T)(ir - tmpr);
  36461. nii = (T)(ii - tmpi);
  36462. ir+=(T)tmpr;
  36463. ii+=(T)tmpi;
  36464. }
  36465. const float nwr = wr*ca-wi*sa;
  36466. wi = wi*ca + wr*sa;
  36467. wr = nwr;
  36468. }
  36469. }
  36470. }
  36471. if (is_invert) { real/=N; imag/=N; }
  36472. } break;
  36473. case 'z' : { // Fourier along Z, using built-in functions.
  36474. const unsigned int N = real._depth, N2 = N>>1;
  36475. if (((N - 1)&N) && N!=1)
  36476. throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) "
  36477. "have non 2^N dimension along the Z-axis.",
  36478. pixel_type(),
  36479. real._width,real._height,real._depth,real._spectrum);
  36480. for (unsigned int i = 0, j = 0; i<N2; ++i) {
  36481. if (j>i) cimg_forXYC(real,x,y,c) {
  36482. cimg::swap(real(x,y,i,c),real(x,y,j,c));
  36483. cimg::swap(imag(x,y,i,c),imag(x,y,j,c));
  36484. if (j<N2) {
  36485. const unsigned int ri = N - 1 - i, rj = N - 1 - j;
  36486. cimg::swap(real(x,y,ri,c),real(x,y,rj,c));
  36487. cimg::swap(imag(x,y,ri,c),imag(x,y,rj,c));
  36488. }
  36489. }
  36490. for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
  36491. }
  36492. for (unsigned int delta = 2; delta<=N; delta<<=1) {
  36493. const unsigned int delta2 = (delta>>1);
  36494. for (unsigned int i = 0; i<N; i+=delta) {
  36495. float wr = 1, wi = 0;
  36496. const float
  36497. angle = (float)((is_invert?+1:-1)*2*cimg::PI/delta),
  36498. ca = (float)std::cos(angle),
  36499. sa = (float)std::sin(angle);
  36500. for (unsigned int k = 0; k<delta2; ++k) {
  36501. const unsigned int j = i + k, nj = j + delta2;
  36502. cimg_forXYC(real,x,y,c) {
  36503. 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);
  36504. const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
  36505. nir = (T)(ir - tmpr);
  36506. nii = (T)(ii - tmpi);
  36507. ir+=(T)tmpr;
  36508. ii+=(T)tmpi;
  36509. }
  36510. const float nwr = wr*ca-wi*sa;
  36511. wi = wi*ca + wr*sa;
  36512. wr = nwr;
  36513. }
  36514. }
  36515. }
  36516. if (is_invert) { real/=N; imag/=N; }
  36517. } break;
  36518. default :
  36519. throw CImgArgumentException("CImgList<%s>::FFT(): Invalid specified axis '%c' for real and imaginary parts "
  36520. "(%u,%u,%u,%u) "
  36521. "(should be { x | y | z }).",
  36522. pixel_type(),axis,
  36523. real._width,real._height,real._depth,real._spectrum);
  36524. }
  36525. #endif
  36526. }
  36527. //! Compute n-d Fast Fourier Transform.
  36528. /**
  36529. \param[in,out] real Real part of the pixel values.
  36530. \param[in,out] imag Imaginary part of the pixel values.
  36531. \param is_invert Tells if the forward (\c false) or inverse (\c true) FFT is computed.
  36532. \param nb_threads Number of parallel threads used for the computation.
  36533. Use \c 0 to set this to the number of available cpus.
  36534. **/
  36535. static void FFT(CImg<T>& real, CImg<T>& imag, const bool is_invert=false, const unsigned int nb_threads=0) {
  36536. if (!real)
  36537. throw CImgInstanceException("CImgList<%s>::FFT(): Empty specified real part.",
  36538. pixel_type());
  36539. if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,(T)0);
  36540. if (!real.is_sameXYZC(imag))
  36541. throw CImgInstanceException("CImgList<%s>::FFT(): Specified real part (%u,%u,%u,%u,%p) and "
  36542. "imaginary part (%u,%u,%u,%u,%p) have different dimensions.",
  36543. pixel_type(),
  36544. real._width,real._height,real._depth,real._spectrum,real._data,
  36545. imag._width,imag._height,imag._depth,imag._spectrum,imag._data);
  36546. #ifdef cimg_use_fftw3
  36547. cimg::mutex(12);
  36548. #ifndef cimg_use_fftw3_singlethread
  36549. const unsigned int _nb_threads = nb_threads?nb_threads:cimg::nb_cpus();
  36550. static int fftw_st = fftw_init_threads();
  36551. cimg::unused(fftw_st);
  36552. fftw_plan_with_nthreads(_nb_threads);
  36553. #else
  36554. cimg::unused(nb_threads);
  36555. #endif
  36556. fftw_complex *data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width*real._height*real._depth);
  36557. if (!data_in) throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) "
  36558. "for computing FFT of image (%u,%u,%u,%u).",
  36559. pixel_type(),
  36560. cimg::strbuffersize(sizeof(fftw_complex)*real._width*
  36561. real._height*real._depth*real._spectrum),
  36562. real._width,real._height,real._depth,real._spectrum);
  36563. fftw_plan data_plan;
  36564. const ulongT w = (ulongT)real._width, wh = w*real._height, whd = wh*real._depth;
  36565. data_plan = fftw_plan_dft_3d(real._width,real._height,real._depth,data_in,data_in,
  36566. is_invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
  36567. cimg_forC(real,c) {
  36568. T *ptrr = real.data(0,0,0,c), *ptri = imag.data(0,0,0,c);
  36569. double *ptrd = (double*)data_in;
  36570. for (unsigned int x = 0; x<real._width; ++x, ptrr-=wh - 1, ptri-=wh - 1)
  36571. for (unsigned int y = 0; y<real._height; ++y, ptrr-=whd-w, ptri-=whd-w)
  36572. for (unsigned int z = 0; z<real._depth; ++z, ptrr+=wh, ptri+=wh) {
  36573. *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri;
  36574. }
  36575. fftw_execute(data_plan);
  36576. ptrd = (double*)data_in;
  36577. ptrr = real.data(0,0,0,c);
  36578. ptri = imag.data(0,0,0,c);
  36579. if (!is_invert) for (unsigned int x = 0; x<real._width; ++x, ptrr-=wh - 1, ptri-=wh - 1)
  36580. for (unsigned int y = 0; y<real._height; ++y, ptrr-=whd-w, ptri-=whd-w)
  36581. for (unsigned int z = 0; z<real._depth; ++z, ptrr+=wh, ptri+=wh) {
  36582. *ptrr = (T)*(ptrd++); *ptri = (T)*(ptrd++);
  36583. }
  36584. else for (unsigned int x = 0; x<real._width; ++x, ptrr-=wh - 1, ptri-=wh - 1)
  36585. for (unsigned int y = 0; y<real._height; ++y, ptrr-=whd-w, ptri-=whd-w)
  36586. for (unsigned int z = 0; z<real._depth; ++z, ptrr+=wh, ptri+=wh) {
  36587. *ptrr = (T)(*(ptrd++)/whd); *ptri = (T)(*(ptrd++)/whd);
  36588. }
  36589. }
  36590. fftw_destroy_plan(data_plan);
  36591. fftw_free(data_in);
  36592. #ifndef cimg_use_fftw3_singlethread
  36593. fftw_cleanup_threads();
  36594. #endif
  36595. cimg::mutex(12,0);
  36596. #else
  36597. cimg::unused(nb_threads);
  36598. if (real._depth>1) FFT(real,imag,'z',is_invert);
  36599. if (real._height>1) FFT(real,imag,'y',is_invert);
  36600. if (real._width>1) FFT(real,imag,'x',is_invert);
  36601. #endif
  36602. }
  36603. //@}
  36604. //-------------------------------------
  36605. //
  36606. //! \name 3d Objects Management
  36607. //@{
  36608. //-------------------------------------
  36609. //! Shift 3d object's vertices.
  36610. /**
  36611. \param tx X-coordinate of the 3d displacement vector.
  36612. \param ty Y-coordinate of the 3d displacement vector.
  36613. \param tz Z-coordinate of the 3d displacement vector.
  36614. **/
  36615. CImg<T>& shift_object3d(const float tx, const float ty=0, const float tz=0) {
  36616. if (_height!=3 || _depth>1 || _spectrum>1)
  36617. throw CImgInstanceException(_cimg_instance
  36618. "shift_object3d(): Instance is not a set of 3d vertices.",
  36619. cimg_instance);
  36620. get_shared_row(0)+=tx; get_shared_row(1)+=ty; get_shared_row(2)+=tz;
  36621. return *this;
  36622. }
  36623. //! Shift 3d object's vertices \newinstance.
  36624. CImg<Tfloat> get_shift_object3d(const float tx, const float ty=0, const float tz=0) const {
  36625. return CImg<Tfloat>(*this,false).shift_object3d(tx,ty,tz);
  36626. }
  36627. //! Shift 3d object's vertices, so that it becomes centered.
  36628. /**
  36629. \note The object center is computed as its barycenter.
  36630. **/
  36631. CImg<T>& shift_object3d() {
  36632. if (_height!=3 || _depth>1 || _spectrum>1)
  36633. throw CImgInstanceException(_cimg_instance
  36634. "shift_object3d(): Instance is not a set of 3d vertices.",
  36635. cimg_instance);
  36636. CImg<T> xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2);
  36637. float
  36638. xm, xM = (float)xcoords.max_min(xm),
  36639. ym, yM = (float)ycoords.max_min(ym),
  36640. zm, zM = (float)zcoords.max_min(zm);
  36641. xcoords-=(xm + xM)/2; ycoords-=(ym + yM)/2; zcoords-=(zm + zM)/2;
  36642. return *this;
  36643. }
  36644. //! Shift 3d object's vertices, so that it becomes centered \newinstance.
  36645. CImg<Tfloat> get_shift_object3d() const {
  36646. return CImg<Tfloat>(*this,false).shift_object3d();
  36647. }
  36648. //! Resize 3d object.
  36649. /**
  36650. \param sx Width of the 3d object's bounding box.
  36651. \param sy Height of the 3d object's bounding box.
  36652. \param sz Depth of the 3d object's bounding box.
  36653. **/
  36654. CImg<T>& resize_object3d(const float sx, const float sy=-100, const float sz=-100) {
  36655. if (_height!=3 || _depth>1 || _spectrum>1)
  36656. throw CImgInstanceException(_cimg_instance
  36657. "resize_object3d(): Instance is not a set of 3d vertices.",
  36658. cimg_instance);
  36659. CImg<T> xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2);
  36660. float
  36661. xm, xM = (float)xcoords.max_min(xm),
  36662. ym, yM = (float)ycoords.max_min(ym),
  36663. zm, zM = (float)zcoords.max_min(zm);
  36664. if (xm<xM) { if (sx>0) xcoords*=sx/(xM-xm); else xcoords*=-sx/100; }
  36665. if (ym<yM) { if (sy>0) ycoords*=sy/(yM-ym); else ycoords*=-sy/100; }
  36666. if (zm<zM) { if (sz>0) zcoords*=sz/(zM-zm); else zcoords*=-sz/100; }
  36667. return *this;
  36668. }
  36669. //! Resize 3d object \newinstance.
  36670. CImg<Tfloat> get_resize_object3d(const float sx, const float sy=-100, const float sz=-100) const {
  36671. return CImg<Tfloat>(*this,false).resize_object3d(sx,sy,sz);
  36672. }
  36673. //! Resize 3d object to unit size.
  36674. CImg<T> resize_object3d() {
  36675. if (_height!=3 || _depth>1 || _spectrum>1)
  36676. throw CImgInstanceException(_cimg_instance
  36677. "resize_object3d(): Instance is not a set of 3d vertices.",
  36678. cimg_instance);
  36679. CImg<T> xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2);
  36680. float
  36681. xm, xM = (float)xcoords.max_min(xm),
  36682. ym, yM = (float)ycoords.max_min(ym),
  36683. zm, zM = (float)zcoords.max_min(zm);
  36684. const float dx = xM - xm, dy = yM - ym, dz = zM - zm, dmax = cimg::max(dx,dy,dz);
  36685. if (dmax>0) { xcoords/=dmax; ycoords/=dmax; zcoords/=dmax; }
  36686. return *this;
  36687. }
  36688. //! Resize 3d object to unit size \newinstance.
  36689. CImg<Tfloat> get_resize_object3d() const {
  36690. return CImg<Tfloat>(*this,false).resize_object3d();
  36691. }
  36692. //! Merge two 3d objects together.
  36693. /**
  36694. \param[in,out] primitives Primitives data of the current 3d object.
  36695. \param obj_vertices Vertices data of the additional 3d object.
  36696. \param obj_primitives Primitives data of the additional 3d object.
  36697. **/
  36698. template<typename tf, typename tp, typename tff>
  36699. CImg<T>& append_object3d(CImgList<tf>& primitives, const CImg<tp>& obj_vertices,
  36700. const CImgList<tff>& obj_primitives) {
  36701. if (!obj_vertices || !obj_primitives) return *this;
  36702. if (obj_vertices._height!=3 || obj_vertices._depth>1 || obj_vertices._spectrum>1)
  36703. throw CImgInstanceException(_cimg_instance
  36704. "append_object3d(): Specified vertice image (%u,%u,%u,%u,%p) is not a "
  36705. "set of 3d vertices.",
  36706. cimg_instance,
  36707. obj_vertices._width,obj_vertices._height,
  36708. obj_vertices._depth,obj_vertices._spectrum,obj_vertices._data);
  36709. if (is_empty()) { primitives.assign(obj_primitives); return assign(obj_vertices); }
  36710. if (_height!=3 || _depth>1 || _spectrum>1)
  36711. throw CImgInstanceException(_cimg_instance
  36712. "append_object3d(): Instance is not a set of 3d vertices.",
  36713. cimg_instance);
  36714. const unsigned int P = _width;
  36715. append(obj_vertices,'x');
  36716. const unsigned int N = primitives._width;
  36717. primitives.insert(obj_primitives);
  36718. for (unsigned int i = N; i<primitives._width; ++i) {
  36719. CImg<tf> &p = primitives[i];
  36720. switch (p.size()) {
  36721. case 1 : p[0]+=P; break; // Point.
  36722. case 5 : p[0]+=P; p[1]+=P; break; // Sphere.
  36723. case 2 : case 6 : p[0]+=P; p[1]+=P; break; // Segment.
  36724. case 3 : case 9 : p[0]+=P; p[1]+=P; p[2]+=P; break; // Triangle.
  36725. case 4 : case 12 : p[0]+=P; p[1]+=P; p[2]+=P; p[3]+=P; break; // Rectangle.
  36726. }
  36727. }
  36728. return *this;
  36729. }
  36730. //! Texturize primitives of a 3d object.
  36731. /**
  36732. \param[in,out] primitives Primitives data of the 3d object.
  36733. \param[in,out] colors Colors data of the 3d object.
  36734. \param texture Texture image to map to 3d object.
  36735. \param coords Texture-mapping coordinates.
  36736. **/
  36737. template<typename tp, typename tc, typename tt, typename tx>
  36738. const CImg<T>& texturize_object3d(CImgList<tp>& primitives, CImgList<tc>& colors,
  36739. const CImg<tt>& texture, const CImg<tx>& coords=CImg<tx>::const_empty()) const {
  36740. if (is_empty()) return *this;
  36741. if (_height!=3)
  36742. throw CImgInstanceException(_cimg_instance
  36743. "texturize_object3d(): image instance is not a set of 3d points.",
  36744. cimg_instance);
  36745. if (coords && (coords._width!=_width || coords._height!=2))
  36746. throw CImgArgumentException(_cimg_instance
  36747. "texturize_object3d(): Invalid specified texture coordinates (%u,%u,%u,%u,%p).",
  36748. cimg_instance,
  36749. coords._width,coords._height,coords._depth,coords._spectrum,coords._data);
  36750. CImg<intT> _coords;
  36751. if (!coords) { // If no texture coordinates specified, do a default XY-projection.
  36752. _coords.assign(_width,2);
  36753. float
  36754. xmin, xmax = (float)get_shared_row(0).max_min(xmin),
  36755. ymin, ymax = (float)get_shared_row(1).max_min(ymin),
  36756. dx = xmax>xmin?xmax-xmin:1,
  36757. dy = ymax>ymin?ymax-ymin:1;
  36758. cimg_forX(*this,p) {
  36759. _coords(p,0) = (int)(((*this)(p,0) - xmin)*texture._width/dx);
  36760. _coords(p,1) = (int)(((*this)(p,1) - ymin)*texture._height/dy);
  36761. }
  36762. } else _coords = coords;
  36763. int texture_ind = -1;
  36764. cimglist_for(primitives,l) {
  36765. CImg<tp> &p = primitives[l];
  36766. const unsigned int siz = p.size();
  36767. switch (siz) {
  36768. case 1 : { // Point.
  36769. const unsigned int i0 = (unsigned int)p[0];
  36770. const int x0 = _coords(i0,0), y0 = _coords(i0,1);
  36771. texture.get_vector_at(x0<=0?0:x0>=texture.width()?texture.width() - 1:x0,
  36772. y0<=0?0:y0>=texture.height()?texture.height() - 1:y0).move_to(colors[l]);
  36773. } break;
  36774. case 2 : case 6 : { // Line.
  36775. const unsigned int i0 = (unsigned int)p[0], i1 = (unsigned int)p[1];
  36776. const int
  36777. x0 = _coords(i0,0), y0 = _coords(i0,1),
  36778. x1 = _coords(i1,0), y1 = _coords(i1,1);
  36779. if (texture_ind<0) colors[texture_ind=l].assign(texture,false);
  36780. else colors[l].assign(colors[texture_ind],true);
  36781. CImg<tp>::vector(i0,i1,x0,y0,x1,y1).move_to(p);
  36782. } break;
  36783. case 3 : case 9 : { // Triangle.
  36784. const unsigned int i0 = (unsigned int)p[0], i1 = (unsigned int)p[1], i2 = (unsigned int)p[2];
  36785. const int
  36786. x0 = _coords(i0,0), y0 = _coords(i0,1),
  36787. x1 = _coords(i1,0), y1 = _coords(i1,1),
  36788. x2 = _coords(i2,0), y2 = _coords(i2,1);
  36789. if (texture_ind<0) colors[texture_ind=l].assign(texture,false);
  36790. else colors[l].assign(colors[texture_ind],true);
  36791. CImg<tp>::vector(i0,i1,i2,x0,y0,x1,y1,x2,y2).move_to(p);
  36792. } break;
  36793. case 4 : case 12 : { // Quadrangle.
  36794. const unsigned int
  36795. i0 = (unsigned int)p[0], i1 = (unsigned int)p[1], i2 = (unsigned int)p[2], i3 = (unsigned int)p[3];
  36796. const int
  36797. x0 = _coords(i0,0), y0 = _coords(i0,1),
  36798. x1 = _coords(i1,0), y1 = _coords(i1,1),
  36799. x2 = _coords(i2,0), y2 = _coords(i2,1),
  36800. x3 = _coords(i3,0), y3 = _coords(i3,1);
  36801. if (texture_ind<0) colors[texture_ind=l].assign(texture,false);
  36802. else colors[l].assign(colors[texture_ind],true);
  36803. CImg<tp>::vector(i0,i1,i2,i3,x0,y0,x1,y1,x2,y2,x3,y3).move_to(p);
  36804. } break;
  36805. }
  36806. }
  36807. return *this;
  36808. }
  36809. //! Generate a 3d elevation of the image instance.
  36810. /**
  36811. \param[out] primitives The returned list of the 3d object primitives
  36812. (template type \e tf should be at least \e unsigned \e int).
  36813. \param[out] colors The returned list of the 3d object colors.
  36814. \param elevation The input elevation map.
  36815. \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N - 1).
  36816. \par Example
  36817. \code
  36818. const CImg<float> img("reference.jpg");
  36819. CImgList<unsigned int> faces3d;
  36820. CImgList<unsigned char> colors3d;
  36821. const CImg<float> points3d = img.get_elevation3d(faces3d,colors3d,img.get_norm()*0.2);
  36822. CImg<unsigned char>().display_object3d("Elevation3d",points3d,faces3d,colors3d);
  36823. \endcode
  36824. \image html ref_elevation3d.jpg
  36825. **/
  36826. template<typename tf, typename tc, typename te>
  36827. CImg<floatT> get_elevation3d(CImgList<tf>& primitives, CImgList<tc>& colors, const CImg<te>& elevation) const {
  36828. if (!is_sameXY(elevation) || elevation._depth>1 || elevation._spectrum>1)
  36829. throw CImgArgumentException(_cimg_instance
  36830. "get_elevation3d(): Instance and specified elevation (%u,%u,%u,%u,%p) "
  36831. "have incompatible dimensions.",
  36832. cimg_instance,
  36833. elevation._width,elevation._height,elevation._depth,
  36834. elevation._spectrum,elevation._data);
  36835. if (is_empty()) return *this;
  36836. float m, M = (float)max_min(m);
  36837. if (M==m) ++M;
  36838. colors.assign();
  36839. const unsigned int size_x1 = _width - 1, size_y1 = _height - 1;
  36840. for (unsigned int y = 0; y<size_y1; ++y)
  36841. for (unsigned int x = 0; x<size_x1; ++x) {
  36842. const unsigned char
  36843. r = (unsigned char)(((*this)(x,y,0) - m)*255/(M-m)),
  36844. g = (unsigned char)(_spectrum>1?((*this)(x,y,1) - m)*255/(M-m):r),
  36845. b = (unsigned char)(_spectrum>2?((*this)(x,y,2) - m)*255/(M-m):_spectrum>1?0:r);
  36846. CImg<tc>::vector((tc)r,(tc)g,(tc)b).move_to(colors);
  36847. }
  36848. const typename CImg<te>::_functor2d_int func(elevation);
  36849. return elevation3d(primitives,func,0,0,_width - 1.0f,_height - 1.0f,_width,_height);
  36850. }
  36851. //! Generate the 3d projection planes of the image instance.
  36852. /**
  36853. \param[out] primitives Primitives data of the returned 3d object.
  36854. \param[out] colors Colors data of the returned 3d object.
  36855. \param x0 X-coordinate of the projection point.
  36856. \param y0 Y-coordinate of the projection point.
  36857. \param z0 Z-coordinate of the projection point.
  36858. \param normalize_colors Tells if the created textures have normalized colors.
  36859. **/
  36860. template<typename tf, typename tc>
  36861. CImg<floatT> get_projections3d(CImgList<tf>& primitives, CImgList<tc>& colors,
  36862. const unsigned int x0, const unsigned int y0, const unsigned int z0,
  36863. const bool normalize_colors=false) const {
  36864. float m = 0, M = 0, delta = 1;
  36865. if (normalize_colors) { m = (float)min_max(M); delta = 255/(m==M?1:M-m); }
  36866. const unsigned int
  36867. _x0 = (x0>=_width)?_width - 1:x0,
  36868. _y0 = (y0>=_height)?_height - 1:y0,
  36869. _z0 = (z0>=_depth)?_depth - 1:z0;
  36870. CImg<tc> img_xy, img_xz, img_yz;
  36871. if (normalize_colors) {
  36872. ((get_crop(0,0,_z0,0,_width - 1,_height - 1,_z0,_spectrum - 1)-=m)*=delta).move_to(img_xy);
  36873. ((get_crop(0,_y0,0,0,_width - 1,_y0,_depth - 1,_spectrum - 1)-=m)*=delta).resize(_width,_depth,1,-100,-1).
  36874. move_to(img_xz);
  36875. ((get_crop(_x0,0,0,0,_x0,_height - 1,_depth - 1,_spectrum - 1)-=m)*=delta).resize(_height,_depth,1,-100,-1).
  36876. move_to(img_yz);
  36877. } else {
  36878. get_crop(0,0,_z0,0,_width - 1,_height - 1,_z0,_spectrum - 1).move_to(img_xy);
  36879. get_crop(0,_y0,0,0,_width - 1,_y0,_depth - 1,_spectrum - 1).resize(_width,_depth,1,-100,-1).move_to(img_xz);
  36880. get_crop(_x0,0,0,0,_x0,_height - 1,_depth - 1,_spectrum - 1).resize(_height,_depth,1,-100,-1).move_to(img_yz);
  36881. }
  36882. CImg<floatT> points(12,3,1,1,
  36883. 0,_width - 1,_width - 1,0, 0,_width - 1,_width - 1,0, _x0,_x0,_x0,_x0,
  36884. 0,0,_height - 1,_height - 1, _y0,_y0,_y0,_y0, 0,_height - 1,_height - 1,0,
  36885. _z0,_z0,_z0,_z0, 0,0,_depth - 1,_depth - 1, 0,0,_depth - 1,_depth - 1);
  36886. primitives.assign();
  36887. 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).
  36888. move_to(primitives);
  36889. 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).
  36890. move_to(primitives);
  36891. 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).
  36892. move_to(primitives);
  36893. colors.assign();
  36894. img_xy.move_to(colors);
  36895. img_xz.move_to(colors);
  36896. img_yz.move_to(colors);
  36897. return points;
  36898. }
  36899. //! Generate a isoline of the image instance as a 3d object.
  36900. /**
  36901. \param[out] primitives The returned list of the 3d object primitives
  36902. (template type \e tf should be at least \e unsigned \e int).
  36903. \param isovalue The returned list of the 3d object colors.
  36904. \param size_x The number of subdivisions along the X-axis.
  36905. \param size_y The number of subdisivions along the Y-axis.
  36906. \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N - 1).
  36907. \par Example
  36908. \code
  36909. const CImg<float> img("reference.jpg");
  36910. CImgList<unsigned int> faces3d;
  36911. const CImg<float> points3d = img.get_isoline3d(faces3d,100);
  36912. CImg<unsigned char>().display_object3d("Isoline3d",points3d,faces3d,colors3d);
  36913. \endcode
  36914. \image html ref_isoline3d.jpg
  36915. **/
  36916. template<typename tf>
  36917. CImg<floatT> get_isoline3d(CImgList<tf>& primitives, const float isovalue,
  36918. const int size_x=-100, const int size_y=-100) const {
  36919. if (_spectrum>1)
  36920. throw CImgInstanceException(_cimg_instance
  36921. "get_isoline3d(): Instance is not a scalar image.",
  36922. cimg_instance);
  36923. if (_depth>1)
  36924. throw CImgInstanceException(_cimg_instance
  36925. "get_isoline3d(): Instance is not a 2d image.",
  36926. cimg_instance);
  36927. primitives.assign();
  36928. if (is_empty()) return *this;
  36929. CImg<floatT> vertices;
  36930. if ((size_x==-100 && size_y==-100) || (size_x==width() && size_y==height())) {
  36931. const _functor2d_int func(*this);
  36932. vertices = isoline3d(primitives,func,isovalue,0,0,width() - 1.0f,height() - 1.0f,width(),height());
  36933. } else {
  36934. const _functor2d_float func(*this);
  36935. vertices = isoline3d(primitives,func,isovalue,0,0,width() - 1.0f,height() - 1.0f,size_x,size_y);
  36936. }
  36937. return vertices;
  36938. }
  36939. //! Generate an isosurface of the image instance as a 3d object.
  36940. /**
  36941. \param[out] primitives The returned list of the 3d object primitives
  36942. (template type \e tf should be at least \e unsigned \e int).
  36943. \param isovalue The returned list of the 3d object colors.
  36944. \param size_x Number of subdivisions along the X-axis.
  36945. \param size_y Number of subdisivions along the Y-axis.
  36946. \param size_z Number of subdisivions along the Z-axis.
  36947. \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N - 1).
  36948. \par Example
  36949. \code
  36950. const CImg<float> img = CImg<unsigned char>("reference.jpg").resize(-100,-100,20);
  36951. CImgList<unsigned int> faces3d;
  36952. const CImg<float> points3d = img.get_isosurface3d(faces3d,100);
  36953. CImg<unsigned char>().display_object3d("Isosurface3d",points3d,faces3d,colors3d);
  36954. \endcode
  36955. \image html ref_isosurface3d.jpg
  36956. **/
  36957. template<typename tf>
  36958. CImg<floatT> get_isosurface3d(CImgList<tf>& primitives, const float isovalue,
  36959. const int size_x=-100, const int size_y=-100, const int size_z=-100) const {
  36960. if (_spectrum>1)
  36961. throw CImgInstanceException(_cimg_instance
  36962. "get_isosurface3d(): Instance is not a scalar image.",
  36963. cimg_instance);
  36964. primitives.assign();
  36965. if (is_empty()) return *this;
  36966. CImg<floatT> vertices;
  36967. if ((size_x==-100 && size_y==-100 && size_z==-100) || (size_x==width() && size_y==height() && size_z==depth())) {
  36968. const _functor3d_int func(*this);
  36969. vertices = isosurface3d(primitives,func,isovalue,0,0,0,width() - 1.0f,height() - 1.0f,depth() - 1.0f,
  36970. width(),height(),depth());
  36971. } else {
  36972. const _functor3d_float func(*this);
  36973. vertices = isosurface3d(primitives,func,isovalue,0,0,0,width() - 1.0f,height() - 1.0f,depth() - 1.0f,
  36974. size_x,size_y,size_z);
  36975. }
  36976. return vertices;
  36977. }
  36978. //! Compute 3d elevation of a function as a 3d object.
  36979. /**
  36980. \param[out] primitives Primitives data of the resulting 3d object.
  36981. \param func Elevation function. Is of type <tt>float (*func)(const float x,const float y)</tt>.
  36982. \param x0 X-coordinate of the starting point.
  36983. \param y0 Y-coordinate of the starting point.
  36984. \param x1 X-coordinate of the ending point.
  36985. \param y1 Y-coordinate of the ending point.
  36986. \param size_x Resolution of the function along the X-axis.
  36987. \param size_y Resolution of the function along the Y-axis.
  36988. **/
  36989. template<typename tf, typename tfunc>
  36990. static CImg<floatT> elevation3d(CImgList<tf>& primitives, const tfunc& func,
  36991. const float x0, const float y0, const float x1, const float y1,
  36992. const int size_x=256, const int size_y=256) {
  36993. const float
  36994. nx0 = x0<x1?x0:x1, ny0 = y0<y1?y0:y1,
  36995. nx1 = x0<x1?x1:x0, ny1 = y0<y1?y1:y0;
  36996. const unsigned int
  36997. _nsize_x = (unsigned int)(size_x>=0?size_x:(nx1-nx0)*-size_x/100),
  36998. nsize_x = _nsize_x?_nsize_x:1, nsize_x1 = nsize_x - 1,
  36999. _nsize_y = (unsigned int)(size_y>=0?size_y:(ny1-ny0)*-size_y/100),
  37000. nsize_y = _nsize_y?_nsize_y:1, nsize_y1 = nsize_y - 1;
  37001. if (nsize_x<2 || nsize_y<2)
  37002. throw CImgArgumentException("CImg<%s>::elevation3d(): Invalid specified size (%d,%d).",
  37003. pixel_type(),
  37004. nsize_x,nsize_y);
  37005. CImg<floatT> vertices(nsize_x*nsize_y,3);
  37006. floatT *ptr_x = vertices.data(0,0), *ptr_y = vertices.data(0,1), *ptr_z = vertices.data(0,2);
  37007. for (unsigned int y = 0; y<nsize_y; ++y) {
  37008. const float Y = ny0 + y*(ny1-ny0)/nsize_y1;
  37009. for (unsigned int x = 0; x<nsize_x; ++x) {
  37010. const float X = nx0 + x*(nx1-nx0)/nsize_x1;
  37011. *(ptr_x++) = (float)x;
  37012. *(ptr_y++) = (float)y;
  37013. *(ptr_z++) = (float)func(X,Y);
  37014. }
  37015. }
  37016. primitives.assign(nsize_x1*nsize_y1,1,4);
  37017. for (unsigned int p = 0, y = 0; y<nsize_y1; ++y) {
  37018. const unsigned int yw = y*nsize_x;
  37019. for (unsigned int x = 0; x<nsize_x1; ++x) {
  37020. const unsigned int xpyw = x + yw, xpyww = xpyw + nsize_x;
  37021. primitives[p++].fill(xpyw,xpyww,xpyww + 1,xpyw + 1);
  37022. }
  37023. }
  37024. return vertices;
  37025. }
  37026. //! Compute 3d elevation of a function, as a 3d object \overloading.
  37027. template<typename tf>
  37028. static CImg<floatT> elevation3d(CImgList<tf>& primitives, const char *const expression,
  37029. const float x0, const float y0, const float x1, const float y1,
  37030. const int size_x=256, const int size_y=256) {
  37031. const _functor2d_expr func(expression);
  37032. return elevation3d(primitives,func,x0,y0,x1,y1,size_x,size_y);
  37033. }
  37034. //! Compute 0-isolines of a function, as a 3d object.
  37035. /**
  37036. \param[out] primitives Primitives data of the resulting 3d object.
  37037. \param func Elevation function. Is of type <tt>float (*func)(const float x,const float y)</tt>.
  37038. \param isovalue Isovalue to extract from function.
  37039. \param x0 X-coordinate of the starting point.
  37040. \param y0 Y-coordinate of the starting point.
  37041. \param x1 X-coordinate of the ending point.
  37042. \param y1 Y-coordinate of the ending point.
  37043. \param size_x Resolution of the function along the X-axis.
  37044. \param size_y Resolution of the function along the Y-axis.
  37045. \note Use the marching squares algorithm for extracting the isolines.
  37046. **/
  37047. template<typename tf, typename tfunc>
  37048. static CImg<floatT> isoline3d(CImgList<tf>& primitives, const tfunc& func, const float isovalue,
  37049. const float x0, const float y0, const float x1, const float y1,
  37050. const int size_x=256, const int size_y=256) {
  37051. static const unsigned int edges[16] = { 0x0, 0x9, 0x3, 0xa, 0x6, 0xf, 0x5, 0xc, 0xc,
  37052. 0x5, 0xf, 0x6, 0xa, 0x3, 0x9, 0x0 };
  37053. static const int segments[16][4] = { { -1,-1,-1,-1 }, { 0,3,-1,-1 }, { 0,1,-1,-1 }, { 1,3,-1,-1 },
  37054. { 1,2,-1,-1 }, { 0,1,2,3 }, { 0,2,-1,-1 }, { 2,3,-1,-1 },
  37055. { 2,3,-1,-1 }, { 0,2,-1,-1}, { 0,3,1,2 }, { 1,2,-1,-1 },
  37056. { 1,3,-1,-1 }, { 0,1,-1,-1}, { 0,3,-1,-1}, { -1,-1,-1,-1 } };
  37057. const unsigned int
  37058. _nx = (unsigned int)(size_x>=0?size_x:cimg::round((x1-x0)*-size_x/100 + 1)),
  37059. _ny = (unsigned int)(size_y>=0?size_y:cimg::round((y1-y0)*-size_y/100 + 1)),
  37060. nx = _nx?_nx:1,
  37061. ny = _ny?_ny:1,
  37062. nxm1 = nx - 1,
  37063. nym1 = ny - 1;
  37064. primitives.assign();
  37065. if (!nxm1 || !nym1) return CImg<floatT>();
  37066. const float dx = (x1 - x0)/nxm1, dy = (y1 - y0)/nym1;
  37067. CImgList<floatT> vertices;
  37068. CImg<intT> indices1(nx,1,1,2,-1), indices2(nx,1,1,2);
  37069. CImg<floatT> values1(nx), values2(nx);
  37070. float X = x0, Y = y0, nX = X + dx, nY = Y + dy;
  37071. // Fill first line with values
  37072. cimg_forX(values1,x) { values1(x) = (float)func(X,Y); X+=dx; }
  37073. // Run the marching squares algorithm
  37074. for (unsigned int yi = 0, nyi = 1; yi<nym1; ++yi, ++nyi, Y=nY, nY+=dy) {
  37075. X = x0; nX = X + dx;
  37076. indices2.fill(-1);
  37077. for (unsigned int xi = 0, nxi = 1; xi<nxm1; ++xi, ++nxi, X=nX, nX+=dx) {
  37078. // Determine square configuration
  37079. const float
  37080. val0 = values1(xi),
  37081. val1 = values1(nxi),
  37082. val2 = values2(nxi) = (float)func(nX,nY),
  37083. val3 = values2(xi) = (float)func(X,nY);
  37084. const unsigned int
  37085. configuration = (val0<isovalue?1U:0U) | (val1<isovalue?2U:0U) |
  37086. (val2<isovalue?4U:0U) | (val3<isovalue?8U:0U),
  37087. edge = edges[configuration];
  37088. // Compute intersection vertices
  37089. if (edge) {
  37090. if ((edge&1) && indices1(xi,0)<0) {
  37091. const float Xi = X + (isovalue-val0)*dx/(val1-val0);
  37092. indices1(xi,0) = vertices.width();
  37093. CImg<floatT>::vector(Xi,Y,0).move_to(vertices);
  37094. }
  37095. if ((edge&2) && indices1(nxi,1)<0) {
  37096. const float Yi = Y + (isovalue-val1)*dy/(val2-val1);
  37097. indices1(nxi,1) = vertices.width();
  37098. CImg<floatT>::vector(nX,Yi,0).move_to(vertices);
  37099. }
  37100. if ((edge&4) && indices2(xi,0)<0) {
  37101. const float Xi = X + (isovalue-val3)*dx/(val2-val3);
  37102. indices2(xi,0) = vertices.width();
  37103. CImg<floatT>::vector(Xi,nY,0).move_to(vertices);
  37104. }
  37105. if ((edge&8) && indices1(xi,1)<0) {
  37106. const float Yi = Y + (isovalue-val0)*dy/(val3-val0);
  37107. indices1(xi,1) = vertices.width();
  37108. CImg<floatT>::vector(X,Yi,0).move_to(vertices);
  37109. }
  37110. // Create segments
  37111. for (const int *segment = segments[configuration]; *segment!=-1; ) {
  37112. const unsigned int p0 = (unsigned int)*(segment++), p1 = (unsigned int)*(segment++);
  37113. const tf
  37114. i0 = (tf)(_isoline3d_indice(p0,indices1,indices2,xi,nxi)),
  37115. i1 = (tf)(_isoline3d_indice(p1,indices1,indices2,xi,nxi));
  37116. CImg<tf>::vector(i0,i1).move_to(primitives);
  37117. }
  37118. }
  37119. }
  37120. values1.swap(values2);
  37121. indices1.swap(indices2);
  37122. }
  37123. return vertices>'x';
  37124. }
  37125. //! Compute isolines of a function, as a 3d object \overloading.
  37126. template<typename tf>
  37127. static CImg<floatT> isoline3d(CImgList<tf>& primitives, const char *const expression, const float isovalue,
  37128. const float x0, const float y0, const float x1, const float y1,
  37129. const int size_x=256, const int size_y=256) {
  37130. const _functor2d_expr func(expression);
  37131. return isoline3d(primitives,func,isovalue,x0,y0,x1,y1,size_x,size_y);
  37132. }
  37133. template<typename t>
  37134. static int _isoline3d_indice(const unsigned int edge, const CImg<t>& indices1, const CImg<t>& indices2,
  37135. const unsigned int x, const unsigned int nx) {
  37136. switch (edge) {
  37137. case 0 : return (int)indices1(x,0);
  37138. case 1 : return (int)indices1(nx,1);
  37139. case 2 : return (int)indices2(x,0);
  37140. case 3 : return (int)indices1(x,1);
  37141. }
  37142. return 0;
  37143. }
  37144. //! Compute isosurface of a function, as a 3d object.
  37145. /**
  37146. \param[out] primitives Primitives data of the resulting 3d object.
  37147. \param func Implicit function. Is of type <tt>float (*func)(const float x, const float y, const float z)</tt>.
  37148. \param isovalue Isovalue to extract.
  37149. \param x0 X-coordinate of the starting point.
  37150. \param y0 Y-coordinate of the starting point.
  37151. \param z0 Z-coordinate of the starting point.
  37152. \param x1 X-coordinate of the ending point.
  37153. \param y1 Y-coordinate of the ending point.
  37154. \param z1 Z-coordinate of the ending point.
  37155. \param size_x Resolution of the elevation function along the X-axis.
  37156. \param size_y Resolution of the elevation function along the Y-axis.
  37157. \param size_z Resolution of the elevation function along the Z-axis.
  37158. \note Use the marching cubes algorithm for extracting the isosurface.
  37159. **/
  37160. template<typename tf, typename tfunc>
  37161. static CImg<floatT> isosurface3d(CImgList<tf>& primitives, const tfunc& func, const float isovalue,
  37162. const float x0, const float y0, const float z0,
  37163. const float x1, const float y1, const float z1,
  37164. const int size_x=32, const int size_y=32, const int size_z=32) {
  37165. static const unsigned int edges[256] = {
  37166. 0x000, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,
  37167. 0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,
  37168. 0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,
  37169. 0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
  37170. 0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
  37171. 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,
  37172. 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c, 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,
  37173. 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,
  37174. 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,
  37175. 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, 0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
  37176. 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,
  37177. 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, 0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460,
  37178. 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0,
  37179. 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230,
  37180. 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190,
  37181. 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x000
  37182. };
  37183. static const int triangles[256][16] = {
  37184. { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37185. { 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37186. { 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37187. { 1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37188. { 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37189. { 0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37190. { 9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37191. { 2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1 },
  37192. { 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37193. { 0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37194. { 1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37195. { 1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1 },
  37196. { 3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37197. { 0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1 },
  37198. { 3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1 },
  37199. { 9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37200. { 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37201. { 4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37202. { 0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37203. { 4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1 },
  37204. { 1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37205. { 3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1 },
  37206. { 9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 },
  37207. { 2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1 },
  37208. { 8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37209. { 11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1 },
  37210. { 9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 },
  37211. { 4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1 },
  37212. { 3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1 },
  37213. { 1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1 },
  37214. { 4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1 },
  37215. { 4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1 },
  37216. { 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37217. { 9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37218. { 0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37219. { 8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1 },
  37220. { 1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37221. { 3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 },
  37222. { 5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1 },
  37223. { 2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1 },
  37224. { 9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37225. { 0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 },
  37226. { 0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 },
  37227. { 2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1 },
  37228. { 10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1 },
  37229. { 4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1 },
  37230. { 5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1 },
  37231. { 5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1 },
  37232. { 9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37233. { 9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1 },
  37234. { 0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1 },
  37235. { 1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37236. { 9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1 },
  37237. { 10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1 },
  37238. { 8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1 },
  37239. { 2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1 },
  37240. { 7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1 },
  37241. { 9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1 },
  37242. { 2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1 },
  37243. { 11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1 },
  37244. { 9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1 },
  37245. { 5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1 },
  37246. { 11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1 },
  37247. { 11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37248. { 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37249. { 0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37250. { 9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37251. { 1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 },
  37252. { 1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37253. { 1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1 },
  37254. { 9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1 },
  37255. { 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1 },
  37256. { 2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37257. { 11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 },
  37258. { 0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 },
  37259. { 5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1 },
  37260. { 6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1 },
  37261. { 0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1 },
  37262. { 3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1 },
  37263. { 6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1 },
  37264. { 5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37265. { 4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1 },
  37266. { 1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 },
  37267. { 10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1 },
  37268. { 6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1 },
  37269. { 1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1 },
  37270. { 8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1 },
  37271. { 7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1 },
  37272. { 3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 },
  37273. { 5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1 },
  37274. { 0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1 },
  37275. { 9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1 },
  37276. { 8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1 },
  37277. { 5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1 },
  37278. { 0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1 },
  37279. { 6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1 },
  37280. { 10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37281. { 4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1 },
  37282. { 10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1 },
  37283. { 8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1 },
  37284. { 1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1 },
  37285. { 3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1 },
  37286. { 0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37287. { 8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1 },
  37288. { 10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1 },
  37289. { 0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1 },
  37290. { 3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1 },
  37291. { 6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1 },
  37292. { 9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1 },
  37293. { 8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1 },
  37294. { 3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1 },
  37295. { 6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37296. { 7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1 },
  37297. { 0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1 },
  37298. { 10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1 },
  37299. { 10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1 },
  37300. { 1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1 },
  37301. { 2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1 },
  37302. { 7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1 },
  37303. { 7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37304. { 2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1 },
  37305. { 2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1 },
  37306. { 1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1 },
  37307. { 11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1 },
  37308. { 8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1 },
  37309. { 0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37310. { 7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1 },
  37311. { 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37312. { 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37313. { 3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37314. { 0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37315. { 8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 },
  37316. { 10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37317. { 1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 },
  37318. { 2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 },
  37319. { 6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1 },
  37320. { 7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37321. { 7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1 },
  37322. { 2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1 },
  37323. { 1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1 },
  37324. { 10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1 },
  37325. { 10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1 },
  37326. { 0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1 },
  37327. { 7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1 },
  37328. { 6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37329. { 3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1 },
  37330. { 8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1 },
  37331. { 9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1 },
  37332. { 6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1 },
  37333. { 1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1 },
  37334. { 4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1 },
  37335. { 10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1 },
  37336. { 8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1 },
  37337. { 0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37338. { 1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1 },
  37339. { 1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1 },
  37340. { 8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1 },
  37341. { 10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1 },
  37342. { 4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1 },
  37343. { 10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37344. { 4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37345. { 0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 },
  37346. { 5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 },
  37347. { 11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1 },
  37348. { 9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 },
  37349. { 6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1 },
  37350. { 7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1 },
  37351. { 3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1 },
  37352. { 7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1 },
  37353. { 9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1 },
  37354. { 3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1 },
  37355. { 6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1 },
  37356. { 9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1 },
  37357. { 1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1 },
  37358. { 4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1 },
  37359. { 7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1 },
  37360. { 6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1 },
  37361. { 3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1 },
  37362. { 0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1 },
  37363. { 6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1 },
  37364. { 1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1 },
  37365. { 0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1 },
  37366. { 11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1 },
  37367. { 6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1 },
  37368. { 5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1 },
  37369. { 9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1 },
  37370. { 1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1 },
  37371. { 1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37372. { 1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1 },
  37373. { 10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1 },
  37374. { 0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37375. { 10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37376. { 11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37377. { 11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1 },
  37378. { 5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1 },
  37379. { 10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1 },
  37380. { 11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1 },
  37381. { 0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1 },
  37382. { 9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1 },
  37383. { 7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1 },
  37384. { 2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1 },
  37385. { 8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1 },
  37386. { 9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1 },
  37387. { 9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1 },
  37388. { 1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37389. { 0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1 },
  37390. { 9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1 },
  37391. { 9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37392. { 5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1 },
  37393. { 5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1 },
  37394. { 0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1 },
  37395. { 10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1 },
  37396. { 2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1 },
  37397. { 0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1 },
  37398. { 0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1 },
  37399. { 9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37400. { 2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1 },
  37401. { 5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1 },
  37402. { 3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1 },
  37403. { 5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1 },
  37404. { 8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1 },
  37405. { 0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37406. { 8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1 },
  37407. { 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37408. { 4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1 },
  37409. { 0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1 },
  37410. { 1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1 },
  37411. { 3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1 },
  37412. { 4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1 },
  37413. { 9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1 },
  37414. { 11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1 },
  37415. { 11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1 },
  37416. { 2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1 },
  37417. { 9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1 },
  37418. { 3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1 },
  37419. { 1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37420. { 4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1 },
  37421. { 4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1 },
  37422. { 4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37423. { 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37424. { 9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37425. { 3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1 },
  37426. { 0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1 },
  37427. { 3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37428. { 1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1 },
  37429. { 3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1 },
  37430. { 0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37431. { 3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37432. { 2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1 },
  37433. { 9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37434. { 2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1 },
  37435. { 1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37436. { 1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37437. { 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37438. { 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  37439. { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }
  37440. };
  37441. const unsigned int
  37442. _nx = (unsigned int)(size_x>=0?size_x:cimg::round((x1-x0)*-size_x/100 + 1)),
  37443. _ny = (unsigned int)(size_y>=0?size_y:cimg::round((y1-y0)*-size_y/100 + 1)),
  37444. _nz = (unsigned int)(size_z>=0?size_z:cimg::round((z1-z0)*-size_z/100 + 1)),
  37445. nx = _nx?_nx:1,
  37446. ny = _ny?_ny:1,
  37447. nz = _nz?_nz:1,
  37448. nxm1 = nx - 1,
  37449. nym1 = ny - 1,
  37450. nzm1 = nz - 1;
  37451. primitives.assign();
  37452. if (!nxm1 || !nym1 || !nzm1) return CImg<floatT>();
  37453. const float dx = (x1 - x0)/nxm1, dy = (y1 - y0)/nym1, dz = (z1 - z0)/nzm1;
  37454. CImgList<floatT> vertices;
  37455. CImg<intT> indices1(nx,ny,1,3,-1), indices2(indices1);
  37456. CImg<floatT> values1(nx,ny), values2(nx,ny);
  37457. float X = 0, Y = 0, Z = 0, nX = 0, nY = 0, nZ = 0;
  37458. // Fill the first plane with function values
  37459. Y = y0;
  37460. cimg_forY(values1,y) {
  37461. X = x0;
  37462. cimg_forX(values1,x) { values1(x,y) = (float)func(X,Y,z0); X+=dx; }
  37463. Y+=dy;
  37464. }
  37465. // Run Marching Cubes algorithm
  37466. Z = z0; nZ = Z + dz;
  37467. for (unsigned int zi = 0; zi<nzm1; ++zi, Z = nZ, nZ+=dz) {
  37468. Y = y0; nY = Y + dy;
  37469. indices2.fill(-1);
  37470. for (unsigned int yi = 0, nyi = 1; yi<nym1; ++yi, ++nyi, Y = nY, nY+=dy) {
  37471. X = x0; nX = X + dx;
  37472. for (unsigned int xi = 0, nxi = 1; xi<nxm1; ++xi, ++nxi, X = nX, nX+=dx) {
  37473. // Determine cube configuration
  37474. const float
  37475. val0 = values1(xi,yi),
  37476. val1 = values1(nxi,yi),
  37477. val2 = values1(nxi,nyi),
  37478. val3 = values1(xi,nyi),
  37479. val4 = values2(xi,yi) = (float)func(X,Y,nZ),
  37480. val5 = values2(nxi,yi) = (float)func(nX,Y,nZ),
  37481. val6 = values2(nxi,nyi) = (float)func(nX,nY,nZ),
  37482. val7 = values2(xi,nyi) = (float)func(X,nY,nZ);
  37483. const unsigned int configuration =
  37484. (val0<isovalue?1U:0U) | (val1<isovalue?2U:0U) | (val2<isovalue?4U:0U) | (val3<isovalue?8U:0U) |
  37485. (val4<isovalue?16U:0U) | (val5<isovalue?32U:0U) | (val6<isovalue?64U:0U) | (val7<isovalue?128U:0U),
  37486. edge = edges[configuration];
  37487. // Compute intersection vertices
  37488. if (edge) {
  37489. if ((edge&1) && indices1(xi,yi,0)<0) {
  37490. const float Xi = X + (isovalue-val0)*dx/(val1-val0);
  37491. indices1(xi,yi,0) = vertices.width();
  37492. CImg<floatT>::vector(Xi,Y,Z).move_to(vertices);
  37493. }
  37494. if ((edge&2) && indices1(nxi,yi,1)<0) {
  37495. const float Yi = Y + (isovalue-val1)*dy/(val2-val1);
  37496. indices1(nxi,yi,1) = vertices.width();
  37497. CImg<floatT>::vector(nX,Yi,Z).move_to(vertices);
  37498. }
  37499. if ((edge&4) && indices1(xi,nyi,0)<0) {
  37500. const float Xi = X + (isovalue-val3)*dx/(val2-val3);
  37501. indices1(xi,nyi,0) = vertices.width();
  37502. CImg<floatT>::vector(Xi,nY,Z).move_to(vertices);
  37503. }
  37504. if ((edge&8) && indices1(xi,yi,1)<0) {
  37505. const float Yi = Y + (isovalue-val0)*dy/(val3-val0);
  37506. indices1(xi,yi,1) = vertices.width();
  37507. CImg<floatT>::vector(X,Yi,Z).move_to(vertices);
  37508. }
  37509. if ((edge&16) && indices2(xi,yi,0)<0) {
  37510. const float Xi = X + (isovalue-val4)*dx/(val5-val4);
  37511. indices2(xi,yi,0) = vertices.width();
  37512. CImg<floatT>::vector(Xi,Y,nZ).move_to(vertices);
  37513. }
  37514. if ((edge&32) && indices2(nxi,yi,1)<0) {
  37515. const float Yi = Y + (isovalue-val5)*dy/(val6-val5);
  37516. indices2(nxi,yi,1) = vertices.width();
  37517. CImg<floatT>::vector(nX,Yi,nZ).move_to(vertices);
  37518. }
  37519. if ((edge&64) && indices2(xi,nyi,0)<0) {
  37520. const float Xi = X + (isovalue-val7)*dx/(val6-val7);
  37521. indices2(xi,nyi,0) = vertices.width();
  37522. CImg<floatT>::vector(Xi,nY,nZ).move_to(vertices);
  37523. }
  37524. if ((edge&128) && indices2(xi,yi,1)<0) {
  37525. const float Yi = Y + (isovalue-val4)*dy/(val7-val4);
  37526. indices2(xi,yi,1) = vertices.width();
  37527. CImg<floatT>::vector(X,Yi,nZ).move_to(vertices);
  37528. }
  37529. if ((edge&256) && indices1(xi,yi,2)<0) {
  37530. const float Zi = Z+ (isovalue-val0)*dz/(val4-val0);
  37531. indices1(xi,yi,2) = vertices.width();
  37532. CImg<floatT>::vector(X,Y,Zi).move_to(vertices);
  37533. }
  37534. if ((edge&512) && indices1(nxi,yi,2)<0) {
  37535. const float Zi = Z + (isovalue-val1)*dz/(val5-val1);
  37536. indices1(nxi,yi,2) = vertices.width();
  37537. CImg<floatT>::vector(nX,Y,Zi).move_to(vertices);
  37538. }
  37539. if ((edge&1024) && indices1(nxi,nyi,2)<0) {
  37540. const float Zi = Z + (isovalue-val2)*dz/(val6-val2);
  37541. indices1(nxi,nyi,2) = vertices.width();
  37542. CImg<floatT>::vector(nX,nY,Zi).move_to(vertices);
  37543. }
  37544. if ((edge&2048) && indices1(xi,nyi,2)<0) {
  37545. const float Zi = Z + (isovalue-val3)*dz/(val7-val3);
  37546. indices1(xi,nyi,2) = vertices.width();
  37547. CImg<floatT>::vector(X,nY,Zi).move_to(vertices);
  37548. }
  37549. // Create triangles
  37550. for (const int *triangle = triangles[configuration]; *triangle!=-1; ) {
  37551. const unsigned int
  37552. p0 = (unsigned int)*(triangle++),
  37553. p1 = (unsigned int)*(triangle++),
  37554. p2 = (unsigned int)*(triangle++);
  37555. const tf
  37556. i0 = (tf)(_isosurface3d_indice(p0,indices1,indices2,xi,yi,nxi,nyi)),
  37557. i1 = (tf)(_isosurface3d_indice(p1,indices1,indices2,xi,yi,nxi,nyi)),
  37558. i2 = (tf)(_isosurface3d_indice(p2,indices1,indices2,xi,yi,nxi,nyi));
  37559. CImg<tf>::vector(i0,i2,i1).move_to(primitives);
  37560. }
  37561. }
  37562. }
  37563. }
  37564. cimg::swap(values1,values2);
  37565. cimg::swap(indices1,indices2);
  37566. }
  37567. return vertices>'x';
  37568. }
  37569. //! Compute isosurface of a function, as a 3d object \overloading.
  37570. template<typename tf>
  37571. static CImg<floatT> isosurface3d(CImgList<tf>& primitives, const char *const expression, const float isovalue,
  37572. const float x0, const float y0, const float z0,
  37573. const float x1, const float y1, const float z1,
  37574. const int dx=32, const int dy=32, const int dz=32) {
  37575. const _functor3d_expr func(expression);
  37576. return isosurface3d(primitives,func,isovalue,x0,y0,z0,x1,y1,z1,dx,dy,dz);
  37577. }
  37578. template<typename t>
  37579. static int _isosurface3d_indice(const unsigned int edge, const CImg<t>& indices1, const CImg<t>& indices2,
  37580. const unsigned int x, const unsigned int y,
  37581. const unsigned int nx, const unsigned int ny) {
  37582. switch (edge) {
  37583. case 0 : return indices1(x,y,0);
  37584. case 1 : return indices1(nx,y,1);
  37585. case 2 : return indices1(x,ny,0);
  37586. case 3 : return indices1(x,y,1);
  37587. case 4 : return indices2(x,y,0);
  37588. case 5 : return indices2(nx,y,1);
  37589. case 6 : return indices2(x,ny,0);
  37590. case 7 : return indices2(x,y,1);
  37591. case 8 : return indices1(x,y,2);
  37592. case 9 : return indices1(nx,y,2);
  37593. case 10 : return indices1(nx,ny,2);
  37594. case 11 : return indices1(x,ny,2);
  37595. }
  37596. return 0;
  37597. }
  37598. // Define functors for accessing image values (used in previous functions).
  37599. struct _functor2d_int {
  37600. const CImg<T>& ref;
  37601. _functor2d_int(const CImg<T>& pref):ref(pref) {}
  37602. float operator()(const float x, const float y) const {
  37603. return (float)ref((int)x,(int)y);
  37604. }
  37605. };
  37606. struct _functor2d_float {
  37607. const CImg<T>& ref;
  37608. _functor2d_float(const CImg<T>& pref):ref(pref) {}
  37609. float operator()(const float x, const float y) const {
  37610. return (float)ref._linear_atXY(x,y);
  37611. }
  37612. };
  37613. struct _functor2d_expr {
  37614. _cimg_math_parser *mp;
  37615. ~_functor2d_expr() { mp->end(); delete mp; }
  37616. _functor2d_expr(const char *const expr):mp(0) {
  37617. mp = new _cimg_math_parser(expr,0,CImg<T>::const_empty(),0);
  37618. }
  37619. float operator()(const float x, const float y) const {
  37620. return (float)(*mp)(x,y,0,0);
  37621. }
  37622. };
  37623. struct _functor3d_int {
  37624. const CImg<T>& ref;
  37625. _functor3d_int(const CImg<T>& pref):ref(pref) {}
  37626. float operator()(const float x, const float y, const float z) const {
  37627. return (float)ref((int)x,(int)y,(int)z);
  37628. }
  37629. };
  37630. struct _functor3d_float {
  37631. const CImg<T>& ref;
  37632. _functor3d_float(const CImg<T>& pref):ref(pref) {}
  37633. float operator()(const float x, const float y, const float z) const {
  37634. return (float)ref._linear_atXYZ(x,y,z);
  37635. }
  37636. };
  37637. struct _functor3d_expr {
  37638. _cimg_math_parser *mp;
  37639. ~_functor3d_expr() { mp->end(); delete mp; }
  37640. _functor3d_expr(const char *const expr):mp(0) {
  37641. mp = new _cimg_math_parser(expr,0,CImg<T>::const_empty(),0);
  37642. }
  37643. float operator()(const float x, const float y, const float z) const {
  37644. return (float)(*mp)(x,y,z,0);
  37645. }
  37646. };
  37647. struct _functor4d_int {
  37648. const CImg<T>& ref;
  37649. _functor4d_int(const CImg<T>& pref):ref(pref) {}
  37650. float operator()(const float x, const float y, const float z, const unsigned int c) const {
  37651. return (float)ref((int)x,(int)y,(int)z,c);
  37652. }
  37653. };
  37654. //! Generate a 3d box object.
  37655. /**
  37656. \param[out] primitives The returned list of the 3d object primitives
  37657. (template type \e tf should be at least \e unsigned \e int).
  37658. \param size_x The width of the box (dimension along the X-axis).
  37659. \param size_y The height of the box (dimension along the Y-axis).
  37660. \param size_z The depth of the box (dimension along the Z-axis).
  37661. \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N - 1).
  37662. \par Example
  37663. \code
  37664. CImgList<unsigned int> faces3d;
  37665. const CImg<float> points3d = CImg<float>::box3d(faces3d,10,20,30);
  37666. CImg<unsigned char>().display_object3d("Box3d",points3d,faces3d);
  37667. \endcode
  37668. \image html ref_box3d.jpg
  37669. **/
  37670. template<typename tf>
  37671. static CImg<floatT> box3d(CImgList<tf>& primitives,
  37672. const float size_x=200, const float size_y=100, const float size_z=100) {
  37673. 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);
  37674. return CImg<floatT>(8,3,1,1,
  37675. 0.,size_x,size_x, 0., 0.,size_x,size_x, 0.,
  37676. 0., 0.,size_y,size_y, 0., 0.,size_y,size_y,
  37677. 0., 0., 0., 0.,size_z,size_z,size_z,size_z);
  37678. }
  37679. //! Generate a 3d cone.
  37680. /**
  37681. \param[out] primitives The returned list of the 3d object primitives
  37682. (template type \e tf should be at least \e unsigned \e int).
  37683. \param radius The radius of the cone basis.
  37684. \param size_z The cone's height.
  37685. \param subdivisions The number of basis angular subdivisions.
  37686. \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N - 1).
  37687. \par Example
  37688. \code
  37689. CImgList<unsigned int> faces3d;
  37690. const CImg<float> points3d = CImg<float>::cone3d(faces3d,50);
  37691. CImg<unsigned char>().display_object3d("Cone3d",points3d,faces3d);
  37692. \endcode
  37693. \image html ref_cone3d.jpg
  37694. **/
  37695. template<typename tf>
  37696. static CImg<floatT> cone3d(CImgList<tf>& primitives,
  37697. const float radius=50, const float size_z=100, const unsigned int subdivisions=24) {
  37698. primitives.assign();
  37699. if (!subdivisions) return CImg<floatT>();
  37700. CImgList<floatT> vertices(2,1,3,1,1,
  37701. 0.,0.,size_z,
  37702. 0.,0.,0.);
  37703. for (float delta = 360.0f/subdivisions, angle = 0; angle<360; angle+=delta) {
  37704. const float a = (float)(angle*cimg::PI/180);
  37705. CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0).move_to(vertices);
  37706. }
  37707. const unsigned int nbr = vertices._width - 2;
  37708. for (unsigned int p = 0; p<nbr; ++p) {
  37709. const unsigned int curr = 2 + p, next = 2 + ((p + 1)%nbr);
  37710. CImg<tf>::vector(1,next,curr).move_to(primitives);
  37711. CImg<tf>::vector(0,curr,next).move_to(primitives);
  37712. }
  37713. return vertices>'x';
  37714. }
  37715. //! Generate a 3d cylinder.
  37716. /**
  37717. \param[out] primitives The returned list of the 3d object primitives
  37718. (template type \e tf should be at least \e unsigned \e int).
  37719. \param radius The radius of the cylinder basis.
  37720. \param size_z The cylinder's height.
  37721. \param subdivisions The number of basis angular subdivisions.
  37722. \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N - 1).
  37723. \par Example
  37724. \code
  37725. CImgList<unsigned int> faces3d;
  37726. const CImg<float> points3d = CImg<float>::cylinder3d(faces3d,50);
  37727. CImg<unsigned char>().display_object3d("Cylinder3d",points3d,faces3d);
  37728. \endcode
  37729. \image html ref_cylinder3d.jpg
  37730. **/
  37731. template<typename tf>
  37732. static CImg<floatT> cylinder3d(CImgList<tf>& primitives,
  37733. const float radius=50, const float size_z=100, const unsigned int subdivisions=24) {
  37734. primitives.assign();
  37735. if (!subdivisions) return CImg<floatT>();
  37736. CImgList<floatT> vertices(2,1,3,1,1,
  37737. 0.,0.,0.,
  37738. 0.,0.,size_z);
  37739. for (float delta = 360.0f/subdivisions, angle = 0; angle<360; angle+=delta) {
  37740. const float a = (float)(angle*cimg::PI/180);
  37741. CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0.0f).move_to(vertices);
  37742. CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),size_z).move_to(vertices);
  37743. }
  37744. const unsigned int nbr = (vertices._width - 2)/2;
  37745. for (unsigned int p = 0; p<nbr; ++p) {
  37746. const unsigned int curr = 2 + 2*p, next = 2 + (2*((p + 1)%nbr));
  37747. CImg<tf>::vector(0,next,curr).move_to(primitives);
  37748. CImg<tf>::vector(1,curr + 1,next + 1).move_to(primitives);
  37749. CImg<tf>::vector(curr,next,next + 1,curr + 1).move_to(primitives);
  37750. }
  37751. return vertices>'x';
  37752. }
  37753. //! Generate a 3d torus.
  37754. /**
  37755. \param[out] primitives The returned list of the 3d object primitives
  37756. (template type \e tf should be at least \e unsigned \e int).
  37757. \param radius1 The large radius.
  37758. \param radius2 The small radius.
  37759. \param subdivisions1 The number of angular subdivisions for the large radius.
  37760. \param subdivisions2 The number of angular subdivisions for the small radius.
  37761. \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N - 1).
  37762. \par Example
  37763. \code
  37764. CImgList<unsigned int> faces3d;
  37765. const CImg<float> points3d = CImg<float>::torus3d(faces3d,20,4);
  37766. CImg<unsigned char>().display_object3d("Torus3d",points3d,faces3d);
  37767. \endcode
  37768. \image html ref_torus3d.jpg
  37769. **/
  37770. template<typename tf>
  37771. static CImg<floatT> torus3d(CImgList<tf>& primitives,
  37772. const float radius1=100, const float radius2=30,
  37773. const unsigned int subdivisions1=24, const unsigned int subdivisions2=12) {
  37774. primitives.assign();
  37775. if (!subdivisions1 || !subdivisions2) return CImg<floatT>();
  37776. CImgList<floatT> vertices;
  37777. for (unsigned int v = 0; v<subdivisions1; ++v) {
  37778. const float
  37779. beta = (float)(v*2*cimg::PI/subdivisions1),
  37780. xc = radius1*(float)std::cos(beta),
  37781. yc = radius1*(float)std::sin(beta);
  37782. for (unsigned int u = 0; u<subdivisions2; ++u) {
  37783. const float
  37784. alpha = (float)(u*2*cimg::PI/subdivisions2),
  37785. x = xc + radius2*(float)(std::cos(alpha)*std::cos(beta)),
  37786. y = yc + radius2*(float)(std::cos(alpha)*std::sin(beta)),
  37787. z = radius2*(float)std::sin(alpha);
  37788. CImg<floatT>::vector(x,y,z).move_to(vertices);
  37789. }
  37790. }
  37791. for (unsigned int vv = 0; vv<subdivisions1; ++vv) {
  37792. const unsigned int nv = (vv + 1)%subdivisions1;
  37793. for (unsigned int uu = 0; uu<subdivisions2; ++uu) {
  37794. const unsigned int nu = (uu + 1)%subdivisions2, svv = subdivisions2*vv, snv = subdivisions2*nv;
  37795. CImg<tf>::vector(svv + nu,svv + uu,snv + uu,snv + nu).move_to(primitives);
  37796. }
  37797. }
  37798. return vertices>'x';
  37799. }
  37800. //! Generate a 3d XY-plane.
  37801. /**
  37802. \param[out] primitives The returned list of the 3d object primitives
  37803. (template type \e tf should be at least \e unsigned \e int).
  37804. \param size_x The width of the plane (dimension along the X-axis).
  37805. \param size_y The height of the plane (dimensions along the Y-axis).
  37806. \param subdivisions_x The number of planar subdivisions along the X-axis.
  37807. \param subdivisions_y The number of planar subdivisions along the Y-axis.
  37808. \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N - 1).
  37809. \par Example
  37810. \code
  37811. CImgList<unsigned int> faces3d;
  37812. const CImg<float> points3d = CImg<float>::plane3d(faces3d,100,50);
  37813. CImg<unsigned char>().display_object3d("Plane3d",points3d,faces3d);
  37814. \endcode
  37815. \image html ref_plane3d.jpg
  37816. **/
  37817. template<typename tf>
  37818. static CImg<floatT> plane3d(CImgList<tf>& primitives,
  37819. const float size_x=100, const float size_y=100,
  37820. const unsigned int subdivisions_x=10, const unsigned int subdivisions_y=10) {
  37821. primitives.assign();
  37822. if (!subdivisions_x || !subdivisions_y) return CImg<floatT>();
  37823. CImgList<floatT> vertices;
  37824. const unsigned int w = subdivisions_x + 1, h = subdivisions_y + 1;
  37825. const float fx = (float)size_x/w, fy = (float)size_y/h;
  37826. for (unsigned int y = 0; y<h; ++y) for (unsigned int x = 0; x<w; ++x)
  37827. CImg<floatT>::vector(fx*x,fy*y,0).move_to(vertices);
  37828. for (unsigned int y = 0; y<subdivisions_y; ++y) for (unsigned int x = 0; x<subdivisions_x; ++x) {
  37829. const int off1 = x + y*w, off2 = x + 1 + y*w, off3 = x + 1 + (y + 1)*w, off4 = x + (y + 1)*w;
  37830. CImg<tf>::vector(off1,off4,off3,off2).move_to(primitives);
  37831. }
  37832. return vertices>'x';
  37833. }
  37834. //! Generate a 3d sphere.
  37835. /**
  37836. \param[out] primitives The returned list of the 3d object primitives
  37837. (template type \e tf should be at least \e unsigned \e int).
  37838. \param radius The radius of the sphere (dimension along the X-axis).
  37839. \param subdivisions The number of recursive subdivisions from an initial icosahedron.
  37840. \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N - 1).
  37841. \par Example
  37842. \code
  37843. CImgList<unsigned int> faces3d;
  37844. const CImg<float> points3d = CImg<float>::sphere3d(faces3d,100,4);
  37845. CImg<unsigned char>().display_object3d("Sphere3d",points3d,faces3d);
  37846. \endcode
  37847. \image html ref_sphere3d.jpg
  37848. **/
  37849. template<typename tf>
  37850. static CImg<floatT> sphere3d(CImgList<tf>& primitives,
  37851. const float radius=50, const unsigned int subdivisions=3) {
  37852. // Create initial icosahedron
  37853. primitives.assign();
  37854. const double tmp = (1 + std::sqrt(5.0f))/2, a = 1.0/std::sqrt(1 + tmp*tmp), b = tmp*a;
  37855. CImgList<floatT> vertices(12,1,3,1,1, b,a,0.0, -b,a,0.0, -b,-a,0.0, b,-a,0.0, a,0.0,b, a,0.0,-b,
  37856. -a,0.0,-b, -a,0.0,b, 0.0,b,a, 0.0,-b,a, 0.0,-b,-a, 0.0,b,-a);
  37857. 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,
  37858. 8,0,11, 8,11,1, 9,10,3, 9,2,10, 8,4,0, 11,0,5, 4,9,3,
  37859. 5,3,10, 7,8,1, 6,1,11, 7,2,9, 6,10,2);
  37860. // edge - length/2
  37861. float he = (float)a;
  37862. // Recurse subdivisions
  37863. for (unsigned int i = 0; i<subdivisions; ++i) {
  37864. const unsigned int L = primitives._width;
  37865. he/=2;
  37866. const float he2 = he*he;
  37867. for (unsigned int l = 0; l<L; ++l) {
  37868. const unsigned int
  37869. p0 = (unsigned int)primitives(0,0), p1 = (unsigned int)primitives(0,1), p2 = (unsigned int)primitives(0,2);
  37870. const float
  37871. x0 = vertices(p0,0), y0 = vertices(p0,1), z0 = vertices(p0,2),
  37872. x1 = vertices(p1,0), y1 = vertices(p1,1), z1 = vertices(p1,2),
  37873. x2 = vertices(p2,0), y2 = vertices(p2,1), z2 = vertices(p2,2),
  37874. tnx0 = (x0 + x1)/2, tny0 = (y0 + y1)/2, tnz0 = (z0 + z1)/2,
  37875. nn0 = cimg::hypot(tnx0,tny0,tnz0),
  37876. tnx1 = (x0 + x2)/2, tny1 = (y0 + y2)/2, tnz1 = (z0 + z2)/2,
  37877. nn1 = cimg::hypot(tnx1,tny1,tnz1),
  37878. tnx2 = (x1 + x2)/2, tny2 = (y1 + y2)/2, tnz2 = (z1 + z2)/2,
  37879. nn2 = cimg::hypot(tnx2,tny2,tnz2),
  37880. nx0 = tnx0/nn0, ny0 = tny0/nn0, nz0 = tnz0/nn0,
  37881. nx1 = tnx1/nn1, ny1 = tny1/nn1, nz1 = tnz1/nn1,
  37882. nx2 = tnx2/nn2, ny2 = tny2/nn2, nz2 = tnz2/nn2;
  37883. int i0 = -1, i1 = -1, i2 = -1;
  37884. cimglist_for(vertices,p) {
  37885. const float x = (float)vertices(p,0), y = (float)vertices(p,1), z = (float)vertices(p,2);
  37886. if (cimg::sqr(x-nx0) + cimg::sqr(y-ny0) + cimg::sqr(z-nz0)<he2) i0 = p;
  37887. if (cimg::sqr(x-nx1) + cimg::sqr(y-ny1) + cimg::sqr(z-nz1)<he2) i1 = p;
  37888. if (cimg::sqr(x-nx2) + cimg::sqr(y-ny2) + cimg::sqr(z-nz2)<he2) i2 = p;
  37889. }
  37890. if (i0<0) { CImg<floatT>::vector(nx0,ny0,nz0).move_to(vertices); i0 = vertices.width() - 1; }
  37891. if (i1<0) { CImg<floatT>::vector(nx1,ny1,nz1).move_to(vertices); i1 = vertices.width() - 1; }
  37892. if (i2<0) { CImg<floatT>::vector(nx2,ny2,nz2).move_to(vertices); i2 = vertices.width() - 1; }
  37893. primitives.remove(0);
  37894. CImg<tf>::vector(p0,i0,i1).move_to(primitives);
  37895. CImg<tf>::vector((tf)i0,(tf)p1,(tf)i2).move_to(primitives);
  37896. CImg<tf>::vector((tf)i1,(tf)i2,(tf)p2).move_to(primitives);
  37897. CImg<tf>::vector((tf)i1,(tf)i0,(tf)i2).move_to(primitives);
  37898. }
  37899. }
  37900. return (vertices>'x')*=radius;
  37901. }
  37902. //! Generate a 3d ellipsoid.
  37903. /**
  37904. \param[out] primitives The returned list of the 3d object primitives
  37905. (template type \e tf should be at least \e unsigned \e int).
  37906. \param tensor The tensor which gives the shape and size of the ellipsoid.
  37907. \param subdivisions The number of recursive subdivisions from an initial stretched icosahedron.
  37908. \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N - 1).
  37909. \par Example
  37910. \code
  37911. CImgList<unsigned int> faces3d;
  37912. const CImg<float> tensor = CImg<float>::diagonal(10,7,3),
  37913. points3d = CImg<float>::ellipsoid3d(faces3d,tensor,4);
  37914. CImg<unsigned char>().display_object3d("Ellipsoid3d",points3d,faces3d);
  37915. \endcode
  37916. \image html ref_ellipsoid3d.jpg
  37917. **/
  37918. template<typename tf, typename t>
  37919. static CImg<floatT> ellipsoid3d(CImgList<tf>& primitives,
  37920. const CImg<t>& tensor, const unsigned int subdivisions=3) {
  37921. primitives.assign();
  37922. if (!subdivisions) return CImg<floatT>();
  37923. CImg<floatT> S, V;
  37924. tensor.symmetric_eigen(S,V);
  37925. const float orient =
  37926. (V(0,1)*V(1,2) - V(0,2)*V(1,1))*V(2,0) +
  37927. (V(0,2)*V(1,0) - V(0,0)*V(1,2))*V(2,1) +
  37928. (V(0,0)*V(1,1) - V(0,1)*V(1,0))*V(2,2);
  37929. if (orient<0) { V(2,0) = -V(2,0); V(2,1) = -V(2,1); V(2,2) = -V(2,2); }
  37930. const float l0 = S[0], l1 = S[1], l2 = S[2];
  37931. CImg<floatT> vertices = sphere3d(primitives,1.0,subdivisions);
  37932. vertices.get_shared_row(0)*=l0;
  37933. vertices.get_shared_row(1)*=l1;
  37934. vertices.get_shared_row(2)*=l2;
  37935. return V*vertices;
  37936. }
  37937. //! Convert 3d object into a CImg3d representation.
  37938. /**
  37939. \param primitives Primitives data of the 3d object.
  37940. \param colors Colors data of the 3d object.
  37941. \param opacities Opacities data of the 3d object.
  37942. \param full_check Tells if full checking of the 3d object must be performed.
  37943. **/
  37944. template<typename tp, typename tc, typename to>
  37945. CImg<T>& object3dtoCImg3d(const CImgList<tp>& primitives,
  37946. const CImgList<tc>& colors,
  37947. const to& opacities,
  37948. const bool full_check=true) {
  37949. return get_object3dtoCImg3d(primitives,colors,opacities,full_check).move_to(*this);
  37950. }
  37951. //! Convert 3d object into a CImg3d representation \overloading.
  37952. template<typename tp, typename tc>
  37953. CImg<T>& object3dtoCImg3d(const CImgList<tp>& primitives,
  37954. const CImgList<tc>& colors,
  37955. const bool full_check=true) {
  37956. return get_object3dtoCImg3d(primitives,colors,full_check).move_to(*this);
  37957. }
  37958. //! Convert 3d object into a CImg3d representation \overloading.
  37959. template<typename tp>
  37960. CImg<T>& object3dtoCImg3d(const CImgList<tp>& primitives,
  37961. const bool full_check=true) {
  37962. return get_object3dtoCImg3d(primitives,full_check).move_to(*this);
  37963. }
  37964. //! Convert 3d object into a CImg3d representation \overloading.
  37965. CImg<T>& object3dtoCImg3d(const bool full_check=true) {
  37966. return get_object3dtoCImg3d(full_check).move_to(*this);
  37967. }
  37968. //! Convert 3d object into a CImg3d representation \newinstance.
  37969. template<typename tp, typename tc, typename to>
  37970. CImg<floatT> get_object3dtoCImg3d(const CImgList<tp>& primitives,
  37971. const CImgList<tc>& colors,
  37972. const to& opacities,
  37973. const bool full_check=true) const {
  37974. CImg<charT> error_message(1024);
  37975. if (!is_object3d(primitives,colors,opacities,full_check,error_message))
  37976. throw CImgInstanceException(_cimg_instance
  37977. "object3dtoCImg3d(): Invalid specified 3d object (%u,%u) (%s).",
  37978. cimg_instance,_width,primitives._width,error_message.data());
  37979. CImg<floatT> res(1,_size_object3dtoCImg3d(primitives,colors,opacities));
  37980. float *ptrd = res._data;
  37981. // Put magick number.
  37982. *(ptrd++) = 'C' + 0.5f; *(ptrd++) = 'I' + 0.5f; *(ptrd++) = 'm' + 0.5f;
  37983. *(ptrd++) = 'g' + 0.5f; *(ptrd++) = '3' + 0.5f; *(ptrd++) = 'd' + 0.5f;
  37984. // Put number of vertices and primitives.
  37985. *(ptrd++) = cimg::uint2float(_width);
  37986. *(ptrd++) = cimg::uint2float(primitives._width);
  37987. // Put vertex data.
  37988. if (is_empty() || !primitives) return res;
  37989. const T *ptrx = data(0,0), *ptry = data(0,1), *ptrz = data(0,2);
  37990. cimg_forX(*this,p) {
  37991. *(ptrd++) = (float)*(ptrx++);
  37992. *(ptrd++) = (float)*(ptry++);
  37993. *(ptrd++) = (float)*(ptrz++);
  37994. }
  37995. // Put primitive data.
  37996. cimglist_for(primitives,p) {
  37997. *(ptrd++) = (float)primitives[p].size();
  37998. const tp *ptrp = primitives[p]._data;
  37999. cimg_foroff(primitives[p],i) *(ptrd++) = cimg::uint2float((unsigned int)*(ptrp++));
  38000. }
  38001. // Put color/texture data.
  38002. const unsigned int csiz = std::min(colors._width,primitives._width);
  38003. for (int c = 0; c<(int)csiz; ++c) {
  38004. const CImg<tc>& color = colors[c];
  38005. const tc *ptrc = color._data;
  38006. if (color.size()==3) { *(ptrd++) = (float)*(ptrc++); *(ptrd++) = (float)*(ptrc++); *(ptrd++) = (float)*ptrc; }
  38007. else {
  38008. *(ptrd++) = -128.0f;
  38009. int shared_ind = -1;
  38010. if (color.is_shared()) for (int i = 0; i<c; ++i) if (ptrc==colors[i]._data) { shared_ind = i; break; }
  38011. if (shared_ind<0) {
  38012. *(ptrd++) = (float)color._width;
  38013. *(ptrd++) = (float)color._height;
  38014. *(ptrd++) = (float)color._spectrum;
  38015. cimg_foroff(color,l) *(ptrd++) = (float)*(ptrc++);
  38016. } else {
  38017. *(ptrd++) = (float)shared_ind;
  38018. *(ptrd++) = 0;
  38019. *(ptrd++) = 0;
  38020. }
  38021. }
  38022. }
  38023. const int csiz2 = primitives.width() - colors.width();
  38024. for (int c = 0; c<csiz2; ++c) { *(ptrd++) = 200.0f; *(ptrd++) = 200.0f; *(ptrd++) = 200.0f; }
  38025. // Put opacity data.
  38026. ptrd = _object3dtoCImg3d(opacities,ptrd);
  38027. const float *ptre = res.end();
  38028. while (ptrd<ptre) *(ptrd++) = 1.0f;
  38029. return res;
  38030. }
  38031. template<typename to>
  38032. float* _object3dtoCImg3d(const CImgList<to>& opacities, float *ptrd) const {
  38033. cimglist_for(opacities,o) {
  38034. const CImg<to>& opacity = opacities[o];
  38035. const to *ptro = opacity._data;
  38036. if (opacity.size()==1) *(ptrd++) = (float)*ptro;
  38037. else {
  38038. *(ptrd++) = -128.0f;
  38039. int shared_ind = -1;
  38040. if (opacity.is_shared()) for (int i = 0; i<o; ++i) if (ptro==opacities[i]._data) { shared_ind = i; break; }
  38041. if (shared_ind<0) {
  38042. *(ptrd++) = (float)opacity._width;
  38043. *(ptrd++) = (float)opacity._height;
  38044. *(ptrd++) = (float)opacity._spectrum;
  38045. cimg_foroff(opacity,l) *(ptrd++) = (float)*(ptro++);
  38046. } else {
  38047. *(ptrd++) = (float)shared_ind;
  38048. *(ptrd++) = 0;
  38049. *(ptrd++) = 0;
  38050. }
  38051. }
  38052. }
  38053. return ptrd;
  38054. }
  38055. template<typename to>
  38056. float* _object3dtoCImg3d(const CImg<to>& opacities, float *ptrd) const {
  38057. const to *ptro = opacities._data;
  38058. cimg_foroff(opacities,o) *(ptrd++) = (float)*(ptro++);
  38059. return ptrd;
  38060. }
  38061. template<typename tp, typename tc, typename to>
  38062. unsigned int _size_object3dtoCImg3d(const CImgList<tp>& primitives,
  38063. const CImgList<tc>& colors,
  38064. const CImgList<to>& opacities) const {
  38065. unsigned int siz = 8U + 3*_width;
  38066. cimglist_for(primitives,p) siz+=primitives[p].size() + 1;
  38067. for (int c = std::min(primitives.width(),colors.width()) - 1; c>=0; --c) {
  38068. if (colors[c].is_shared()) siz+=4;
  38069. else { const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4 + csiz:3; }
  38070. }
  38071. if (colors._width<primitives._width) siz+=3*(primitives._width - colors._width);
  38072. cimglist_for(opacities,o) {
  38073. if (opacities[o].is_shared()) siz+=4;
  38074. else { const unsigned int osiz = opacities[o].size(); siz+=(osiz!=1)?4 + osiz:1; }
  38075. }
  38076. siz+=primitives._width - opacities._width;
  38077. return siz;
  38078. }
  38079. template<typename tp, typename tc, typename to>
  38080. unsigned int _size_object3dtoCImg3d(const CImgList<tp>& primitives,
  38081. const CImgList<tc>& colors,
  38082. const CImg<to>& opacities) const {
  38083. unsigned int siz = 8U + 3*_width;
  38084. cimglist_for(primitives,p) siz+=primitives[p].size() + 1;
  38085. for (int c = std::min(primitives.width(),colors.width()) - 1; c>=0; --c) {
  38086. const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4 + csiz:3;
  38087. }
  38088. if (colors._width<primitives._width) siz+=3*(primitives._width - colors._width);
  38089. siz+=primitives.size();
  38090. cimg::unused(opacities);
  38091. return siz;
  38092. }
  38093. //! Convert 3d object into a CImg3d representation \overloading.
  38094. template<typename tp, typename tc>
  38095. CImg<floatT> get_object3dtoCImg3d(const CImgList<tp>& primitives,
  38096. const CImgList<tc>& colors,
  38097. const bool full_check=true) const {
  38098. CImgList<T> opacities;
  38099. return get_object3dtoCImg3d(primitives,colors,opacities,full_check);
  38100. }
  38101. //! Convert 3d object into a CImg3d representation \overloading.
  38102. template<typename tp>
  38103. CImg<floatT> get_object3dtoCImg3d(const CImgList<tp>& primitives,
  38104. const bool full_check=true) const {
  38105. CImgList<T> colors, opacities;
  38106. return get_object3dtoCImg3d(primitives,colors,opacities,full_check);
  38107. }
  38108. //! Convert 3d object into a CImg3d representation \overloading.
  38109. CImg<floatT> get_object3dtoCImg3d(const bool full_check=true) const {
  38110. CImgList<T> opacities, colors;
  38111. CImgList<uintT> primitives(width(),1,1,1,1);
  38112. cimglist_for(primitives,p) primitives(p,0) = p;
  38113. return get_object3dtoCImg3d(primitives,colors,opacities,full_check);
  38114. }
  38115. //! Convert CImg3d representation into a 3d object.
  38116. /**
  38117. \param[out] primitives Primitives data of the 3d object.
  38118. \param[out] colors Colors data of the 3d object.
  38119. \param[out] opacities Opacities data of the 3d object.
  38120. \param full_check Tells if full checking of the 3d object must be performed.
  38121. **/
  38122. template<typename tp, typename tc, typename to>
  38123. CImg<T>& CImg3dtoobject3d(CImgList<tp>& primitives,
  38124. CImgList<tc>& colors,
  38125. CImgList<to>& opacities,
  38126. const bool full_check=true) {
  38127. return get_CImg3dtoobject3d(primitives,colors,opacities,full_check).move_to(*this);
  38128. }
  38129. //! Convert CImg3d representation into a 3d object \newinstance.
  38130. template<typename tp, typename tc, typename to>
  38131. CImg<T> get_CImg3dtoobject3d(CImgList<tp>& primitives,
  38132. CImgList<tc>& colors,
  38133. CImgList<to>& opacities,
  38134. const bool full_check=true) const {
  38135. CImg<charT> error_message(1024);
  38136. if (!is_CImg3d(full_check,error_message))
  38137. throw CImgInstanceException(_cimg_instance
  38138. "CImg3dtoobject3d(): image instance is not a CImg3d (%s).",
  38139. cimg_instance,error_message.data());
  38140. const T *ptrs = _data + 6;
  38141. const unsigned int
  38142. nb_points = cimg::float2uint((float)*(ptrs++)),
  38143. nb_primitives = cimg::float2uint((float)*(ptrs++));
  38144. const CImg<T> points = CImg<T>(ptrs,3,nb_points,1,1,true).get_transpose();
  38145. ptrs+=3*nb_points;
  38146. primitives.assign(nb_primitives);
  38147. cimglist_for(primitives,p) {
  38148. const unsigned int nb_inds = (unsigned int)*(ptrs++);
  38149. primitives[p].assign(1,nb_inds);
  38150. tp *ptrp = primitives[p]._data;
  38151. for (unsigned int i = 0; i<nb_inds; ++i) *(ptrp++) = (tp)cimg::float2uint((float)*(ptrs++));
  38152. }
  38153. colors.assign(nb_primitives);
  38154. cimglist_for(colors,c) {
  38155. if (*ptrs==(T)-128) {
  38156. ++ptrs;
  38157. const unsigned int w = (unsigned int)*(ptrs++), h = (unsigned int)*(ptrs++), s = (unsigned int)*(ptrs++);
  38158. if (!h && !s) colors[c].assign(colors[w],true);
  38159. else { colors[c].assign(ptrs,w,h,1,s,false); ptrs+=w*h*s; }
  38160. } else { colors[c].assign(ptrs,1,1,1,3,false); ptrs+=3; }
  38161. }
  38162. opacities.assign(nb_primitives);
  38163. cimglist_for(opacities,o) {
  38164. if (*ptrs==(T)-128) {
  38165. ++ptrs;
  38166. const unsigned int w = (unsigned int)*(ptrs++), h = (unsigned int)*(ptrs++), s = (unsigned int)*(ptrs++);
  38167. if (!h && !s) opacities[o].assign(opacities[w],true);
  38168. else { opacities[o].assign(ptrs,w,h,1,s,false); ptrs+=w*h*s; }
  38169. } else opacities[o].assign(1,1,1,1,*(ptrs++));
  38170. }
  38171. return points;
  38172. }
  38173. //@}
  38174. //---------------------------
  38175. //
  38176. //! \name Drawing Functions
  38177. //@{
  38178. //---------------------------
  38179. #define cimg_init_scanline(color,opacity) \
  38180. const float _sc_nopacity = cimg::abs((float)opacity), _sc_copacity = 1 - std::max((float)opacity,0.0f); \
  38181. const ulongT _sc_whd = (ulongT)_width*_height*_depth
  38182. #define cimg_draw_scanline(x0,x1,y,color,opacity,brightness) \
  38183. _draw_scanline(x0,x1,y,color,opacity,brightness,_sc_nopacity,_sc_copacity,_sc_whd)
  38184. // [internal] The following _draw_scanline() routines are *non user-friendly functions*,
  38185. // used only for internal purpose.
  38186. // Pre-requisites: x0<=x1, y-coordinate is valid, col is valid.
  38187. template<typename tc>
  38188. CImg<T>& _draw_scanline(const int x0, const int x1, const int y,
  38189. const tc *const color, const float opacity,
  38190. const float brightness,
  38191. const float nopacity, const float copacity, const ulongT whd) {
  38192. static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max());
  38193. const int nx0 = x0>0?x0:0, nx1 = x1<width()?x1:width() - 1, dx = nx1 - nx0;
  38194. if (dx>=0) {
  38195. const tc *col = color;
  38196. const ulongT off = whd - dx - 1;
  38197. T *ptrd = data(nx0,y);
  38198. if (opacity>=1) { // ** Opaque drawing **
  38199. if (brightness==1) { // Brightness==1
  38200. if (sizeof(T)!=1) cimg_forC(*this,c) {
  38201. const T val = (T)*(col++);
  38202. for (int x = dx; x>=0; --x) *(ptrd++) = val;
  38203. ptrd+=off;
  38204. } else cimg_forC(*this,c) {
  38205. const T val = (T)*(col++);
  38206. std::memset(ptrd,(int)val,dx + 1);
  38207. ptrd+=whd;
  38208. }
  38209. } else if (brightness<1) { // Brightness<1
  38210. if (sizeof(T)!=1) cimg_forC(*this,c) {
  38211. const T val = (T)(*(col++)*brightness);
  38212. for (int x = dx; x>=0; --x) *(ptrd++) = val;
  38213. ptrd+=off;
  38214. } else cimg_forC(*this,c) {
  38215. const T val = (T)(*(col++)*brightness);
  38216. std::memset(ptrd,(int)val,dx + 1);
  38217. ptrd+=whd;
  38218. }
  38219. } else { // Brightness>1
  38220. if (sizeof(T)!=1) cimg_forC(*this,c) {
  38221. const T val = (T)((2-brightness)**(col++) + (brightness - 1)*maxval);
  38222. for (int x = dx; x>=0; --x) *(ptrd++) = val;
  38223. ptrd+=off;
  38224. } else cimg_forC(*this,c) {
  38225. const T val = (T)((2-brightness)**(col++) + (brightness - 1)*maxval);
  38226. std::memset(ptrd,(int)val,dx + 1);
  38227. ptrd+=whd;
  38228. }
  38229. }
  38230. } else { // ** Transparent drawing **
  38231. if (brightness==1) { // Brightness==1
  38232. cimg_forC(*this,c) {
  38233. const Tfloat val = *(col++)*nopacity;
  38234. for (int x = dx; x>=0; --x) { *ptrd = (T)(val + *ptrd*copacity); ++ptrd; }
  38235. ptrd+=off;
  38236. }
  38237. } else if (brightness<=1) { // Brightness<1
  38238. cimg_forC(*this,c) {
  38239. const Tfloat val = *(col++)*brightness*nopacity;
  38240. for (int x = dx; x>=0; --x) { *ptrd = (T)(val + *ptrd*copacity); ++ptrd; }
  38241. ptrd+=off;
  38242. }
  38243. } else { // Brightness>1
  38244. cimg_forC(*this,c) {
  38245. const Tfloat val = ((2-brightness)**(col++) + (brightness - 1)*maxval)*nopacity;
  38246. for (int x = dx; x>=0; --x) { *ptrd = (T)(val + *ptrd*copacity); ++ptrd; }
  38247. ptrd+=off;
  38248. }
  38249. }
  38250. }
  38251. }
  38252. return *this;
  38253. }
  38254. //! Draw a 3d point.
  38255. /**
  38256. \param x0 X-coordinate of the point.
  38257. \param y0 Y-coordinate of the point.
  38258. \param z0 Z-coordinate of the point.
  38259. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  38260. \param opacity Drawing opacity.
  38261. \note
  38262. - To set pixel values without clipping needs, you should use the faster CImg::operator()() function.
  38263. \par Example:
  38264. \code
  38265. CImg<unsigned char> img(100,100,1,3,0);
  38266. const unsigned char color[] = { 255,128,64 };
  38267. img.draw_point(50,50,color);
  38268. \endcode
  38269. **/
  38270. template<typename tc>
  38271. CImg<T>& draw_point(const int x0, const int y0, const int z0,
  38272. const tc *const color, const float opacity=1) {
  38273. if (is_empty()) return *this;
  38274. if (!color)
  38275. throw CImgArgumentException(_cimg_instance
  38276. "draw_point(): Specified color is (null).",
  38277. cimg_instance);
  38278. if (x0>=0 && y0>=0 && z0>=0 && x0<width() && y0<height() && z0<depth()) {
  38279. const ulongT whd = (ulongT)_width*_height*_depth;
  38280. const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
  38281. T *ptrd = data(x0,y0,z0,0);
  38282. const tc *col = color;
  38283. if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; }
  38284. else cimg_forC(*this,c) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whd; }
  38285. }
  38286. return *this;
  38287. }
  38288. //! Draw a 2d point \simplification.
  38289. template<typename tc>
  38290. CImg<T>& draw_point(const int x0, const int y0,
  38291. const tc *const color, const float opacity=1) {
  38292. return draw_point(x0,y0,0,color,opacity);
  38293. }
  38294. // Draw a points cloud.
  38295. /**
  38296. \param points Image of vertices coordinates.
  38297. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  38298. \param opacity Drawing opacity.
  38299. **/
  38300. template<typename t, typename tc>
  38301. CImg<T>& draw_point(const CImg<t>& points,
  38302. const tc *const color, const float opacity=1) {
  38303. if (is_empty() || !points) return *this;
  38304. switch (points._height) {
  38305. case 0 : case 1 :
  38306. throw CImgArgumentException(_cimg_instance
  38307. "draw_point(): Invalid specified point set (%u,%u,%u,%u,%p).",
  38308. cimg_instance,
  38309. points._width,points._height,points._depth,points._spectrum,points._data);
  38310. case 2 : {
  38311. cimg_forX(points,i) draw_point((int)points(i,0),(int)points(i,1),color,opacity);
  38312. } break;
  38313. default : {
  38314. cimg_forX(points,i) draw_point((int)points(i,0),(int)points(i,1),(int)points(i,2),color,opacity);
  38315. }
  38316. }
  38317. return *this;
  38318. }
  38319. //! Draw a 2d line.
  38320. /**
  38321. \param x0 X-coordinate of the starting line point.
  38322. \param y0 Y-coordinate of the starting line point.
  38323. \param x1 X-coordinate of the ending line point.
  38324. \param y1 Y-coordinate of the ending line point.
  38325. \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
  38326. \param opacity Drawing opacity.
  38327. \param pattern An integer whose bits describe the line pattern.
  38328. \param init_hatch Tells if a reinitialization of the hash state must be done.
  38329. \note
  38330. - Line routine uses Bresenham's algorithm.
  38331. - Set \p init_hatch = false to draw consecutive hatched segments without breaking the line pattern.
  38332. \par Example:
  38333. \code
  38334. CImg<unsigned char> img(100,100,1,3,0);
  38335. const unsigned char color[] = { 255,128,64 };
  38336. img.draw_line(40,40,80,70,color);
  38337. \endcode
  38338. **/
  38339. template<typename tc>
  38340. CImg<T>& draw_line(const int x0, const int y0,
  38341. const int x1, const int y1,
  38342. const tc *const color, const float opacity=1,
  38343. const unsigned int pattern=~0U, const bool init_hatch=true) {
  38344. if (is_empty()) return *this;
  38345. if (!color)
  38346. throw CImgArgumentException(_cimg_instance
  38347. "draw_line(): Specified color is (null).",
  38348. cimg_instance);
  38349. static unsigned int hatch = ~0U - (~0U>>1);
  38350. if (init_hatch) hatch = ~0U - (~0U>>1);
  38351. const bool xdir = x0<x1, ydir = y0<y1;
  38352. int
  38353. nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
  38354. &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
  38355. &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
  38356. &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
  38357. &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
  38358. if (xright<0 || xleft>=width()) return *this;
  38359. if (xleft<0) { yleft-=(int)((float)xleft*((float)yright - yleft)/((float)xright - xleft)); xleft = 0; }
  38360. if (xright>=width()) {
  38361. yright-=(int)(((float)xright - width())*((float)yright - yleft)/((float)xright - xleft));
  38362. xright = width() - 1;
  38363. }
  38364. if (ydown<0 || yup>=height()) return *this;
  38365. if (yup<0) { xup-=(int)((float)yup*((float)xdown - xup)/((float)ydown - yup)); yup = 0; }
  38366. if (ydown>=height()) {
  38367. xdown-=(int)(((float)ydown - height())*((float)xdown - xup)/((float)ydown - yup));
  38368. ydown = height() - 1;
  38369. }
  38370. T *ptrd0 = data(nx0,ny0);
  38371. int dx = xright - xleft, dy = ydown - yup;
  38372. const bool steep = dy>dx;
  38373. if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
  38374. const longT
  38375. offx = (longT)(nx0<nx1?1:-1)*(steep?width():1),
  38376. offy = (longT)(ny0<ny1?1:-1)*(steep?1:width());
  38377. const ulongT wh = (ulongT)_width*_height;
  38378. if (opacity>=1) {
  38379. if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
  38380. if (pattern&hatch) {
  38381. T *ptrd = ptrd0; const tc* col = color;
  38382. cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=wh; }
  38383. }
  38384. hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
  38385. ptrd0+=offx;
  38386. if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
  38387. } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
  38388. T *ptrd = ptrd0; const tc* col = color; cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=wh; }
  38389. ptrd0+=offx;
  38390. if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
  38391. }
  38392. } else {
  38393. const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
  38394. if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
  38395. if (pattern&hatch) {
  38396. T *ptrd = ptrd0; const tc* col = color;
  38397. cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; }
  38398. }
  38399. hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
  38400. ptrd0+=offx;
  38401. if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
  38402. } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
  38403. T *ptrd = ptrd0; const tc* col = color;
  38404. cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; }
  38405. ptrd0+=offx;
  38406. if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
  38407. }
  38408. }
  38409. return *this;
  38410. }
  38411. //! Draw a 2d line, with z-buffering.
  38412. /**
  38413. \param zbuffer Zbuffer image.
  38414. \param x0 X-coordinate of the starting point.
  38415. \param y0 Y-coordinate of the starting point.
  38416. \param z0 Z-coordinate of the starting point
  38417. \param x1 X-coordinate of the ending point.
  38418. \param y1 Y-coordinate of the ending point.
  38419. \param z1 Z-coordinate of the ending point.
  38420. \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
  38421. \param opacity Drawing opacity.
  38422. \param pattern An integer whose bits describe the line pattern.
  38423. \param init_hatch Tells if a reinitialization of the hash state must be done.
  38424. **/
  38425. template<typename tz,typename tc>
  38426. CImg<T>& draw_line(CImg<tz>& zbuffer,
  38427. const int x0, const int y0, const float z0,
  38428. const int x1, const int y1, const float z1,
  38429. const tc *const color, const float opacity=1,
  38430. const unsigned int pattern=~0U, const bool init_hatch=true) {
  38431. typedef typename cimg::superset<tz,float>::type tzfloat;
  38432. if (is_empty() || z0<=0 || z1<=0) return *this;
  38433. if (!color)
  38434. throw CImgArgumentException(_cimg_instance
  38435. "draw_line(): Specified color is (null).",
  38436. cimg_instance);
  38437. if (!is_sameXY(zbuffer))
  38438. throw CImgArgumentException(_cimg_instance
  38439. "draw_line(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
  38440. "different dimensions.",
  38441. cimg_instance,
  38442. zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
  38443. static unsigned int hatch = ~0U - (~0U>>1);
  38444. if (init_hatch) hatch = ~0U - (~0U>>1);
  38445. const bool xdir = x0<x1, ydir = y0<y1;
  38446. int
  38447. nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
  38448. &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
  38449. &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
  38450. &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
  38451. &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
  38452. tzfloat
  38453. Z0 = 1/(tzfloat)z0, Z1 = 1/(tzfloat)z1, nz0 = Z0, nz1 = Z1, dz = Z1 - Z0,
  38454. &zleft = xdir?nz0:nz1,
  38455. &zright = xdir?nz1:nz0,
  38456. &zup = ydir?nz0:nz1,
  38457. &zdown = ydir?nz1:nz0;
  38458. if (xright<0 || xleft>=width()) return *this;
  38459. if (xleft<0) {
  38460. const float D = (float)xright - xleft;
  38461. yleft-=(int)((float)xleft*((float)yright - yleft)/D);
  38462. zleft-=(tzfloat)xleft*(zright - zleft)/D;
  38463. xleft = 0;
  38464. }
  38465. if (xright>=width()) {
  38466. const float d = (float)xright - width(), D = (float)xright - xleft;
  38467. yright-=(int)(d*((float)yright - yleft)/D);
  38468. zright-=(tzfloat)d*(zright - zleft)/D;
  38469. xright = width() - 1;
  38470. }
  38471. if (ydown<0 || yup>=height()) return *this;
  38472. if (yup<0) {
  38473. const float D = (float)ydown - yup;
  38474. xup-=(int)((float)yup*((float)xdown - xup)/D);
  38475. zup-=(tzfloat)yup*(zdown - zup)/D;
  38476. yup = 0;
  38477. }
  38478. if (ydown>=height()) {
  38479. const float d = (float)ydown - height(), D = (float)ydown - yup;
  38480. xdown-=(int)(d*((float)xdown - xup)/D);
  38481. zdown-=(tzfloat)d*(zdown - zup)/D;
  38482. ydown = height() - 1;
  38483. }
  38484. T *ptrd0 = data(nx0,ny0);
  38485. tz *ptrz = zbuffer.data(nx0,ny0);
  38486. int dx = xright - xleft, dy = ydown - yup;
  38487. const bool steep = dy>dx;
  38488. if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
  38489. const longT
  38490. offx = (longT)(nx0<nx1?1:-1)*(steep?width():1),
  38491. offy = (longT)(ny0<ny1?1:-1)*(steep?1:width());
  38492. const ulongT
  38493. wh = (ulongT)_width*_height,
  38494. ndx = (ulongT)(dx>0?dx:1);
  38495. if (opacity>=1) {
  38496. if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
  38497. const tzfloat z = Z0 + x*dz/ndx;
  38498. if (z>=(tzfloat)*ptrz && pattern&hatch) {
  38499. *ptrz = (tz)z;
  38500. T *ptrd = ptrd0; const tc *col = color;
  38501. cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=wh; }
  38502. }
  38503. hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
  38504. ptrd0+=offx; ptrz+=offx;
  38505. if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
  38506. } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
  38507. const tzfloat z = Z0 + x*dz/ndx;
  38508. if (z>=(tzfloat)*ptrz) {
  38509. *ptrz = (tz)z;
  38510. T *ptrd = ptrd0; const tc *col = color;
  38511. cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=wh; }
  38512. }
  38513. ptrd0+=offx; ptrz+=offx;
  38514. if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
  38515. }
  38516. } else {
  38517. const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
  38518. if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
  38519. const tzfloat z = Z0 + x*dz/ndx;
  38520. if (z>=(tzfloat)*ptrz && pattern&hatch) {
  38521. *ptrz = (tz)z;
  38522. T *ptrd = ptrd0; const tc *col = color;
  38523. cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; }
  38524. }
  38525. hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
  38526. ptrd0+=offx; ptrz+=offx;
  38527. if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
  38528. } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
  38529. const tzfloat z = Z0 + x*dz/ndx;
  38530. if (z>=(tzfloat)*ptrz) {
  38531. *ptrz = (tz)z;
  38532. T *ptrd = ptrd0; const tc *col = color;
  38533. cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; }
  38534. }
  38535. ptrd0+=offx; ptrz+=offx;
  38536. if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
  38537. }
  38538. }
  38539. return *this;
  38540. }
  38541. //! Draw a 3d line.
  38542. /**
  38543. \param x0 X-coordinate of the starting point.
  38544. \param y0 Y-coordinate of the starting point.
  38545. \param z0 Z-coordinate of the starting point
  38546. \param x1 X-coordinate of the ending point.
  38547. \param y1 Y-coordinate of the ending point.
  38548. \param z1 Z-coordinate of the ending point.
  38549. \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
  38550. \param opacity Drawing opacity.
  38551. \param pattern An integer whose bits describe the line pattern.
  38552. \param init_hatch Tells if a reinitialization of the hash state must be done.
  38553. **/
  38554. template<typename tc>
  38555. CImg<T>& draw_line(const int x0, const int y0, const int z0,
  38556. const int x1, const int y1, const int z1,
  38557. const tc *const color, const float opacity=1,
  38558. const unsigned int pattern=~0U, const bool init_hatch=true) {
  38559. if (is_empty()) return *this;
  38560. if (!color)
  38561. throw CImgArgumentException(_cimg_instance
  38562. "draw_line(): Specified color is (null).",
  38563. cimg_instance);
  38564. static unsigned int hatch = ~0U - (~0U>>1);
  38565. if (init_hatch) hatch = ~0U - (~0U>>1);
  38566. int nx0 = x0, ny0 = y0, nz0 = z0, nx1 = x1, ny1 = y1, nz1 = z1;
  38567. if (nx0>nx1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1);
  38568. if (nx1<0 || nx0>=width()) return *this;
  38569. if (nx0<0) {
  38570. const float D = 1.0f + nx1 - nx0;
  38571. ny0-=(int)((float)nx0*(1.0f + ny1 - ny0)/D);
  38572. nz0-=(int)((float)nx0*(1.0f + nz1 - nz0)/D);
  38573. nx0 = 0;
  38574. }
  38575. if (nx1>=width()) {
  38576. const float d = (float)nx1 - width(), D = 1.0f + nx1 - nx0;
  38577. ny1+=(int)(d*(1.0f + ny0 - ny1)/D);
  38578. nz1+=(int)(d*(1.0f + nz0 - nz1)/D);
  38579. nx1 = width() - 1;
  38580. }
  38581. if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1);
  38582. if (ny1<0 || ny0>=height()) return *this;
  38583. if (ny0<0) {
  38584. const float D = 1.0f + ny1 - ny0;
  38585. nx0-=(int)((float)ny0*(1.0f + nx1 - nx0)/D);
  38586. nz0-=(int)((float)ny0*(1.0f + nz1 - nz0)/D);
  38587. ny0 = 0;
  38588. }
  38589. if (ny1>=height()) {
  38590. const float d = (float)ny1 - height(), D = 1.0f + ny1 - ny0;
  38591. nx1+=(int)(d*(1.0f + nx0 - nx1)/D);
  38592. nz1+=(int)(d*(1.0f + nz0 - nz1)/D);
  38593. ny1 = height() - 1;
  38594. }
  38595. if (nz0>nz1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1);
  38596. if (nz1<0 || nz0>=depth()) return *this;
  38597. if (nz0<0) {
  38598. const float D = 1.0f + nz1 - nz0;
  38599. nx0-=(int)((float)nz0*(1.0f + nx1 - nx0)/D);
  38600. ny0-=(int)((float)nz0*(1.0f + ny1 - ny0)/D);
  38601. nz0 = 0;
  38602. }
  38603. if (nz1>=depth()) {
  38604. const float d = (float)nz1 - depth(), D = 1.0f + nz1 - nz0;
  38605. nx1+=(int)(d*(1.0f + nx0 - nx1)/D);
  38606. ny1+=(int)(d*(1.0f + ny0 - ny1)/D);
  38607. nz1 = depth() - 1;
  38608. }
  38609. const unsigned int dmax = (unsigned int)cimg::max(cimg::abs(nx1 - nx0),cimg::abs(ny1 - ny0),nz1 - nz0);
  38610. const ulongT whd = (ulongT)_width*_height*_depth;
  38611. const float px = (nx1 - nx0)/(float)dmax, py = (ny1 - ny0)/(float)dmax, pz = (nz1 - nz0)/(float)dmax;
  38612. float x = (float)nx0, y = (float)ny0, z = (float)nz0;
  38613. if (opacity>=1) for (unsigned int t = 0; t<=dmax; ++t) {
  38614. if (!(~pattern) || (~pattern && pattern&hatch)) {
  38615. T* ptrd = data((unsigned int)x,(unsigned int)y,(unsigned int)z);
  38616. const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; }
  38617. }
  38618. x+=px; y+=py; z+=pz; if (pattern) { hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); }
  38619. } else {
  38620. const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
  38621. for (unsigned int t = 0; t<=dmax; ++t) {
  38622. if (!(~pattern) || (~pattern && pattern&hatch)) {
  38623. T* ptrd = data((unsigned int)x,(unsigned int)y,(unsigned int)z);
  38624. const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whd; }
  38625. }
  38626. x+=px; y+=py; z+=pz; if (pattern) { hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); }
  38627. }
  38628. }
  38629. return *this;
  38630. }
  38631. //! Draw a textured 2d line.
  38632. /**
  38633. \param x0 X-coordinate of the starting line point.
  38634. \param y0 Y-coordinate of the starting line point.
  38635. \param x1 X-coordinate of the ending line point.
  38636. \param y1 Y-coordinate of the ending line point.
  38637. \param texture Texture image defining the pixel colors.
  38638. \param tx0 X-coordinate of the starting texture point.
  38639. \param ty0 Y-coordinate of the starting texture point.
  38640. \param tx1 X-coordinate of the ending texture point.
  38641. \param ty1 Y-coordinate of the ending texture point.
  38642. \param opacity Drawing opacity.
  38643. \param pattern An integer whose bits describe the line pattern.
  38644. \param init_hatch Tells if the hash variable must be reinitialized.
  38645. \note
  38646. - Line routine uses the well known Bresenham's algorithm.
  38647. \par Example:
  38648. \code
  38649. CImg<unsigned char> img(100,100,1,3,0), texture("texture256x256.ppm");
  38650. const unsigned char color[] = { 255,128,64 };
  38651. img.draw_line(40,40,80,70,texture,0,0,255,255);
  38652. \endcode
  38653. **/
  38654. template<typename tc>
  38655. CImg<T>& draw_line(const int x0, const int y0,
  38656. const int x1, const int y1,
  38657. const CImg<tc>& texture,
  38658. const int tx0, const int ty0,
  38659. const int tx1, const int ty1,
  38660. const float opacity=1,
  38661. const unsigned int pattern=~0U, const bool init_hatch=true) {
  38662. if (is_empty()) return *this;
  38663. if (texture._depth>1 || texture._spectrum<_spectrum)
  38664. throw CImgArgumentException(_cimg_instance
  38665. "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).",
  38666. cimg_instance,
  38667. texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
  38668. if (is_overlapped(texture)) return draw_line(x0,y0,x1,y1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
  38669. static unsigned int hatch = ~0U - (~0U>>1);
  38670. if (init_hatch) hatch = ~0U - (~0U>>1);
  38671. const bool xdir = x0<x1, ydir = y0<y1;
  38672. int
  38673. dtx = tx1-tx0, dty = ty1-ty0,
  38674. nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
  38675. tnx0 = tx0, tnx1 = tx1, tny0 = ty0, tny1 = ty1,
  38676. &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1, &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
  38677. &txleft = xdir?tnx0:tnx1, &tyleft = xdir?tny0:tny1, &txright = xdir?tnx1:tnx0, &tyright = xdir?tny1:tny0,
  38678. &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1, &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0,
  38679. &txup = ydir?tnx0:tnx1, &tyup = ydir?tny0:tny1, &txdown = ydir?tnx1:tnx0, &tydown = ydir?tny1:tny0;
  38680. if (xright<0 || xleft>=width()) return *this;
  38681. if (xleft<0) {
  38682. const float D = (float)xright - xleft;
  38683. yleft-=(int)((float)xleft*((float)yright - yleft)/D);
  38684. txleft-=(int)((float)xleft*((float)txright - txleft)/D);
  38685. tyleft-=(int)((float)xleft*((float)tyright - tyleft)/D);
  38686. xleft = 0;
  38687. }
  38688. if (xright>=width()) {
  38689. const float d = (float)xright - width(), D = (float)xright - xleft;
  38690. yright-=(int)(d*((float)yright - yleft)/D);
  38691. txright-=(int)(d*((float)txright - txleft)/D);
  38692. tyright-=(int)(d*((float)tyright - tyleft)/D);
  38693. xright = width() - 1;
  38694. }
  38695. if (ydown<0 || yup>=height()) return *this;
  38696. if (yup<0) {
  38697. const float D = (float)ydown - yup;
  38698. xup-=(int)((float)yup*((float)xdown - xup)/D);
  38699. txup-=(int)((float)yup*((float)txdown - txup)/D);
  38700. tyup-=(int)((float)yup*((float)tydown - tyup)/D);
  38701. yup = 0;
  38702. }
  38703. if (ydown>=height()) {
  38704. const float d = (float)ydown - height(), D = (float)ydown - yup;
  38705. xdown-=(int)(d*((float)xdown - xup)/D);
  38706. txdown-=(int)(d*((float)txdown - txup)/D);
  38707. tydown-=(int)(d*((float)tydown - tyup)/D);
  38708. ydown = height() - 1;
  38709. }
  38710. T *ptrd0 = data(nx0,ny0);
  38711. int dx = xright - xleft, dy = ydown - yup;
  38712. const bool steep = dy>dx;
  38713. if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
  38714. const longT
  38715. offx = (longT)(nx0<nx1?1:-1)*(steep?width():1),
  38716. offy = (longT)(ny0<ny1?1:-1)*(steep?1:width()),
  38717. ndx = (longT)(dx>0?dx:1);
  38718. const ulongT
  38719. whd = (ulongT)_width*_height*_depth,
  38720. twh = (ulongT)texture._width*texture._height;
  38721. if (opacity>=1) {
  38722. if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
  38723. if (pattern&hatch) {
  38724. T *ptrd = ptrd0;
  38725. const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx;
  38726. const tc *col = &texture._atXY(tx,ty);
  38727. cimg_forC(*this,c) { *ptrd = (T)*col; ptrd+=whd; col+=twh; }
  38728. }
  38729. hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
  38730. ptrd0+=offx;
  38731. if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
  38732. } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
  38733. T *ptrd = ptrd0;
  38734. const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx;
  38735. const tc *col = &texture._atXY(tx,ty);
  38736. cimg_forC(*this,c) { *ptrd = (T)*col; ptrd+=whd; col+=twh; }
  38737. ptrd0+=offx;
  38738. if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
  38739. }
  38740. } else {
  38741. const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
  38742. if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
  38743. T *ptrd = ptrd0;
  38744. if (pattern&hatch) {
  38745. const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx;
  38746. const tc *col = &texture._atXY(tx,ty);
  38747. cimg_forC(*this,c) { *ptrd = (T)(nopacity**col + *ptrd*copacity); ptrd+=whd; col+=twh; }
  38748. }
  38749. hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
  38750. ptrd0+=offx;
  38751. if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
  38752. } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
  38753. T *ptrd = ptrd0;
  38754. const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx;
  38755. const tc *col = &texture._atXY(tx,ty);
  38756. cimg_forC(*this,c) { *ptrd = (T)(nopacity**col + *ptrd*copacity); ptrd+=whd; col+=twh; }
  38757. ptrd0+=offx;
  38758. if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
  38759. }
  38760. }
  38761. return *this;
  38762. }
  38763. //! Draw a textured 2d line, with perspective correction.
  38764. /**
  38765. \param x0 X-coordinate of the starting point.
  38766. \param y0 Y-coordinate of the starting point.
  38767. \param z0 Z-coordinate of the starting point
  38768. \param x1 X-coordinate of the ending point.
  38769. \param y1 Y-coordinate of the ending point.
  38770. \param z1 Z-coordinate of the ending point.
  38771. \param texture Texture image defining the pixel colors.
  38772. \param tx0 X-coordinate of the starting texture point.
  38773. \param ty0 Y-coordinate of the starting texture point.
  38774. \param tx1 X-coordinate of the ending texture point.
  38775. \param ty1 Y-coordinate of the ending texture point.
  38776. \param opacity Drawing opacity.
  38777. \param pattern An integer whose bits describe the line pattern.
  38778. \param init_hatch Tells if the hash variable must be reinitialized.
  38779. **/
  38780. template<typename tc>
  38781. CImg<T>& draw_line(const int x0, const int y0, const float z0,
  38782. const int x1, const int y1, const float z1,
  38783. const CImg<tc>& texture,
  38784. const int tx0, const int ty0,
  38785. const int tx1, const int ty1,
  38786. const float opacity=1,
  38787. const unsigned int pattern=~0U, const bool init_hatch=true) {
  38788. if (is_empty() && z0<=0 && z1<=0) return *this;
  38789. if (texture._depth>1 || texture._spectrum<_spectrum)
  38790. throw CImgArgumentException(_cimg_instance
  38791. "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).",
  38792. cimg_instance,
  38793. texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
  38794. if (is_overlapped(texture))
  38795. return draw_line(x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
  38796. static unsigned int hatch = ~0U - (~0U>>1);
  38797. if (init_hatch) hatch = ~0U - (~0U>>1);
  38798. const bool xdir = x0<x1, ydir = y0<y1;
  38799. int
  38800. nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
  38801. &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
  38802. &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
  38803. &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
  38804. &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
  38805. float
  38806. Tx0 = tx0/z0, Tx1 = tx1/z1,
  38807. Ty0 = ty0/z0, Ty1 = ty1/z1,
  38808. Z0 = 1/z0, Z1 = 1/z1,
  38809. dz = Z1 - Z0, dtx = Tx1 - Tx0, dty = Ty1 - Ty0,
  38810. tnx0 = Tx0, tnx1 = Tx1, tny0 = Ty0, tny1 = Ty1, nz0 = Z0, nz1 = Z1,
  38811. &zleft = xdir?nz0:nz1, &txleft = xdir?tnx0:tnx1, &tyleft = xdir?tny0:tny1,
  38812. &zright = xdir?nz1:nz0, &txright = xdir?tnx1:tnx0, &tyright = xdir?tny1:tny0,
  38813. &zup = ydir?nz0:nz1, &txup = ydir?tnx0:tnx1, &tyup = ydir?tny0:tny1,
  38814. &zdown = ydir?nz1:nz0, &txdown = ydir?tnx1:tnx0, &tydown = ydir?tny1:tny0;
  38815. if (xright<0 || xleft>=width()) return *this;
  38816. if (xleft<0) {
  38817. const float D = (float)xright - xleft;
  38818. yleft-=(int)((float)xleft*((float)yright - yleft)/D);
  38819. zleft-=(float)xleft*(zright - zleft)/D;
  38820. txleft-=(float)xleft*(txright - txleft)/D;
  38821. tyleft-=(float)xleft*(tyright - tyleft)/D;
  38822. xleft = 0;
  38823. }
  38824. if (xright>=width()) {
  38825. const float d = (float)xright - width(), D = (float)xright - xleft;
  38826. yright-=(int)(d*((float)yright - yleft)/D);
  38827. zright-=d*(zright - zleft)/D;
  38828. txright-=d*(txright - txleft)/D;
  38829. tyright-=d*(tyright - tyleft)/D;
  38830. xright = width() - 1;
  38831. }
  38832. if (ydown<0 || yup>=height()) return *this;
  38833. if (yup<0) {
  38834. const float D = (float)ydown - yup;
  38835. xup-=(int)((float)yup*((float)xdown - xup)/D);
  38836. zup-=(float)yup*(zdown - zup)/D;
  38837. txup-=(float)yup*(txdown - txup)/D;
  38838. tyup-=(float)yup*(tydown - tyup)/D;
  38839. yup = 0;
  38840. }
  38841. if (ydown>=height()) {
  38842. const float d = (float)ydown - height(), D = (float)ydown - yup;
  38843. xdown-=(int)(d*((float)xdown - xup)/D);
  38844. zdown-=d*(zdown - zup)/D;
  38845. txdown-=d*(txdown - txup)/D;
  38846. tydown-=d*(tydown - tyup)/D;
  38847. ydown = height() - 1;
  38848. }
  38849. T *ptrd0 = data(nx0,ny0);
  38850. int dx = xright - xleft, dy = ydown - yup;
  38851. const bool steep = dy>dx;
  38852. if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
  38853. const longT
  38854. offx = (longT)(nx0<nx1?1:-1)*(steep?width():1),
  38855. offy = (longT)(ny0<ny1?1:-1)*(steep?1:width()),
  38856. ndx = (longT)(dx>0?dx:1);
  38857. const ulongT
  38858. whd = (ulongT)_width*_height*_depth,
  38859. twh = (ulongT)texture._width*texture._height;
  38860. if (opacity>=1) {
  38861. if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
  38862. if (pattern&hatch) {
  38863. const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
  38864. const tc *col = &texture._atXY((int)(tx/z),(int)(ty/z));
  38865. T *ptrd = ptrd0;
  38866. cimg_forC(*this,c) { *ptrd = (T)*col; ptrd+=whd; col+=twh; }
  38867. }
  38868. hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
  38869. ptrd0+=offx;
  38870. if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
  38871. } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
  38872. const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
  38873. const tc *col = &texture._atXY((int)(tx/z),(int)(ty/z));
  38874. T *ptrd = ptrd0;
  38875. cimg_forC(*this,c) { *ptrd = (T)*col; ptrd+=whd; col+=twh; }
  38876. ptrd0+=offx;
  38877. if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
  38878. }
  38879. } else {
  38880. const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
  38881. if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
  38882. if (pattern&hatch) {
  38883. const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
  38884. const tc *col = &texture._atXY((int)(tx/z),(int)(ty/z));
  38885. T *ptrd = ptrd0;
  38886. cimg_forC(*this,c) { *ptrd = (T)(nopacity**col + *ptrd*copacity); ptrd+=whd; col+=twh; }
  38887. }
  38888. hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
  38889. ptrd0+=offx;
  38890. if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
  38891. } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
  38892. const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
  38893. const tc *col = &texture._atXY((int)(tx/z),(int)(ty/z));
  38894. T *ptrd = ptrd0;
  38895. cimg_forC(*this,c) { *ptrd = (T)(nopacity**col + *ptrd*copacity); ptrd+=whd; col+=twh; }
  38896. ptrd0+=offx;
  38897. if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
  38898. }
  38899. }
  38900. return *this;
  38901. }
  38902. //! Draw a textured 2d line, with perspective correction and z-buffering.
  38903. /**
  38904. \param zbuffer Z-buffer image.
  38905. \param x0 X-coordinate of the starting point.
  38906. \param y0 Y-coordinate of the starting point.
  38907. \param z0 Z-coordinate of the starting point
  38908. \param x1 X-coordinate of the ending point.
  38909. \param y1 Y-coordinate of the ending point.
  38910. \param z1 Z-coordinate of the ending point.
  38911. \param texture Texture image defining the pixel colors.
  38912. \param tx0 X-coordinate of the starting texture point.
  38913. \param ty0 Y-coordinate of the starting texture point.
  38914. \param tx1 X-coordinate of the ending texture point.
  38915. \param ty1 Y-coordinate of the ending texture point.
  38916. \param opacity Drawing opacity.
  38917. \param pattern An integer whose bits describe the line pattern.
  38918. \param init_hatch Tells if the hash variable must be reinitialized.
  38919. **/
  38920. template<typename tz, typename tc>
  38921. CImg<T>& draw_line(CImg<tz>& zbuffer,
  38922. const int x0, const int y0, const float z0,
  38923. const int x1, const int y1, const float z1,
  38924. const CImg<tc>& texture,
  38925. const int tx0, const int ty0,
  38926. const int tx1, const int ty1,
  38927. const float opacity=1,
  38928. const unsigned int pattern=~0U, const bool init_hatch=true) {
  38929. typedef typename cimg::superset<tz,float>::type tzfloat;
  38930. if (is_empty() || z0<=0 || z1<=0) return *this;
  38931. if (!is_sameXY(zbuffer))
  38932. throw CImgArgumentException(_cimg_instance
  38933. "draw_line(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
  38934. "different dimensions.",
  38935. cimg_instance,
  38936. zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
  38937. if (texture._depth>1 || texture._spectrum<_spectrum)
  38938. throw CImgArgumentException(_cimg_instance
  38939. "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).",
  38940. cimg_instance,
  38941. texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
  38942. if (is_overlapped(texture))
  38943. return draw_line(zbuffer,x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
  38944. static unsigned int hatch = ~0U - (~0U>>1);
  38945. if (init_hatch) hatch = ~0U - (~0U>>1);
  38946. const bool xdir = x0<x1, ydir = y0<y1;
  38947. int
  38948. nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
  38949. &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
  38950. &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
  38951. &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
  38952. &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
  38953. float
  38954. Tx0 = tx0/z0, Tx1 = tx1/z1,
  38955. Ty0 = ty0/z0, Ty1 = ty1/z1,
  38956. dtx = Tx1 - Tx0, dty = Ty1 - Ty0,
  38957. tnx0 = Tx0, tnx1 = Tx1, tny0 = Ty0, tny1 = Ty1,
  38958. &txleft = xdir?tnx0:tnx1, &tyleft = xdir?tny0:tny1,
  38959. &txright = xdir?tnx1:tnx0, &tyright = xdir?tny1:tny0,
  38960. &txup = ydir?tnx0:tnx1, &tyup = ydir?tny0:tny1,
  38961. &txdown = ydir?tnx1:tnx0, &tydown = ydir?tny1:tny0;
  38962. tzfloat
  38963. Z0 = 1/(tzfloat)z0, Z1 = 1/(tzfloat)z1,
  38964. dz = Z1 - Z0, nz0 = Z0, nz1 = Z1,
  38965. &zleft = xdir?nz0:nz1,
  38966. &zright = xdir?nz1:nz0,
  38967. &zup = ydir?nz0:nz1,
  38968. &zdown = ydir?nz1:nz0;
  38969. if (xright<0 || xleft>=width()) return *this;
  38970. if (xleft<0) {
  38971. const float D = (float)xright - xleft;
  38972. yleft-=(int)((float)xleft*((float)yright - yleft)/D);
  38973. zleft-=(float)xleft*(zright - zleft)/D;
  38974. txleft-=(float)xleft*(txright - txleft)/D;
  38975. tyleft-=(float)xleft*(tyright - tyleft)/D;
  38976. xleft = 0;
  38977. }
  38978. if (xright>=width()) {
  38979. const float d = (float)xright - width(), D = (float)xright - xleft;
  38980. yright-=(int)(d*((float)yright - yleft)/D);
  38981. zright-=d*(zright - zleft)/D;
  38982. txright-=d*(txright - txleft)/D;
  38983. tyright-=d*(tyright - tyleft)/D;
  38984. xright = width() - 1;
  38985. }
  38986. if (ydown<0 || yup>=height()) return *this;
  38987. if (yup<0) {
  38988. const float D = (float)ydown - yup;
  38989. xup-=(int)((float)yup*((float)xdown - xup)/D);
  38990. zup-=yup*(zdown - zup)/D;
  38991. txup-=yup*(txdown - txup)/D;
  38992. tyup-=yup*(tydown - tyup)/D;
  38993. yup = 0;
  38994. }
  38995. if (ydown>=height()) {
  38996. const float d = (float)ydown - height(), D = (float)ydown - yup;
  38997. xdown-=(int)(d*((float)xdown - xup)/D);
  38998. zdown-=d*(zdown - zup)/D;
  38999. txdown-=d*(txdown - txup)/D;
  39000. tydown-=d*(tydown - tyup)/D;
  39001. ydown = height() - 1;
  39002. }
  39003. T *ptrd0 = data(nx0,ny0);
  39004. tz *ptrz = zbuffer.data(nx0,ny0);
  39005. int dx = xright - xleft, dy = ydown - yup;
  39006. const bool steep = dy>dx;
  39007. if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
  39008. const longT
  39009. offx = (longT)(nx0<nx1?1:-1)*(steep?width():1),
  39010. offy = (longT)(ny0<ny1?1:-1)*(steep?1:width()),
  39011. ndx = (longT)(dx>0?dx:1);
  39012. const ulongT
  39013. whd = (ulongT)_width*_height*_depth,
  39014. twh = (ulongT)texture._width*texture._height;
  39015. if (opacity>=1) {
  39016. if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
  39017. if (pattern&hatch) {
  39018. const tzfloat z = Z0 + x*dz/ndx;
  39019. if (z>=(tzfloat)*ptrz) {
  39020. *ptrz = (tz)z;
  39021. const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
  39022. const tc *col = &texture._atXY((int)(tx/z),(int)(ty/z));
  39023. T *ptrd = ptrd0;
  39024. cimg_forC(*this,c) { *ptrd = (T)*col; ptrd+=whd; col+=twh; }
  39025. }
  39026. }
  39027. hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
  39028. ptrd0+=offx; ptrz+=offx;
  39029. if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
  39030. } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
  39031. const tzfloat z = Z0 + x*dz/ndx;
  39032. if (z>=(tzfloat)*ptrz) {
  39033. *ptrz = (tz)z;
  39034. const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
  39035. const tc *col = &texture._atXY((int)(tx/z),(int)(ty/z));
  39036. T *ptrd = ptrd0;
  39037. cimg_forC(*this,c) { *ptrd = (T)*col; ptrd+=whd; col+=twh; }
  39038. }
  39039. ptrd0+=offx; ptrz+=offx;
  39040. if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
  39041. }
  39042. } else {
  39043. const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
  39044. if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
  39045. if (pattern&hatch) {
  39046. const tzfloat z = Z0 + x*dz/ndx;
  39047. if (z>=(tzfloat)*ptrz) {
  39048. *ptrz = (tz)z;
  39049. const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
  39050. const tc *col = &texture._atXY((int)(tx/z),(int)(ty/z));
  39051. T *ptrd = ptrd0;
  39052. cimg_forC(*this,c) { *ptrd = (T)(nopacity**col + *ptrd*copacity); ptrd+=whd; col+=twh; }
  39053. }
  39054. }
  39055. hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
  39056. ptrd0+=offx; ptrz+=offx;
  39057. if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
  39058. } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
  39059. const tzfloat z = Z0 + x*dz/ndx;
  39060. if (z>=(tzfloat)*ptrz) {
  39061. *ptrz = (tz)z;
  39062. const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
  39063. const tc *col = &texture._atXY((int)(tx/z),(int)(ty/z));
  39064. T *ptrd = ptrd0;
  39065. cimg_forC(*this,c) { *ptrd = (T)(nopacity**col + *ptrd*copacity); ptrd+=whd; col+=twh; }
  39066. }
  39067. ptrd0+=offx; ptrz+=offx;
  39068. if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
  39069. }
  39070. }
  39071. return *this;
  39072. }
  39073. //! Draw a set of consecutive lines.
  39074. /**
  39075. \param points Coordinates of vertices, stored as a list of vectors.
  39076. \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
  39077. \param opacity Drawing opacity.
  39078. \param pattern An integer whose bits describe the line pattern.
  39079. \param init_hatch If set to true, init hatch motif.
  39080. \note
  39081. - This function uses several call to the single CImg::draw_line() procedure,
  39082. depending on the vectors size in \p points.
  39083. **/
  39084. template<typename t, typename tc>
  39085. CImg<T>& draw_line(const CImg<t>& points,
  39086. const tc *const color, const float opacity=1,
  39087. const unsigned int pattern=~0U, const bool init_hatch=true) {
  39088. if (is_empty() || !points || points._width<2) return *this;
  39089. bool ninit_hatch = init_hatch;
  39090. switch (points._height) {
  39091. case 0 : case 1 :
  39092. throw CImgArgumentException(_cimg_instance
  39093. "draw_line(): Invalid specified point set (%u,%u,%u,%u,%p).",
  39094. cimg_instance,
  39095. points._width,points._height,points._depth,points._spectrum,points._data);
  39096. case 2 : {
  39097. const int x0 = (int)points(0,0), y0 = (int)points(0,1);
  39098. int ox = x0, oy = y0;
  39099. for (unsigned int i = 1; i<points._width; ++i) {
  39100. const int x = (int)points(i,0), y = (int)points(i,1);
  39101. draw_line(ox,oy,x,y,color,opacity,pattern,ninit_hatch);
  39102. ninit_hatch = false;
  39103. ox = x; oy = y;
  39104. }
  39105. } break;
  39106. default : {
  39107. const int x0 = (int)points(0,0), y0 = (int)points(0,1), z0 = (int)points(0,2);
  39108. int ox = x0, oy = y0, oz = z0;
  39109. for (unsigned int i = 1; i<points._width; ++i) {
  39110. const int x = (int)points(i,0), y = (int)points(i,1), z = (int)points(i,2);
  39111. draw_line(ox,oy,oz,x,y,z,color,opacity,pattern,ninit_hatch);
  39112. ninit_hatch = false;
  39113. ox = x; oy = y; oz = z;
  39114. }
  39115. }
  39116. }
  39117. return *this;
  39118. }
  39119. //! Draw a 2d arrow.
  39120. /**
  39121. \param x0 X-coordinate of the starting arrow point (tail).
  39122. \param y0 Y-coordinate of the starting arrow point (tail).
  39123. \param x1 X-coordinate of the ending arrow point (head).
  39124. \param y1 Y-coordinate of the ending arrow point (head).
  39125. \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
  39126. \param angle Aperture angle of the arrow head.
  39127. \param length Length of the arrow head. If negative, describes a percentage of the arrow length.
  39128. \param opacity Drawing opacity.
  39129. \param pattern An integer whose bits describe the line pattern.
  39130. **/
  39131. template<typename tc>
  39132. CImg<T>& draw_arrow(const int x0, const int y0,
  39133. const int x1, const int y1,
  39134. const tc *const color, const float opacity=1,
  39135. const float angle=30, const float length=-10,
  39136. const unsigned int pattern=~0U) {
  39137. if (is_empty()) return *this;
  39138. const float u = (float)(x0 - x1), v = (float)(y0 - y1), sq = u*u + v*v,
  39139. deg = (float)(angle*cimg::PI/180), ang = (sq>0)?(float)std::atan2(v,u):0.0f,
  39140. l = (length>=0)?length:-length*(float)std::sqrt(sq)/100;
  39141. if (sq>0) {
  39142. const float
  39143. cl = (float)std::cos(ang - deg), sl = (float)std::sin(ang - deg),
  39144. cr = (float)std::cos(ang + deg), sr = (float)std::sin(ang + deg);
  39145. const int
  39146. xl = x1 + (int)(l*cl), yl = y1 + (int)(l*sl),
  39147. xr = x1 + (int)(l*cr), yr = y1 + (int)(l*sr),
  39148. xc = x1 + (int)((l + 1)*(cl + cr))/2, yc = y1 + (int)((l + 1)*(sl + sr))/2;
  39149. draw_line(x0,y0,xc,yc,color,opacity,pattern).draw_triangle(x1,y1,xl,yl,xr,yr,color,opacity);
  39150. } else draw_point(x0,y0,color,opacity);
  39151. return *this;
  39152. }
  39153. //! Draw a 2d spline.
  39154. /**
  39155. \param x0 X-coordinate of the starting curve point
  39156. \param y0 Y-coordinate of the starting curve point
  39157. \param u0 X-coordinate of the starting velocity
  39158. \param v0 Y-coordinate of the starting velocity
  39159. \param x1 X-coordinate of the ending curve point
  39160. \param y1 Y-coordinate of the ending curve point
  39161. \param u1 X-coordinate of the ending velocity
  39162. \param v1 Y-coordinate of the ending velocity
  39163. \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
  39164. \param precision Curve drawing precision.
  39165. \param opacity Drawing opacity.
  39166. \param pattern An integer whose bits describe the line pattern.
  39167. \param init_hatch If \c true, init hatch motif.
  39168. \note
  39169. - The curve is a 2d cubic Bezier spline, from the set of specified starting/ending points
  39170. and corresponding velocity vectors.
  39171. - The spline is drawn as a serie of connected segments. The \p precision parameter sets the
  39172. average number of pixels in each drawn segment.
  39173. - A cubic Bezier curve is sometimes defined by a set of 4 points { (\p x0,\p y0), (\p xa,\p ya),
  39174. (\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
  39175. and (\p xa,\p ya), (\p xb,\p yb) are two
  39176. \e control points.
  39177. The starting and ending velocities (\p u0,\p v0) and (\p u1,\p v1) can be deduced easily from
  39178. the control points as
  39179. \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).
  39180. \par Example:
  39181. \code
  39182. CImg<unsigned char> img(100,100,1,3,0);
  39183. const unsigned char color[] = { 255,255,255 };
  39184. img.draw_spline(30,30,0,100,90,40,0,-100,color);
  39185. \endcode
  39186. **/
  39187. template<typename tc>
  39188. CImg<T>& draw_spline(const int x0, const int y0, const float u0, const float v0,
  39189. const int x1, const int y1, const float u1, const float v1,
  39190. const tc *const color, const float opacity=1,
  39191. const float precision=0.25, const unsigned int pattern=~0U,
  39192. const bool init_hatch=true) {
  39193. if (is_empty()) return *this;
  39194. if (!color)
  39195. throw CImgArgumentException(_cimg_instance
  39196. "draw_spline(): Specified color is (null).",
  39197. cimg_instance);
  39198. if (x0==x1 && y0==y1) return draw_point(x0,y0,color,opacity);
  39199. bool ninit_hatch = init_hatch;
  39200. const float
  39201. ax = u0 + u1 + 2*(x0 - x1),
  39202. bx = 3*(x1 - x0) - 2*u0 - u1,
  39203. ay = v0 + v1 + 2*(y0 - y1),
  39204. by = 3*(y1 - y0) - 2*v0 - v1,
  39205. _precision = 1/(cimg::hypot((float)x0 - x1,(float)y0 - y1)*(precision>0?precision:1));
  39206. int ox = x0, oy = y0;
  39207. for (float t = 0; t<1; t+=_precision) {
  39208. const float t2 = t*t, t3 = t2*t;
  39209. const int
  39210. nx = (int)(ax*t3 + bx*t2 + u0*t + x0),
  39211. ny = (int)(ay*t3 + by*t2 + v0*t + y0);
  39212. draw_line(ox,oy,nx,ny,color,opacity,pattern,ninit_hatch);
  39213. ninit_hatch = false;
  39214. ox = nx; oy = ny;
  39215. }
  39216. return draw_line(ox,oy,x1,y1,color,opacity,pattern,false);
  39217. }
  39218. //! Draw a 3d spline \overloading.
  39219. /**
  39220. \note
  39221. - Similar to CImg::draw_spline() for a 3d spline in a volumetric image.
  39222. **/
  39223. template<typename tc>
  39224. CImg<T>& draw_spline(const int x0, const int y0, const int z0, const float u0, const float v0, const float w0,
  39225. const int x1, const int y1, const int z1, const float u1, const float v1, const float w1,
  39226. const tc *const color, const float opacity=1,
  39227. const float precision=4, const unsigned int pattern=~0U,
  39228. const bool init_hatch=true) {
  39229. if (is_empty()) return *this;
  39230. if (!color)
  39231. throw CImgArgumentException(_cimg_instance
  39232. "draw_spline(): Specified color is (null).",
  39233. cimg_instance);
  39234. if (x0==x1 && y0==y1 && z0==z1) return draw_point(x0,y0,z0,color,opacity);
  39235. bool ninit_hatch = init_hatch;
  39236. const float
  39237. ax = u0 + u1 + 2*(x0 - x1),
  39238. bx = 3*(x1 - x0) - 2*u0 - u1,
  39239. ay = v0 + v1 + 2*(y0 - y1),
  39240. by = 3*(y1 - y0) - 2*v0 - v1,
  39241. az = w0 + w1 + 2*(z0 - z1),
  39242. bz = 3*(z1 - z0) - 2*w0 - w1,
  39243. _precision = 1/(cimg::hypot((float)x0 - x1,(float)y0 - y1)*(precision>0?precision:1));
  39244. int ox = x0, oy = y0, oz = z0;
  39245. for (float t = 0; t<1; t+=_precision) {
  39246. const float t2 = t*t, t3 = t2*t;
  39247. const int
  39248. nx = (int)(ax*t3 + bx*t2 + u0*t + x0),
  39249. ny = (int)(ay*t3 + by*t2 + v0*t + y0),
  39250. nz = (int)(az*t3 + bz*t2 + w0*t + z0);
  39251. draw_line(ox,oy,oz,nx,ny,nz,color,opacity,pattern,ninit_hatch);
  39252. ninit_hatch = false;
  39253. ox = nx; oy = ny; oz = nz;
  39254. }
  39255. return draw_line(ox,oy,oz,x1,y1,z1,color,opacity,pattern,false);
  39256. }
  39257. //! Draw a textured 2d spline.
  39258. /**
  39259. \param x0 X-coordinate of the starting curve point
  39260. \param y0 Y-coordinate of the starting curve point
  39261. \param u0 X-coordinate of the starting velocity
  39262. \param v0 Y-coordinate of the starting velocity
  39263. \param x1 X-coordinate of the ending curve point
  39264. \param y1 Y-coordinate of the ending curve point
  39265. \param u1 X-coordinate of the ending velocity
  39266. \param v1 Y-coordinate of the ending velocity
  39267. \param texture Texture image defining line pixel colors.
  39268. \param tx0 X-coordinate of the starting texture point.
  39269. \param ty0 Y-coordinate of the starting texture point.
  39270. \param tx1 X-coordinate of the ending texture point.
  39271. \param ty1 Y-coordinate of the ending texture point.
  39272. \param precision Curve drawing precision.
  39273. \param opacity Drawing opacity.
  39274. \param pattern An integer whose bits describe the line pattern.
  39275. \param init_hatch if \c true, reinit hatch motif.
  39276. **/
  39277. template<typename t>
  39278. CImg<T>& draw_spline(const int x0, const int y0, const float u0, const float v0,
  39279. const int x1, const int y1, const float u1, const float v1,
  39280. const CImg<t>& texture,
  39281. const int tx0, const int ty0, const int tx1, const int ty1,
  39282. const float opacity=1,
  39283. const float precision=4, const unsigned int pattern=~0U,
  39284. const bool init_hatch=true) {
  39285. if (texture._depth>1 || texture._spectrum<_spectrum)
  39286. throw CImgArgumentException(_cimg_instance
  39287. "draw_spline(): Invalid specified texture (%u,%u,%u,%u,%p).",
  39288. cimg_instance,
  39289. texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
  39290. if (is_empty()) return *this;
  39291. if (is_overlapped(texture))
  39292. return draw_spline(x0,y0,u0,v0,x1,y1,u1,v1,+texture,tx0,ty0,tx1,ty1,precision,opacity,pattern,init_hatch);
  39293. if (x0==x1 && y0==y1)
  39294. return draw_point(x0,y0,texture.get_vector_at(x0<=0?0:x0>=texture.width()?texture.width() - 1:x0,
  39295. y0<=0?0:y0>=texture.height()?texture.height() - 1:y0),opacity);
  39296. bool ninit_hatch = init_hatch;
  39297. const float
  39298. ax = u0 + u1 + 2*(x0 - x1),
  39299. bx = 3*(x1 - x0) - 2*u0 - u1,
  39300. ay = v0 + v1 + 2*(y0 - y1),
  39301. by = 3*(y1 - y0) - 2*v0 - v1,
  39302. _precision = 1/(cimg::hypot((float)x0 - x1,(float)y0 - y1)*(precision>0?precision:1));
  39303. int ox = x0, oy = y0, otx = tx0, oty = ty0;
  39304. for (float t1 = 0; t1<1; t1+=_precision) {
  39305. const float t2 = t1*t1, t3 = t2*t1;
  39306. const int
  39307. nx = (int)(ax*t3 + bx*t2 + u0*t1 + x0),
  39308. ny = (int)(ay*t3 + by*t2 + v0*t1 + y0),
  39309. ntx = tx0 + (int)((tx1 - tx0)*t1),
  39310. nty = ty0 + (int)((ty1 - ty0)*t1);
  39311. draw_line(ox,oy,nx,ny,texture,otx,oty,ntx,nty,opacity,pattern,ninit_hatch);
  39312. ninit_hatch = false;
  39313. ox = nx; oy = ny; otx = ntx; oty = nty;
  39314. }
  39315. return draw_line(ox,oy,x1,y1,texture,otx,oty,tx1,ty1,opacity,pattern,false);
  39316. }
  39317. //! Draw a set of consecutive splines.
  39318. /**
  39319. \param points Vertices data.
  39320. \param tangents Tangents data.
  39321. \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
  39322. \param opacity Drawing opacity.
  39323. \param is_closed_set Tells if the drawn spline set is closed.
  39324. \param precision Precision of the drawing.
  39325. \param pattern An integer whose bits describe the line pattern.
  39326. \param init_hatch If \c true, init hatch motif.
  39327. **/
  39328. template<typename tp, typename tt, typename tc>
  39329. CImg<T>& draw_spline(const CImg<tp>& points, const CImg<tt>& tangents,
  39330. const tc *const color, const float opacity=1,
  39331. const bool is_closed_set=false, const float precision=4,
  39332. const unsigned int pattern=~0U, const bool init_hatch=true) {
  39333. if (is_empty() || !points || !tangents || points._width<2 || tangents._width<2) return *this;
  39334. bool ninit_hatch = init_hatch;
  39335. switch (points._height) {
  39336. case 0 : case 1 :
  39337. throw CImgArgumentException(_cimg_instance
  39338. "draw_spline(): Invalid specified point set (%u,%u,%u,%u,%p).",
  39339. cimg_instance,
  39340. points._width,points._height,points._depth,points._spectrum,points._data);
  39341. case 2 : {
  39342. const int x0 = (int)points(0,0), y0 = (int)points(0,1);
  39343. const float u0 = (float)tangents(0,0), v0 = (float)tangents(0,1);
  39344. int ox = x0, oy = y0;
  39345. float ou = u0, ov = v0;
  39346. for (unsigned int i = 1; i<points._width; ++i) {
  39347. const int x = (int)points(i,0), y = (int)points(i,1);
  39348. const float u = (float)tangents(i,0), v = (float)tangents(i,1);
  39349. draw_spline(ox,oy,ou,ov,x,y,u,v,color,precision,opacity,pattern,ninit_hatch);
  39350. ninit_hatch = false;
  39351. ox = x; oy = y; ou = u; ov = v;
  39352. }
  39353. if (is_closed_set) draw_spline(ox,oy,ou,ov,x0,y0,u0,v0,color,precision,opacity,pattern,false);
  39354. } break;
  39355. default : {
  39356. const int x0 = (int)points(0,0), y0 = (int)points(0,1), z0 = (int)points(0,2);
  39357. const float u0 = (float)tangents(0,0), v0 = (float)tangents(0,1), w0 = (float)tangents(0,2);
  39358. int ox = x0, oy = y0, oz = z0;
  39359. float ou = u0, ov = v0, ow = w0;
  39360. for (unsigned int i = 1; i<points._width; ++i) {
  39361. const int x = (int)points(i,0), y = (int)points(i,1), z = (int)points(i,2);
  39362. const float u = (float)tangents(i,0), v = (float)tangents(i,1), w = (float)tangents(i,2);
  39363. draw_spline(ox,oy,oz,ou,ov,ow,x,y,z,u,v,w,color,opacity,pattern,ninit_hatch);
  39364. ninit_hatch = false;
  39365. ox = x; oy = y; oz = z; ou = u; ov = v; ow = w;
  39366. }
  39367. if (is_closed_set) draw_spline(ox,oy,oz,ou,ov,ow,x0,y0,z0,u0,v0,w0,color,precision,opacity,pattern,false);
  39368. }
  39369. }
  39370. return *this;
  39371. }
  39372. //! Draw a set of consecutive splines \overloading.
  39373. /**
  39374. Similar to previous function, with the point tangents automatically estimated from the given points set.
  39375. **/
  39376. template<typename tp, typename tc>
  39377. CImg<T>& draw_spline(const CImg<tp>& points,
  39378. const tc *const color, const float opacity=1,
  39379. const bool is_closed_set=false, const float precision=4,
  39380. const unsigned int pattern=~0U, const bool init_hatch=true) {
  39381. if (is_empty() || !points || points._width<2) return *this;
  39382. CImg<Tfloat> tangents;
  39383. switch (points._height) {
  39384. case 0 : case 1 :
  39385. throw CImgArgumentException(_cimg_instance
  39386. "draw_spline(): Invalid specified point set (%u,%u,%u,%u,%p).",
  39387. cimg_instance,
  39388. points._width,points._height,points._depth,points._spectrum,points._data);
  39389. case 2 : {
  39390. tangents.assign(points._width,points._height);
  39391. cimg_forX(points,p) {
  39392. const unsigned int
  39393. p0 = is_closed_set?(p + points._width - 1)%points._width:(p?p - 1:0),
  39394. p1 = is_closed_set?(p + 1)%points._width:(p + 1<points._width?p + 1:p);
  39395. const float
  39396. x = (float)points(p,0),
  39397. y = (float)points(p,1),
  39398. x0 = (float)points(p0,0),
  39399. y0 = (float)points(p0,1),
  39400. x1 = (float)points(p1,0),
  39401. y1 = (float)points(p1,1),
  39402. u0 = x - x0,
  39403. v0 = y - y0,
  39404. n0 = 1e-8f + cimg::hypot(u0,v0),
  39405. u1 = x1 - x,
  39406. v1 = y1 - y,
  39407. n1 = 1e-8f + cimg::hypot(u1,v1),
  39408. u = u0/n0 + u1/n1,
  39409. v = v0/n0 + v1/n1,
  39410. n = 1e-8f + cimg::hypot(u,v),
  39411. fact = 0.5f*(n0 + n1);
  39412. tangents(p,0) = (Tfloat)(fact*u/n);
  39413. tangents(p,1) = (Tfloat)(fact*v/n);
  39414. }
  39415. } break;
  39416. default : {
  39417. tangents.assign(points._width,points._height);
  39418. cimg_forX(points,p) {
  39419. const unsigned int
  39420. p0 = is_closed_set?(p + points._width - 1)%points._width:(p?p - 1:0),
  39421. p1 = is_closed_set?(p + 1)%points._width:(p + 1<points._width?p + 1:p);
  39422. const float
  39423. x = (float)points(p,0),
  39424. y = (float)points(p,1),
  39425. z = (float)points(p,2),
  39426. x0 = (float)points(p0,0),
  39427. y0 = (float)points(p0,1),
  39428. z0 = (float)points(p0,2),
  39429. x1 = (float)points(p1,0),
  39430. y1 = (float)points(p1,1),
  39431. z1 = (float)points(p1,2),
  39432. u0 = x - x0,
  39433. v0 = y - y0,
  39434. w0 = z - z0,
  39435. n0 = 1e-8f + cimg::hypot(u0,v0,w0),
  39436. u1 = x1 - x,
  39437. v1 = y1 - y,
  39438. w1 = z1 - z,
  39439. n1 = 1e-8f + cimg::hypot(u1,v1,w1),
  39440. u = u0/n0 + u1/n1,
  39441. v = v0/n0 + v1/n1,
  39442. w = w0/n0 + w1/n1,
  39443. n = 1e-8f + cimg::hypot(u,v,w),
  39444. fact = 0.5f*(n0 + n1);
  39445. tangents(p,0) = (Tfloat)(fact*u/n);
  39446. tangents(p,1) = (Tfloat)(fact*v/n);
  39447. tangents(p,2) = (Tfloat)(fact*w/n);
  39448. }
  39449. }
  39450. }
  39451. return draw_spline(points,tangents,color,opacity,is_closed_set,precision,pattern,init_hatch);
  39452. }
  39453. // Inner macro for drawing triangles.
  39454. #define _cimg_for_triangle1(img,xl,xr,y,x0,y0,x1,y1,x2,y2) \
  39455. for (int y = y0<0?0:y0, \
  39456. xr = y0>=0?x0:(x0 - y0*(x2 - x0)/(y2 - y0)), \
  39457. xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0 - y0*(x1 - x0)/(y1 - y0))):(x1 - y1*(x2 - x1)/(y2 - y1)), \
  39458. _sxn=1, \
  39459. _sxr=1, \
  39460. _sxl=1, \
  39461. _dxn = x2>x1?x2-x1:(_sxn=-1,x1 - x2), \
  39462. _dxr = x2>x0?x2-x0:(_sxr=-1,x0 - x2), \
  39463. _dxl = x1>x0?x1-x0:(_sxl=-1,x0 - x1), \
  39464. _dyn = y2-y1, \
  39465. _dyr = y2-y0, \
  39466. _dyl = y1-y0, \
  39467. _counter = (_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
  39468. _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
  39469. _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
  39470. std::min((int)(img)._height - y - 1,y2 - y)), \
  39471. _errn = _dyn/2, \
  39472. _errr = _dyr/2, \
  39473. _errl = _dyl/2, \
  39474. _rxn = _dyn?(x2-x1)/_dyn:0, \
  39475. _rxr = _dyr?(x2-x0)/_dyr:0, \
  39476. _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \
  39477. (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn); \
  39478. _counter>=0; --_counter, ++y, \
  39479. xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
  39480. xl+=(y!=y1)?_rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0): \
  39481. (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl))
  39482. #define _cimg_for_triangle2(img,xl,cl,xr,cr,y,x0,y0,c0,x1,y1,c1,x2,y2,c2) \
  39483. for (int y = y0<0?0:y0, \
  39484. xr = y0>=0?x0:(x0 - y0*(x2 - x0)/(y2 - y0)), \
  39485. cr = y0>=0?c0:(c0 - y0*(c2 - c0)/(y2 - y0)), \
  39486. xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0 - y0*(x1 - x0)/(y1 - y0))):(x1 - y1*(x2 - x1)/(y2 - y1)), \
  39487. cl = y1>=0?(y0>=0?(y0==y1?c1:c0):(c0 - y0*(c1 - c0)/(y1 - y0))):(c1 - y1*(c2 - c1)/(y2 - y1)), \
  39488. _sxn=1, _scn=1, \
  39489. _sxr=1, _scr=1, \
  39490. _sxl=1, _scl=1, \
  39491. _dxn = x2>x1?x2-x1:(_sxn=-1,x1 - x2), \
  39492. _dxr = x2>x0?x2-x0:(_sxr=-1,x0 - x2), \
  39493. _dxl = x1>x0?x1-x0:(_sxl=-1,x0 - x1), \
  39494. _dcn = c2>c1?c2-c1:(_scn=-1,c1 - c2), \
  39495. _dcr = c2>c0?c2-c0:(_scr=-1,c0 - c2), \
  39496. _dcl = c1>c0?c1-c0:(_scl=-1,c0 - c1), \
  39497. _dyn = y2-y1, \
  39498. _dyr = y2-y0, \
  39499. _dyl = y1-y0, \
  39500. _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
  39501. _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
  39502. _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
  39503. _dcn-=_dyn?_dyn*(_dcn/_dyn):0, \
  39504. _dcr-=_dyr?_dyr*(_dcr/_dyr):0, \
  39505. _dcl-=_dyl?_dyl*(_dcl/_dyl):0, \
  39506. std::min((int)(img)._height - y - 1,y2 - y)), \
  39507. _errn = _dyn/2, _errcn = _errn, \
  39508. _errr = _dyr/2, _errcr = _errr, \
  39509. _errl = _dyl/2, _errcl = _errl, \
  39510. _rxn = _dyn?(x2 - x1)/_dyn:0, \
  39511. _rcn = _dyn?(c2 - c1)/_dyn:0, \
  39512. _rxr = _dyr?(x2 - x0)/_dyr:0, \
  39513. _rcr = _dyr?(c2 - c0)/_dyr:0, \
  39514. _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \
  39515. (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \
  39516. _rcl = (y0!=y1 && y1>0)?(_dyl?(c1-c0)/_dyl:0): \
  39517. (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcn ); \
  39518. _counter>=0; --_counter, ++y, \
  39519. xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
  39520. cr+=_rcr+((_errcr-=_dcr)<0?_errcr+=_dyr,_scr:0), \
  39521. xl+=(y!=y1)?(cl+=_rcl+((_errcl-=_dcl)<0?(_errcl+=_dyl,_scl):0), \
  39522. _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \
  39523. (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcl=_rcn, cl=c1, \
  39524. _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl))
  39525. #define _cimg_for_triangle3(img,xl,txl,tyl,xr,txr,tyr,y,x0,y0,tx0,ty0,x1,y1,tx1,ty1,x2,y2,tx2,ty2) \
  39526. for (int y = y0<0?0:y0, \
  39527. xr = y0>=0?x0:(x0 - y0*(x2 - x0)/(y2 - y0)), \
  39528. txr = y0>=0?tx0:(tx0 - y0*(tx2 - tx0)/(y2 - y0)), \
  39529. tyr = y0>=0?ty0:(ty0 - y0*(ty2 - ty0)/(y2 - y0)), \
  39530. xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0 - y0*(x1 - x0)/(y1 - y0))):(x1 - y1*(x2 - x1)/(y2 - y1)), \
  39531. txl = y1>=0?(y0>=0?(y0==y1?tx1:tx0):(tx0 - y0*(tx1 - tx0)/(y1 - y0))):(tx1 - y1*(tx2 - tx1)/(y2 - y1)), \
  39532. tyl = y1>=0?(y0>=0?(y0==y1?ty1:ty0):(ty0 - y0*(ty1 - ty0)/(y1 - y0))):(ty1 - y1*(ty2 - ty1)/(y2 - y1)), \
  39533. _sxn=1, _stxn=1, _styn=1, \
  39534. _sxr=1, _stxr=1, _styr=1, \
  39535. _sxl=1, _stxl=1, _styl=1, \
  39536. _dxn = x2>x1?x2 - x1:(_sxn=-1,x1 - x2), \
  39537. _dxr = x2>x0?x2 - x0:(_sxr=-1,x0 - x2), \
  39538. _dxl = x1>x0?x1 - x0:(_sxl=-1,x0 - x1), \
  39539. _dtxn = tx2>tx1?tx2 - tx1:(_stxn=-1,tx1 - tx2), \
  39540. _dtxr = tx2>tx0?tx2 - tx0:(_stxr=-1,tx0 - tx2), \
  39541. _dtxl = tx1>tx0?tx1 - tx0:(_stxl=-1,tx0 - tx1), \
  39542. _dtyn = ty2>ty1?ty2 - ty1:(_styn=-1,ty1 - ty2), \
  39543. _dtyr = ty2>ty0?ty2 - ty0:(_styr=-1,ty0 - ty2), \
  39544. _dtyl = ty1>ty0?ty1 - ty0:(_styl=-1,ty0 - ty1), \
  39545. _dyn = y2-y1, \
  39546. _dyr = y2-y0, \
  39547. _dyl = y1-y0, \
  39548. _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
  39549. _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
  39550. _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
  39551. _dtxn-=_dyn?_dyn*(_dtxn/_dyn):0, \
  39552. _dtxr-=_dyr?_dyr*(_dtxr/_dyr):0, \
  39553. _dtxl-=_dyl?_dyl*(_dtxl/_dyl):0, \
  39554. _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \
  39555. _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \
  39556. _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \
  39557. std::min((int)(img)._height - y - 1,y2 - y)), \
  39558. _errn = _dyn/2, _errtxn = _errn, _errtyn = _errn, \
  39559. _errr = _dyr/2, _errtxr = _errr, _errtyr = _errr, \
  39560. _errl = _dyl/2, _errtxl = _errl, _errtyl = _errl, \
  39561. _rxn = _dyn?(x2 - x1)/_dyn:0, \
  39562. _rtxn = _dyn?(tx2 - tx1)/_dyn:0, \
  39563. _rtyn = _dyn?(ty2 - ty1)/_dyn:0, \
  39564. _rxr = _dyr?(x2 - x0)/_dyr:0, \
  39565. _rtxr = _dyr?(tx2 - tx0)/_dyr:0, \
  39566. _rtyr = _dyr?(ty2 - ty0)/_dyr:0, \
  39567. _rxl = (y0!=y1 && y1>0)?(_dyl?(x1 - x0)/_dyl:0): \
  39568. (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \
  39569. _rtxl = (y0!=y1 && y1>0)?(_dyl?(tx1 - tx0)/_dyl:0): \
  39570. (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxn ), \
  39571. _rtyl = (y0!=y1 && y1>0)?(_dyl?(ty1 - ty0)/_dyl:0): \
  39572. (_errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyn ); \
  39573. _counter>=0; --_counter, ++y, \
  39574. xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
  39575. txr+=_rtxr+((_errtxr-=_dtxr)<0?_errtxr+=_dyr,_stxr:0), \
  39576. tyr+=_rtyr+((_errtyr-=_dtyr)<0?_errtyr+=_dyr,_styr:0), \
  39577. xl+=(y!=y1)?(txl+=_rtxl+((_errtxl-=_dtxl)<0?(_errtxl+=_dyl,_stxl):0), \
  39578. tyl+=_rtyl+((_errtyl-=_dtyl)<0?(_errtyl+=_dyl,_styl):0), \
  39579. _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \
  39580. (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxl=_rtxn, txl=tx1, \
  39581. _errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyl=_rtyn, tyl=ty1,\
  39582. _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1 - xl))
  39583. #define _cimg_for_triangle4(img,xl,cl,txl,tyl,xr,cr,txr,tyr,y,x0,y0,c0,tx0,ty0,x1,y1,c1,tx1,ty1,x2,y2,c2,tx2,ty2) \
  39584. for (int y = y0<0?0:y0, \
  39585. xr = y0>=0?x0:(x0 - y0*(x2 - x0)/(y2 - y0)), \
  39586. cr = y0>=0?c0:(c0 - y0*(c2 - c0)/(y2 - y0)), \
  39587. txr = y0>=0?tx0:(tx0 - y0*(tx2 - tx0)/(y2 - y0)), \
  39588. tyr = y0>=0?ty0:(ty0 - y0*(ty2 - ty0)/(y2 - y0)), \
  39589. xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0 - y0*(x1 - x0)/(y1 - y0))):(x1 - y1*(x2 - x1)/(y2 - y1)), \
  39590. cl = y1>=0?(y0>=0?(y0==y1?c1:c0):(c0 - y0*(c1 - c0)/(y1 - y0))):(c1 - y1*(c2 - c1)/(y2 - y1)), \
  39591. txl = y1>=0?(y0>=0?(y0==y1?tx1:tx0):(tx0 - y0*(tx1 - tx0)/(y1 - y0))):(tx1 - y1*(tx2 - tx1)/(y2 - y1)), \
  39592. tyl = y1>=0?(y0>=0?(y0==y1?ty1:ty0):(ty0 - y0*(ty1 - ty0)/(y1 - y0))):(ty1 - y1*(ty2 - ty1)/(y2 - y1)), \
  39593. _sxn=1, _scn=1, _stxn=1, _styn=1, \
  39594. _sxr=1, _scr=1, _stxr=1, _styr=1, \
  39595. _sxl=1, _scl=1, _stxl=1, _styl=1, \
  39596. _dxn = x2>x1?x2 - x1:(_sxn=-1,x1 - x2), \
  39597. _dxr = x2>x0?x2 - x0:(_sxr=-1,x0 - x2), \
  39598. _dxl = x1>x0?x1 - x0:(_sxl=-1,x0 - x1), \
  39599. _dcn = c2>c1?c2 - c1:(_scn=-1,c1 - c2), \
  39600. _dcr = c2>c0?c2 - c0:(_scr=-1,c0 - c2), \
  39601. _dcl = c1>c0?c1 - c0:(_scl=-1,c0 - c1), \
  39602. _dtxn = tx2>tx1?tx2 - tx1:(_stxn=-1,tx1 - tx2), \
  39603. _dtxr = tx2>tx0?tx2 - tx0:(_stxr=-1,tx0 - tx2), \
  39604. _dtxl = tx1>tx0?tx1 - tx0:(_stxl=-1,tx0 - tx1), \
  39605. _dtyn = ty2>ty1?ty2 - ty1:(_styn=-1,ty1 - ty2), \
  39606. _dtyr = ty2>ty0?ty2 - ty0:(_styr=-1,ty0 - ty2), \
  39607. _dtyl = ty1>ty0?ty1 - ty0:(_styl=-1,ty0 - ty1), \
  39608. _dyn = y2 - y1, \
  39609. _dyr = y2 - y0, \
  39610. _dyl = y1 - y0, \
  39611. _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
  39612. _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
  39613. _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
  39614. _dcn-=_dyn?_dyn*(_dcn/_dyn):0, \
  39615. _dcr-=_dyr?_dyr*(_dcr/_dyr):0, \
  39616. _dcl-=_dyl?_dyl*(_dcl/_dyl):0, \
  39617. _dtxn-=_dyn?_dyn*(_dtxn/_dyn):0, \
  39618. _dtxr-=_dyr?_dyr*(_dtxr/_dyr):0, \
  39619. _dtxl-=_dyl?_dyl*(_dtxl/_dyl):0, \
  39620. _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \
  39621. _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \
  39622. _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \
  39623. std::min((int)(img)._height - y - 1,y2 - y)), \
  39624. _errn = _dyn/2, _errcn = _errn, _errtxn = _errn, _errtyn = _errn, \
  39625. _errr = _dyr/2, _errcr = _errr, _errtxr = _errr, _errtyr = _errr, \
  39626. _errl = _dyl/2, _errcl = _errl, _errtxl = _errl, _errtyl = _errl, \
  39627. _rxn = _dyn?(x2 - x1)/_dyn:0, \
  39628. _rcn = _dyn?(c2 - c1)/_dyn:0, \
  39629. _rtxn = _dyn?(tx2 - tx1)/_dyn:0, \
  39630. _rtyn = _dyn?(ty2 - ty1)/_dyn:0, \
  39631. _rxr = _dyr?(x2 - x0)/_dyr:0, \
  39632. _rcr = _dyr?(c2 - c0)/_dyr:0, \
  39633. _rtxr = _dyr?(tx2 - tx0)/_dyr:0, \
  39634. _rtyr = _dyr?(ty2 - ty0)/_dyr:0, \
  39635. _rxl = (y0!=y1 && y1>0)?(_dyl?(x1 - x0)/_dyl:0): \
  39636. (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \
  39637. _rcl = (y0!=y1 && y1>0)?(_dyl?(c1 - c0)/_dyl:0): \
  39638. (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcn ), \
  39639. _rtxl = (y0!=y1 && y1>0)?(_dyl?(tx1 - tx0)/_dyl:0): \
  39640. (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxn ), \
  39641. _rtyl = (y0!=y1 && y1>0)?(_dyl?(ty1 - ty0)/_dyl:0): \
  39642. (_errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyn ); \
  39643. _counter>=0; --_counter, ++y, \
  39644. xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
  39645. cr+=_rcr+((_errcr-=_dcr)<0?_errcr+=_dyr,_scr:0), \
  39646. txr+=_rtxr+((_errtxr-=_dtxr)<0?_errtxr+=_dyr,_stxr:0), \
  39647. tyr+=_rtyr+((_errtyr-=_dtyr)<0?_errtyr+=_dyr,_styr:0), \
  39648. xl+=(y!=y1)?(cl+=_rcl+((_errcl-=_dcl)<0?(_errcl+=_dyl,_scl):0), \
  39649. txl+=_rtxl+((_errtxl-=_dtxl)<0?(_errtxl+=_dyl,_stxl):0), \
  39650. tyl+=_rtyl+((_errtyl-=_dtyl)<0?(_errtyl+=_dyl,_styl):0), \
  39651. _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \
  39652. (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcl=_rcn, cl=c1, \
  39653. _errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxl=_rtxn, txl=tx1, \
  39654. _errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyl=_rtyn, tyl=ty1, \
  39655. _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1 - xl))
  39656. #define _cimg_for_triangle5(img,xl,txl,tyl,lxl,lyl,xr,txr,tyr,lxr,lyr,y,x0,y0,\
  39657. tx0,ty0,lx0,ly0,x1,y1,tx1,ty1,lx1,ly1,x2,y2,tx2,ty2,lx2,ly2) \
  39658. for (int y = y0<0?0:y0, \
  39659. xr = y0>=0?x0:(x0 - y0*(x2 - x0)/(y2 - y0)), \
  39660. txr = y0>=0?tx0:(tx0 - y0*(tx2 - tx0)/(y2 - y0)), \
  39661. tyr = y0>=0?ty0:(ty0 - y0*(ty2 - ty0)/(y2 - y0)), \
  39662. lxr = y0>=0?lx0:(lx0 - y0*(lx2 - lx0)/(y2 - y0)), \
  39663. lyr = y0>=0?ly0:(ly0 - y0*(ly2 - ly0)/(y2 - y0)), \
  39664. xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0 - y0*(x1 - x0)/(y1 - y0))):(x1 - y1*(x2 - x1)/(y2 - y1)), \
  39665. txl = y1>=0?(y0>=0?(y0==y1?tx1:tx0):(tx0 - y0*(tx1 - tx0)/(y1 - y0))):(tx1 - y1*(tx2 - tx1)/(y2 - y1)), \
  39666. tyl = y1>=0?(y0>=0?(y0==y1?ty1:ty0):(ty0 - y0*(ty1 - ty0)/(y1 - y0))):(ty1 - y1*(ty2 - ty1)/(y2 - y1)), \
  39667. lxl = y1>=0?(y0>=0?(y0==y1?lx1:lx0):(lx0 - y0*(lx1 - lx0)/(y1 - y0))):(lx1 - y1*(lx2 - lx1)/(y2 - y1)), \
  39668. lyl = y1>=0?(y0>=0?(y0==y1?ly1:ly0):(ly0 - y0*(ly1 - ly0)/(y1 - y0))):(ly1 - y1*(ly2 - ly1)/(y2 - y1)), \
  39669. _sxn=1, _stxn=1, _styn=1, _slxn=1, _slyn=1, \
  39670. _sxr=1, _stxr=1, _styr=1, _slxr=1, _slyr=1, \
  39671. _sxl=1, _stxl=1, _styl=1, _slxl=1, _slyl=1, \
  39672. _dxn = x2>x1?x2 - x1:(_sxn=-1,x1 - x2), _dyn = y2 - y1, \
  39673. _dxr = x2>x0?x2 - x0:(_sxr=-1,x0 - x2), _dyr = y2 - y0, \
  39674. _dxl = x1>x0?x1 - x0:(_sxl=-1,x0 - x1), _dyl = y1 - y0, \
  39675. _dtxn = tx2>tx1?tx2 - tx1:(_stxn=-1,tx1 - tx2), \
  39676. _dtxr = tx2>tx0?tx2 - tx0:(_stxr=-1,tx0 - tx2), \
  39677. _dtxl = tx1>tx0?tx1 - tx0:(_stxl=-1,tx0 - tx1), \
  39678. _dtyn = ty2>ty1?ty2 - ty1:(_styn=-1,ty1 - ty2), \
  39679. _dtyr = ty2>ty0?ty2 - ty0:(_styr=-1,ty0 - ty2), \
  39680. _dtyl = ty1>ty0?ty1 - ty0:(_styl=-1,ty0 - ty1), \
  39681. _dlxn = lx2>lx1?lx2 - lx1:(_slxn=-1,lx1 - lx2), \
  39682. _dlxr = lx2>lx0?lx2 - lx0:(_slxr=-1,lx0 - lx2), \
  39683. _dlxl = lx1>lx0?lx1 - lx0:(_slxl=-1,lx0 - lx1), \
  39684. _dlyn = ly2>ly1?ly2 - ly1:(_slyn=-1,ly1 - ly2), \
  39685. _dlyr = ly2>ly0?ly2 - ly0:(_slyr=-1,ly0 - ly2), \
  39686. _dlyl = ly1>ly0?ly1 - ly0:(_slyl=-1,ly0 - ly1), \
  39687. _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
  39688. _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
  39689. _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
  39690. _dtxn-=_dyn?_dyn*(_dtxn/_dyn):0, \
  39691. _dtxr-=_dyr?_dyr*(_dtxr/_dyr):0, \
  39692. _dtxl-=_dyl?_dyl*(_dtxl/_dyl):0, \
  39693. _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \
  39694. _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \
  39695. _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \
  39696. _dlxn-=_dyn?_dyn*(_dlxn/_dyn):0, \
  39697. _dlxr-=_dyr?_dyr*(_dlxr/_dyr):0, \
  39698. _dlxl-=_dyl?_dyl*(_dlxl/_dyl):0, \
  39699. _dlyn-=_dyn?_dyn*(_dlyn/_dyn):0, \
  39700. _dlyr-=_dyr?_dyr*(_dlyr/_dyr):0, \
  39701. _dlyl-=_dyl?_dyl*(_dlyl/_dyl):0, \
  39702. std::min((int)(img)._height - y - 1,y2 - y)), \
  39703. _errn = _dyn/2, _errtxn = _errn, _errtyn = _errn, _errlxn = _errn, _errlyn = _errn, \
  39704. _errr = _dyr/2, _errtxr = _errr, _errtyr = _errr, _errlxr = _errr, _errlyr = _errr, \
  39705. _errl = _dyl/2, _errtxl = _errl, _errtyl = _errl, _errlxl = _errl, _errlyl = _errl, \
  39706. _rxn = _dyn?(x2 - x1)/_dyn:0, \
  39707. _rtxn = _dyn?(tx2 - tx1)/_dyn:0, \
  39708. _rtyn = _dyn?(ty2 - ty1)/_dyn:0, \
  39709. _rlxn = _dyn?(lx2 - lx1)/_dyn:0, \
  39710. _rlyn = _dyn?(ly2 - ly1)/_dyn:0, \
  39711. _rxr = _dyr?(x2 - x0)/_dyr:0, \
  39712. _rtxr = _dyr?(tx2 - tx0)/_dyr:0, \
  39713. _rtyr = _dyr?(ty2 - ty0)/_dyr:0, \
  39714. _rlxr = _dyr?(lx2 - lx0)/_dyr:0, \
  39715. _rlyr = _dyr?(ly2 - ly0)/_dyr:0, \
  39716. _rxl = (y0!=y1 && y1>0)?(_dyl?(x1 - x0)/_dyl:0): \
  39717. (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \
  39718. _rtxl = (y0!=y1 && y1>0)?(_dyl?(tx1 - tx0)/_dyl:0): \
  39719. (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxn ), \
  39720. _rtyl = (y0!=y1 && y1>0)?(_dyl?(ty1 - ty0)/_dyl:0): \
  39721. (_errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyn ), \
  39722. _rlxl = (y0!=y1 && y1>0)?(_dyl?(lx1 - lx0)/_dyl:0): \
  39723. (_errlxl=_errlxn, _dlxl=_dlxn, _dyl=_dyn, _slxl=_slxn, _rlxn ), \
  39724. _rlyl = (y0!=y1 && y1>0)?(_dyl?(ly1 - ly0)/_dyl:0): \
  39725. (_errlyl=_errlyn, _dlyl=_dlyn, _dyl=_dyn, _slyl=_slyn, _rlyn ); \
  39726. _counter>=0; --_counter, ++y, \
  39727. xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
  39728. txr+=_rtxr+((_errtxr-=_dtxr)<0?_errtxr+=_dyr,_stxr:0), \
  39729. tyr+=_rtyr+((_errtyr-=_dtyr)<0?_errtyr+=_dyr,_styr:0), \
  39730. lxr+=_rlxr+((_errlxr-=_dlxr)<0?_errlxr+=_dyr,_slxr:0), \
  39731. lyr+=_rlyr+((_errlyr-=_dlyr)<0?_errlyr+=_dyr,_slyr:0), \
  39732. xl+=(y!=y1)?(txl+=_rtxl+((_errtxl-=_dtxl)<0?(_errtxl+=_dyl,_stxl):0), \
  39733. tyl+=_rtyl+((_errtyl-=_dtyl)<0?(_errtyl+=_dyl,_styl):0), \
  39734. lxl+=_rlxl+((_errlxl-=_dlxl)<0?(_errlxl+=_dyl,_slxl):0), \
  39735. lyl+=_rlyl+((_errlyl-=_dlyl)<0?(_errlyl+=_dyl,_slyl):0), \
  39736. _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \
  39737. (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxl=_rtxn, txl=tx1, \
  39738. _errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyl=_rtyn, tyl=ty1, \
  39739. _errlxl=_errlxn, _dlxl=_dlxn, _dyl=_dyn, _slxl=_slxn, _rlxl=_rlxn, lxl=lx1, \
  39740. _errlyl=_errlyn, _dlyl=_dlyn, _dyl=_dyn, _slyl=_slyn, _rlyl=_rlyn, lyl=ly1, \
  39741. _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1 - xl))
  39742. // [internal] Draw a filled triangle.
  39743. template<typename tc>
  39744. CImg<T>& _draw_triangle(const int x0, const int y0,
  39745. const int x1, const int y1,
  39746. const int x2, const int y2,
  39747. const tc *const color, const float opacity,
  39748. const float brightness) {
  39749. cimg_init_scanline(color,opacity);
  39750. const float nbrightness = cimg::cut(brightness,0,2);
  39751. int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2;
  39752. if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1);
  39753. if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2);
  39754. if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2);
  39755. if (ny0<height() && ny2>=0) {
  39756. if ((nx1 - nx0)*(ny2 - ny0) - (nx2 - nx0)*(ny1 - ny0)<0)
  39757. _cimg_for_triangle1(*this,xl,xr,y,nx0,ny0,nx1,ny1,nx2,ny2)
  39758. cimg_draw_scanline(xl,xr,y,color,opacity,nbrightness);
  39759. else
  39760. _cimg_for_triangle1(*this,xl,xr,y,nx0,ny0,nx1,ny1,nx2,ny2)
  39761. cimg_draw_scanline(xr,xl,y,color,opacity,nbrightness);
  39762. }
  39763. return *this;
  39764. }
  39765. //! Draw a filled 2d triangle.
  39766. /**
  39767. \param x0 X-coordinate of the first vertex.
  39768. \param y0 Y-coordinate of the first vertex.
  39769. \param x1 X-coordinate of the second vertex.
  39770. \param y1 Y-coordinate of the second vertex.
  39771. \param x2 X-coordinate of the third vertex.
  39772. \param y2 Y-coordinate of the third vertex.
  39773. \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
  39774. \param opacity Drawing opacity.
  39775. **/
  39776. template<typename tc>
  39777. CImg<T>& draw_triangle(const int x0, const int y0,
  39778. const int x1, const int y1,
  39779. const int x2, const int y2,
  39780. const tc *const color, const float opacity=1) {
  39781. if (is_empty()) return *this;
  39782. if (!color)
  39783. throw CImgArgumentException(_cimg_instance
  39784. "draw_triangle(): Specified color is (null).",
  39785. cimg_instance);
  39786. _draw_triangle(x0,y0,x1,y1,x2,y2,color,opacity,1);
  39787. return *this;
  39788. }
  39789. //! Draw a outlined 2d triangle.
  39790. /**
  39791. \param x0 X-coordinate of the first vertex.
  39792. \param y0 Y-coordinate of the first vertex.
  39793. \param x1 X-coordinate of the second vertex.
  39794. \param y1 Y-coordinate of the second vertex.
  39795. \param x2 X-coordinate of the third vertex.
  39796. \param y2 Y-coordinate of the third vertex.
  39797. \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
  39798. \param opacity Drawing opacity.
  39799. \param pattern An integer whose bits describe the outline pattern.
  39800. **/
  39801. template<typename tc>
  39802. CImg<T>& draw_triangle(const int x0, const int y0,
  39803. const int x1, const int y1,
  39804. const int x2, const int y2,
  39805. const tc *const color, const float opacity,
  39806. const unsigned int pattern) {
  39807. if (is_empty()) return *this;
  39808. if (!color)
  39809. throw CImgArgumentException(_cimg_instance
  39810. "draw_triangle(): Specified color is (null).",
  39811. cimg_instance);
  39812. draw_line(x0,y0,x1,y1,color,opacity,pattern,true).
  39813. draw_line(x1,y1,x2,y2,color,opacity,pattern,false).
  39814. draw_line(x2,y2,x0,y0,color,opacity,pattern,false);
  39815. return *this;
  39816. }
  39817. //! Draw a filled 2d triangle, with z-buffering.
  39818. /**
  39819. \param zbuffer Z-buffer image.
  39820. \param x0 X-coordinate of the first vertex.
  39821. \param y0 Y-coordinate of the first vertex.
  39822. \param z0 Z-coordinate of the first vertex.
  39823. \param x1 X-coordinate of the second vertex.
  39824. \param y1 Y-coordinate of the second vertex.
  39825. \param z1 Z-coordinate of the second vertex.
  39826. \param x2 X-coordinate of the third vertex.
  39827. \param y2 Y-coordinate of the third vertex.
  39828. \param z2 Z-coordinate of the third vertex.
  39829. \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
  39830. \param opacity Drawing opacity.
  39831. \param brightness Brightness factor.
  39832. **/
  39833. template<typename tz, typename tc>
  39834. CImg<T>& draw_triangle(CImg<tz>& zbuffer,
  39835. const int x0, const int y0, const float z0,
  39836. const int x1, const int y1, const float z1,
  39837. const int x2, const int y2, const float z2,
  39838. const tc *const color, const float opacity=1,
  39839. const float brightness=1) {
  39840. typedef typename cimg::superset<tz,float>::type tzfloat;
  39841. if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
  39842. if (!color)
  39843. throw CImgArgumentException(_cimg_instance
  39844. "draw_triangle(): Specified color is (null).",
  39845. cimg_instance);
  39846. if (!is_sameXY(zbuffer))
  39847. throw CImgArgumentException(_cimg_instance
  39848. "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
  39849. "different dimensions.",
  39850. cimg_instance,
  39851. zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
  39852. static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max());
  39853. const float
  39854. nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f),
  39855. nbrightness = cimg::cut(brightness,0,2);
  39856. const longT whd = (longT)width()*height()*depth(), offx = spectrum()*whd;
  39857. int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2;
  39858. tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
  39859. if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1);
  39860. if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nz0,nz2);
  39861. if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nz1,nz2);
  39862. if (ny0>=height() || ny2<0) return *this;
  39863. tzfloat
  39864. pzl = (nz1 - nz0)/(ny1 - ny0),
  39865. pzr = (nz2 - nz0)/(ny2 - ny0),
  39866. pzn = (nz2 - nz1)/(ny2 - ny1),
  39867. zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
  39868. zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
  39869. _cimg_for_triangle1(*this,xleft0,xright0,y,nx0,ny0,nx1,ny1,nx2,ny2) {
  39870. if (y==ny1) { zl = nz1; pzl = pzn; }
  39871. int xleft = xleft0, xright = xright0;
  39872. tzfloat zleft = zl, zright = zr;
  39873. if (xright<xleft) cimg::swap(xleft,xright,zleft,zright);
  39874. const int dx = xright - xleft;
  39875. const tzfloat pentez = (zright - zleft)/dx;
  39876. if (xleft<0 && dx) zleft-=xleft*(zright - zleft)/dx;
  39877. if (xleft<0) xleft = 0;
  39878. if (xright>=width() - 1) xright = width() - 1;
  39879. T* ptrd = data(xleft,y,0,0);
  39880. tz *ptrz = xleft<=xright?zbuffer.data(xleft,y):0;
  39881. if (opacity>=1) {
  39882. if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
  39883. if (zleft>=(tzfloat)*ptrz) {
  39884. *ptrz = (tz)zleft;
  39885. const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; }
  39886. ptrd-=offx;
  39887. }
  39888. zleft+=pentez;
  39889. } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
  39890. if (zleft>=(tzfloat)*ptrz) {
  39891. *ptrz = (tz)zleft;
  39892. const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)(nbrightness*(*col++)); ptrd+=whd; }
  39893. ptrd-=offx;
  39894. }
  39895. zleft+=pentez;
  39896. } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
  39897. if (zleft>=(tzfloat)*ptrz) {
  39898. *ptrz = (tz)zleft;
  39899. const tc *col = color;
  39900. cimg_forC(*this,c) { *ptrd = (T)((2 - nbrightness)**(col++) + (nbrightness - 1)*maxval); ptrd+=whd; }
  39901. ptrd-=offx;
  39902. }
  39903. zleft+=pentez;
  39904. }
  39905. } else {
  39906. if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
  39907. if (zleft>=(tzfloat)*ptrz) {
  39908. *ptrz = (tz)zleft;
  39909. const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=whd; }
  39910. ptrd-=offx;
  39911. }
  39912. zleft+=pentez;
  39913. } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
  39914. if (zleft>=(tzfloat)*ptrz) {
  39915. *ptrz = (tz)zleft;
  39916. const tc *col = color;
  39917. cimg_forC(*this,c) { *ptrd = (T)(nopacity*nbrightness**(col++) + *ptrd*copacity); ptrd+=whd; }
  39918. ptrd-=offx;
  39919. }
  39920. zleft+=pentez;
  39921. } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
  39922. if (zleft>=(tzfloat)*ptrz) {
  39923. *ptrz = (tz)zleft;
  39924. const tc *col = color;
  39925. cimg_forC(*this,c) {
  39926. const T val = (T)((2 - nbrightness)**(col++) + (nbrightness - 1)*maxval);
  39927. *ptrd = (T)(nopacity*val + *ptrd*copacity);
  39928. ptrd+=whd;
  39929. }
  39930. ptrd-=offx;
  39931. }
  39932. zleft+=pentez;
  39933. }
  39934. }
  39935. zr+=pzr; zl+=pzl;
  39936. }
  39937. return *this;
  39938. }
  39939. //! Draw a Gouraud-shaded 2d triangle.
  39940. /**
  39941. \param x0 X-coordinate of the first vertex in the image instance.
  39942. \param y0 Y-coordinate of the first vertex in the image instance.
  39943. \param x1 X-coordinate of the second vertex in the image instance.
  39944. \param y1 Y-coordinate of the second vertex in the image instance.
  39945. \param x2 X-coordinate of the third vertex in the image instance.
  39946. \param y2 Y-coordinate of the third vertex in the image instance.
  39947. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  39948. \param brightness0 Brightness factor of the first vertex (in [0,2]).
  39949. \param brightness1 brightness factor of the second vertex (in [0,2]).
  39950. \param brightness2 brightness factor of the third vertex (in [0,2]).
  39951. \param opacity Drawing opacity.
  39952. **/
  39953. template<typename tc>
  39954. CImg<T>& draw_triangle(const int x0, const int y0,
  39955. const int x1, const int y1,
  39956. const int x2, const int y2,
  39957. const tc *const color,
  39958. const float brightness0,
  39959. const float brightness1,
  39960. const float brightness2,
  39961. const float opacity=1) {
  39962. if (is_empty()) return *this;
  39963. if (!color)
  39964. throw CImgArgumentException(_cimg_instance
  39965. "draw_triangle(): Specified color is (null).",
  39966. cimg_instance);
  39967. static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max());
  39968. const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
  39969. const longT whd = (longT)width()*height()*depth(), offx = spectrum()*whd - 1;
  39970. int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
  39971. nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
  39972. nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
  39973. nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
  39974. if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nc0,nc1);
  39975. if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nc0,nc2);
  39976. if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nc1,nc2);
  39977. if (ny0>=height() || ny2<0) return *this;
  39978. _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) {
  39979. int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0;
  39980. if (xright<xleft) cimg::swap(xleft,xright,cleft,cright);
  39981. const int
  39982. dx = xright - xleft,
  39983. dc = cright>cleft?cright - cleft:cleft - cright,
  39984. rc = dx?(cright - cleft)/dx:0,
  39985. sc = cright>cleft?1:-1,
  39986. ndc = dc - (dx?dx*(dc/dx):0);
  39987. int errc = dx>>1;
  39988. if (xleft<0 && dx) cleft-=xleft*(cright - cleft)/dx;
  39989. if (xleft<0) xleft = 0;
  39990. if (xright>=width() - 1) xright = width() - 1;
  39991. T* ptrd = data(xleft,y);
  39992. if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
  39993. const tc *col = color;
  39994. cimg_forC(*this,c) {
  39995. *ptrd = (T)(cleft<256?cleft**(col++)/256:((512 - cleft)**(col++)+(cleft - 256)*maxval)/256);
  39996. ptrd+=whd;
  39997. }
  39998. ptrd-=offx;
  39999. cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
  40000. } else for (int x = xleft; x<=xright; ++x) {
  40001. const tc *col = color;
  40002. cimg_forC(*this,c) {
  40003. const T val = (T)(cleft<256?cleft**(col++)/256:((512 - cleft)**(col++)+(cleft - 256)*maxval)/256);
  40004. *ptrd = (T)(nopacity*val + *ptrd*copacity);
  40005. ptrd+=whd;
  40006. }
  40007. ptrd-=offx;
  40008. cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
  40009. }
  40010. }
  40011. return *this;
  40012. }
  40013. //! Draw a Gouraud-shaded 2d triangle, with z-buffering \overloading.
  40014. template<typename tz, typename tc>
  40015. CImg<T>& draw_triangle(CImg<tz>& zbuffer,
  40016. const int x0, const int y0, const float z0,
  40017. const int x1, const int y1, const float z1,
  40018. const int x2, const int y2, const float z2,
  40019. const tc *const color,
  40020. const float brightness0,
  40021. const float brightness1,
  40022. const float brightness2,
  40023. const float opacity=1) {
  40024. typedef typename cimg::superset<tz,float>::type tzfloat;
  40025. if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
  40026. if (!color)
  40027. throw CImgArgumentException(_cimg_instance
  40028. "draw_triangle(): Specified color is (null).",
  40029. cimg_instance);
  40030. if (!is_sameXY(zbuffer))
  40031. throw CImgArgumentException(_cimg_instance
  40032. "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
  40033. "different dimensions.",
  40034. cimg_instance,
  40035. zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
  40036. static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max());
  40037. const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
  40038. const longT whd = (longT)width()*height()*depth(), offx = spectrum()*whd;
  40039. int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
  40040. nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
  40041. nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
  40042. nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
  40043. tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
  40044. if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1,nc0,nc1);
  40045. if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nz0,nz2,nc0,nc2);
  40046. if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nz1,nz2,nc1,nc2);
  40047. if (ny0>=height() || ny2<0) return *this;
  40048. tzfloat
  40049. pzl = (nz1 - nz0)/(ny1 - ny0),
  40050. pzr = (nz2 - nz0)/(ny2 - ny0),
  40051. pzn = (nz2 - nz1)/(ny2 - ny1),
  40052. zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
  40053. zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
  40054. _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) {
  40055. if (y==ny1) { zl = nz1; pzl = pzn; }
  40056. int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0;
  40057. tzfloat zleft = zl, zright = zr;
  40058. if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,cleft,cright);
  40059. const int
  40060. dx = xright - xleft,
  40061. dc = cright>cleft?cright - cleft:cleft - cright,
  40062. rc = dx?(cright - cleft)/dx:0,
  40063. sc = cright>cleft?1:-1,
  40064. ndc = dc - (dx?dx*(dc/dx):0);
  40065. const tzfloat pentez = (zright - zleft)/dx;
  40066. int errc = dx>>1;
  40067. if (xleft<0 && dx) {
  40068. cleft-=xleft*(cright - cleft)/dx;
  40069. zleft-=xleft*(zright - zleft)/dx;
  40070. }
  40071. if (xleft<0) xleft = 0;
  40072. if (xright>=width() - 1) xright = width() - 1;
  40073. T *ptrd = data(xleft,y);
  40074. tz *ptrz = xleft<=xright?zbuffer.data(xleft,y):0;
  40075. if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) {
  40076. if (zleft>=(tzfloat)*ptrz) {
  40077. *ptrz = (tz)zleft;
  40078. const tc *col = color;
  40079. cimg_forC(*this,c) {
  40080. *ptrd = (T)(cleft<256?cleft**(col++)/256:((512 - cleft)**(col++)+(cleft - 256)*maxval)/256);
  40081. ptrd+=whd;
  40082. }
  40083. ptrd-=offx;
  40084. }
  40085. zleft+=pentez;
  40086. cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
  40087. } else for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) {
  40088. if (zleft>=(tzfloat)*ptrz) {
  40089. *ptrz = (tz)zleft;
  40090. const tc *col = color;
  40091. cimg_forC(*this,c) {
  40092. const T val = (T)(cleft<256?cleft**(col++)/256:((512 - cleft)**(col++)+(cleft - 256)*maxval)/256);
  40093. *ptrd = (T)(nopacity*val + *ptrd*copacity);
  40094. ptrd+=whd;
  40095. }
  40096. ptrd-=offx;
  40097. }
  40098. zleft+=pentez;
  40099. cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
  40100. }
  40101. zr+=pzr; zl+=pzl;
  40102. }
  40103. return *this;
  40104. }
  40105. //! Draw a color-interpolated 2d triangle.
  40106. /**
  40107. \param x0 X-coordinate of the first vertex in the image instance.
  40108. \param y0 Y-coordinate of the first vertex in the image instance.
  40109. \param x1 X-coordinate of the second vertex in the image instance.
  40110. \param y1 Y-coordinate of the second vertex in the image instance.
  40111. \param x2 X-coordinate of the third vertex in the image instance.
  40112. \param y2 Y-coordinate of the third vertex in the image instance.
  40113. \param color1 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the first vertex.
  40114. \param color2 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the seconf vertex.
  40115. \param color3 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the third vertex.
  40116. \param opacity Drawing opacity.
  40117. **/
  40118. template<typename tc1, typename tc2, typename tc3>
  40119. CImg<T>& draw_triangle(const int x0, const int y0,
  40120. const int x1, const int y1,
  40121. const int x2, const int y2,
  40122. const tc1 *const color1,
  40123. const tc2 *const color2,
  40124. const tc3 *const color3,
  40125. const float opacity=1) {
  40126. const unsigned char one = 1;
  40127. cimg_forC(*this,c)
  40128. get_shared_channel(c).draw_triangle(x0,y0,x1,y1,x2,y2,&one,color1[c],color2[c],color3[c],opacity);
  40129. return *this;
  40130. }
  40131. //! Draw a textured 2d triangle.
  40132. /**
  40133. \param x0 X-coordinate of the first vertex in the image instance.
  40134. \param y0 Y-coordinate of the first vertex in the image instance.
  40135. \param x1 X-coordinate of the second vertex in the image instance.
  40136. \param y1 Y-coordinate of the second vertex in the image instance.
  40137. \param x2 X-coordinate of the third vertex in the image instance.
  40138. \param y2 Y-coordinate of the third vertex in the image instance.
  40139. \param texture Texture image used to fill the triangle.
  40140. \param tx0 X-coordinate of the first vertex in the texture image.
  40141. \param ty0 Y-coordinate of the first vertex in the texture image.
  40142. \param tx1 X-coordinate of the second vertex in the texture image.
  40143. \param ty1 Y-coordinate of the second vertex in the texture image.
  40144. \param tx2 X-coordinate of the third vertex in the texture image.
  40145. \param ty2 Y-coordinate of the third vertex in the texture image.
  40146. \param opacity Drawing opacity.
  40147. \param brightness Brightness factor of the drawing (in [0,2]).
  40148. **/
  40149. template<typename tc>
  40150. CImg<T>& draw_triangle(const int x0, const int y0,
  40151. const int x1, const int y1,
  40152. const int x2, const int y2,
  40153. const CImg<tc>& texture,
  40154. const int tx0, const int ty0,
  40155. const int tx1, const int ty1,
  40156. const int tx2, const int ty2,
  40157. const float opacity=1,
  40158. const float brightness=1) {
  40159. if (is_empty()) return *this;
  40160. if (texture._depth>1 || texture._spectrum<_spectrum)
  40161. throw CImgArgumentException(_cimg_instance
  40162. "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
  40163. cimg_instance,
  40164. texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
  40165. if (is_overlapped(texture))
  40166. return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);
  40167. static const T maxval = (T)std::min(cimg::type<T>::max(),cimg::type<tc>::max());
  40168. const float
  40169. nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f),
  40170. nbrightness = cimg::cut(brightness,0,2);
  40171. const ulongT
  40172. whd = (ulongT)_width*_height*_depth,
  40173. twh = (ulongT)texture._width*texture._height,
  40174. offx = _spectrum*whd - 1;
  40175. int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
  40176. ntx0 = tx0, nty0 = ty0, ntx1 = tx1, nty1 = ty1, ntx2 = tx2, nty2 = ty2;
  40177. if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1);
  40178. if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2);
  40179. if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2);
  40180. if (ny0>=height() || ny2<0) return *this;
  40181. _cimg_for_triangle3(*this,xleft0,txleft0,tyleft0,xright0,txright0,tyright0,y,
  40182. nx0,ny0,ntx0,nty0,nx1,ny1,ntx1,nty1,nx2,ny2,ntx2,nty2) {
  40183. int
  40184. xleft = xleft0, xright = xright0,
  40185. txleft = txleft0, txright = txright0,
  40186. tyleft = tyleft0, tyright = tyright0;
  40187. if (xright<xleft) cimg::swap(xleft,xright,txleft,txright,tyleft,tyright);
  40188. const int
  40189. dx = xright - xleft,
  40190. dtx = txright>txleft?txright - txleft:txleft - txright,
  40191. dty = tyright>tyleft?tyright - tyleft:tyleft - tyright,
  40192. rtx = dx?(txright - txleft)/dx:0,
  40193. rty = dx?(tyright - tyleft)/dx:0,
  40194. stx = txright>txleft?1:-1,
  40195. sty = tyright>tyleft?1:-1,
  40196. ndtx = dtx - (dx?dx*(dtx/dx):0),
  40197. ndty = dty - (dx?dx*(dty/dx):0);
  40198. int errtx = dx>>1, errty = errtx;
  40199. if (xleft<0 && dx) {
  40200. txleft-=xleft*(txright - txleft)/dx;
  40201. tyleft-=xleft*(tyright - tyleft)/dx;
  40202. }
  40203. if (xleft<0) xleft = 0;
  40204. if (xright>=width() - 1) xright = width() - 1;
  40205. T* ptrd = data(xleft,y,0,0);
  40206. if (opacity>=1) {
  40207. if (nbrightness==1) for (int x = xleft; x<=xright; ++x) {
  40208. const tc *col = &texture._atXY(txleft,tyleft);
  40209. cimg_forC(*this,c) {
  40210. *ptrd = (T)*col;
  40211. ptrd+=whd; col+=twh;
  40212. }
  40213. ptrd-=offx;
  40214. txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
  40215. tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
  40216. } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x) {
  40217. const tc *col = &texture._atXY(txleft,tyleft);
  40218. cimg_forC(*this,c) {
  40219. *ptrd = (T)(nbrightness**col);
  40220. ptrd+=whd; col+=twh;
  40221. }
  40222. ptrd-=offx;
  40223. txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
  40224. tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
  40225. } else for (int x = xleft; x<=xright; ++x) {
  40226. const tc *col = &texture._atXY(txleft,tyleft);
  40227. cimg_forC(*this,c) {
  40228. *ptrd = (T)((2 - nbrightness)**(col++) + (nbrightness - 1)*maxval);
  40229. ptrd+=whd; col+=twh;
  40230. }
  40231. ptrd-=offx;
  40232. txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
  40233. tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
  40234. }
  40235. } else {
  40236. if (nbrightness==1) for (int x = xleft; x<=xright; ++x) {
  40237. const tc *col = &texture._atXY(txleft,tyleft);
  40238. cimg_forC(*this,c) {
  40239. *ptrd = (T)(nopacity**col + *ptrd*copacity);
  40240. ptrd+=whd; col+=twh;
  40241. }
  40242. ptrd-=offx;
  40243. txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
  40244. tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
  40245. } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x) {
  40246. const tc *col = &texture._atXY(txleft,tyleft);
  40247. cimg_forC(*this,c) {
  40248. *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity);
  40249. ptrd+=whd; col+=twh;
  40250. }
  40251. ptrd-=offx;
  40252. txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
  40253. tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
  40254. } else for (int x = xleft; x<=xright; ++x) {
  40255. const tc *col = &texture._atXY(txleft,tyleft);
  40256. cimg_forC(*this,c) {
  40257. const T val = (T)((2 - nbrightness)**(col++) + (nbrightness - 1)*maxval);
  40258. *ptrd = (T)(nopacity*val + *ptrd*copacity);
  40259. ptrd+=whd; col+=twh;
  40260. }
  40261. ptrd-=offx;
  40262. txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
  40263. tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
  40264. }
  40265. }
  40266. }
  40267. return *this;
  40268. }
  40269. //! Draw a 2d textured triangle, with perspective correction.
  40270. template<typename tc>
  40271. CImg<T>& draw_triangle(const int x0, const int y0, const float z0,
  40272. const int x1, const int y1, const float z1,
  40273. const int x2, const int y2, const float z2,
  40274. const CImg<tc>& texture,
  40275. const int tx0, const int ty0,
  40276. const int tx1, const int ty1,
  40277. const int tx2, const int ty2,
  40278. const float opacity=1,
  40279. const float brightness=1) {
  40280. if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
  40281. if (texture._depth>1 || texture._spectrum<_spectrum)
  40282. throw CImgArgumentException(_cimg_instance
  40283. "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
  40284. cimg_instance,
  40285. texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
  40286. if (is_overlapped(texture))
  40287. return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);
  40288. static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max());
  40289. const float
  40290. nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f),
  40291. nbrightness = cimg::cut(brightness,0,2);
  40292. const ulongT
  40293. whd = (ulongT)_width*_height*_depth,
  40294. twh = (ulongT)texture._width*texture._height,
  40295. offx = _spectrum*whd - 1;
  40296. int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2;
  40297. float
  40298. ntx0 = tx0/z0, nty0 = ty0/z0,
  40299. ntx1 = tx1/z1, nty1 = ty1/z1,
  40300. ntx2 = tx2/z2, nty2 = ty2/z2,
  40301. nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2;
  40302. if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1);
  40303. if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2);
  40304. if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2);
  40305. if (ny0>=height() || ny2<0) return *this;
  40306. float
  40307. ptxl = (ntx1 - ntx0)/(ny1 - ny0),
  40308. ptxr = (ntx2 - ntx0)/(ny2 - ny0),
  40309. ptxn = (ntx2 - ntx1)/(ny2 - ny1),
  40310. ptyl = (nty1 - nty0)/(ny1 - ny0),
  40311. ptyr = (nty2 - nty0)/(ny2 - ny0),
  40312. ptyn = (nty2 - nty1)/(ny2 - ny1),
  40313. pzl = (nz1 - nz0)/(ny1 - ny0),
  40314. pzr = (nz2 - nz0)/(ny2 - ny0),
  40315. pzn = (nz2 - nz1)/(ny2 - ny1),
  40316. zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
  40317. txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
  40318. tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
  40319. zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))),
  40320. txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):
  40321. (ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
  40322. tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):
  40323. (ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
  40324. _cimg_for_triangle1(*this,xleft0,xright0,y,nx0,ny0,nx1,ny1,nx2,ny2) {
  40325. if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
  40326. int xleft = xleft0, xright = xright0;
  40327. float
  40328. zleft = zl, zright = zr,
  40329. txleft = txl, txright = txr,
  40330. tyleft = tyl, tyright = tyr;
  40331. if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright);
  40332. const int dx = xright - xleft;
  40333. const float
  40334. pentez = (zright - zleft)/dx,
  40335. pentetx = (txright - txleft)/dx,
  40336. pentety = (tyright - tyleft)/dx;
  40337. if (xleft<0 && dx) {
  40338. zleft-=xleft*(zright - zleft)/dx;
  40339. txleft-=xleft*(txright - txleft)/dx;
  40340. tyleft-=xleft*(tyright - tyleft)/dx;
  40341. }
  40342. if (xleft<0) xleft = 0;
  40343. if (xright>=width() - 1) xright = width() - 1;
  40344. T* ptrd = data(xleft,y,0,0);
  40345. if (opacity>=1) {
  40346. if (nbrightness==1) for (int x = xleft; x<=xright; ++x) {
  40347. const float invz = 1/zleft;
  40348. const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
  40349. cimg_forC(*this,c) {
  40350. *ptrd = (T)*col;
  40351. ptrd+=whd; col+=twh;
  40352. }
  40353. ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
  40354. } else if (nbrightness<1) for (int x=xleft; x<=xright; ++x) {
  40355. const float invz = 1/zleft;
  40356. const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
  40357. cimg_forC(*this,c) {
  40358. *ptrd = (T)(nbrightness**col);
  40359. ptrd+=whd; col+=twh;
  40360. }
  40361. ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
  40362. } else for (int x = xleft; x<=xright; ++x) {
  40363. const float invz = 1/zleft;
  40364. const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
  40365. cimg_forC(*this,c) {
  40366. *ptrd = (T)((2 - nbrightness)**col + (nbrightness - 1)*maxval);
  40367. ptrd+=whd; col+=twh;
  40368. }
  40369. ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
  40370. }
  40371. } else {
  40372. if (nbrightness==1) for (int x = xleft; x<=xright; ++x) {
  40373. const float invz = 1/zleft;
  40374. const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
  40375. cimg_forC(*this,c) {
  40376. *ptrd = (T)(nopacity**col + *ptrd*copacity);
  40377. ptrd+=whd; col+=twh;
  40378. }
  40379. ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
  40380. } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x) {
  40381. const float invz = 1/zleft;
  40382. const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
  40383. cimg_forC(*this,c) {
  40384. *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity);
  40385. ptrd+=whd; col+=twh;
  40386. }
  40387. ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
  40388. } else for (int x = xleft; x<=xright; ++x) {
  40389. const float invz = 1/zleft;
  40390. const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
  40391. cimg_forC(*this,c) {
  40392. const T val = (T)((2 - nbrightness)**col + (nbrightness - 1)*maxval);
  40393. *ptrd = (T)(nopacity*val + *ptrd*copacity);
  40394. ptrd+=whd; col+=twh;
  40395. }
  40396. ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
  40397. }
  40398. }
  40399. zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
  40400. }
  40401. return *this;
  40402. }
  40403. //! Draw a textured 2d triangle, with perspective correction and z-buffering.
  40404. template<typename tz, typename tc>
  40405. CImg<T>& draw_triangle(CImg<tz>& zbuffer,
  40406. const int x0, const int y0, const float z0,
  40407. const int x1, const int y1, const float z1,
  40408. const int x2, const int y2, const float z2,
  40409. const CImg<tc>& texture,
  40410. const int tx0, const int ty0,
  40411. const int tx1, const int ty1,
  40412. const int tx2, const int ty2,
  40413. const float opacity=1,
  40414. const float brightness=1) {
  40415. typedef typename cimg::superset<tz,float>::type tzfloat;
  40416. if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
  40417. if (!is_sameXY(zbuffer))
  40418. throw CImgArgumentException(_cimg_instance
  40419. "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
  40420. "different dimensions.",
  40421. cimg_instance,
  40422. zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
  40423. if (texture._depth>1 || texture._spectrum<_spectrum)
  40424. throw CImgArgumentException(_cimg_instance
  40425. "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
  40426. cimg_instance,
  40427. texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
  40428. if (is_overlapped(texture))
  40429. return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);
  40430. static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max());
  40431. const float
  40432. nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f),
  40433. nbrightness = cimg::cut(brightness,0,2);
  40434. const ulongT
  40435. whd = (ulongT)_width*_height*_depth,
  40436. twh = (ulongT)texture._width*texture._height,
  40437. offx = _spectrum*whd;
  40438. int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2;
  40439. float
  40440. ntx0 = tx0/z0, nty0 = ty0/z0,
  40441. ntx1 = tx1/z1, nty1 = ty1/z1,
  40442. ntx2 = tx2/z2, nty2 = ty2/z2;
  40443. tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
  40444. if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1);
  40445. if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2);
  40446. if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2);
  40447. if (ny0>=height() || ny2<0) return *this;
  40448. float
  40449. ptxl = (ntx1 - ntx0)/(ny1 - ny0),
  40450. ptxr = (ntx2 - ntx0)/(ny2 - ny0),
  40451. ptxn = (ntx2 - ntx1)/(ny2 - ny1),
  40452. ptyl = (nty1 - nty0)/(ny1 - ny0),
  40453. ptyr = (nty2 - nty0)/(ny2 - ny0),
  40454. ptyn = (nty2 - nty1)/(ny2 - ny1),
  40455. txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
  40456. tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
  40457. txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):
  40458. (ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
  40459. tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):
  40460. (ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
  40461. tzfloat
  40462. pzl = (nz1 - nz0)/(ny1 - ny0),
  40463. pzr = (nz2 - nz0)/(ny2 - ny0),
  40464. pzn = (nz2 - nz1)/(ny2 - ny1),
  40465. zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
  40466. zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
  40467. _cimg_for_triangle1(*this,xleft0,xright0,y,nx0,ny0,nx1,ny1,nx2,ny2) {
  40468. if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
  40469. int xleft = xleft0, xright = xright0;
  40470. float txleft = txl, txright = txr, tyleft = tyl, tyright = tyr;
  40471. tzfloat zleft = zl, zright = zr;
  40472. if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright);
  40473. const int dx = xright - xleft;
  40474. const float pentetx = (txright - txleft)/dx, pentety = (tyright - tyleft)/dx;
  40475. const tzfloat pentez = (zright - zleft)/dx;
  40476. if (xleft<0 && dx) {
  40477. zleft-=xleft*(zright - zleft)/dx;
  40478. txleft-=xleft*(txright - txleft)/dx;
  40479. tyleft-=xleft*(tyright - tyleft)/dx;
  40480. }
  40481. if (xleft<0) xleft = 0;
  40482. if (xright>=width() - 1) xright = width() - 1;
  40483. T *ptrd = data(xleft,y,0,0);
  40484. tz *ptrz = zbuffer.data(xleft,y);
  40485. if (opacity>=1) {
  40486. if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
  40487. if (zleft>=(tzfloat)*ptrz) {
  40488. *ptrz = (tz)zleft;
  40489. const tzfloat invz = 1/zleft;
  40490. const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
  40491. cimg_forC(*this,c) {
  40492. *ptrd = (T)*col;
  40493. ptrd+=whd; col+=twh;
  40494. }
  40495. ptrd-=offx;
  40496. }
  40497. zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
  40498. } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
  40499. if (zleft>=(tzfloat)*ptrz) {
  40500. *ptrz = (tz)zleft;
  40501. const tzfloat invz = 1/zleft;
  40502. const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
  40503. cimg_forC(*this,c) {
  40504. *ptrd = (T)(nbrightness**col);
  40505. ptrd+=whd; col+=twh;
  40506. }
  40507. ptrd-=offx;
  40508. }
  40509. zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
  40510. } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
  40511. if (zleft>=(tzfloat)*ptrz) {
  40512. *ptrz = (tz)zleft;
  40513. const tzfloat invz = 1/zleft;
  40514. const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
  40515. cimg_forC(*this,c) {
  40516. *ptrd = (T)((2 - nbrightness)**col + (nbrightness - 1)*maxval);
  40517. ptrd+=whd; col+=twh;
  40518. }
  40519. ptrd-=offx;
  40520. }
  40521. zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
  40522. }
  40523. } else {
  40524. if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
  40525. if (zleft>=(tzfloat)*ptrz) {
  40526. *ptrz = (tz)zleft;
  40527. const tzfloat invz = 1/zleft;
  40528. const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
  40529. cimg_forC(*this,c) {
  40530. *ptrd = (T)(nopacity**col + *ptrd*copacity);
  40531. ptrd+=whd; col+=twh;
  40532. }
  40533. ptrd-=offx;
  40534. }
  40535. zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
  40536. } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
  40537. if (zleft>=(tzfloat)*ptrz) {
  40538. *ptrz = (tz)zleft;
  40539. const tzfloat invz = 1/zleft;
  40540. const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
  40541. cimg_forC(*this,c) {
  40542. *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity);
  40543. ptrd+=whd; col+=twh;
  40544. }
  40545. ptrd-=offx;
  40546. }
  40547. zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
  40548. } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
  40549. if (zleft>=(tzfloat)*ptrz) {
  40550. *ptrz = (tz)zleft;
  40551. const tzfloat invz = 1/zleft;
  40552. const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
  40553. cimg_forC(*this,c) {
  40554. const T val = (T)((2 - nbrightness)**col + (nbrightness - 1)*maxval);
  40555. *ptrd = (T)(nopacity*val + *ptrd*copacity);
  40556. ptrd+=whd; col+=twh;
  40557. }
  40558. ptrd-=offx;
  40559. }
  40560. zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
  40561. }
  40562. }
  40563. zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
  40564. }
  40565. return *this;
  40566. }
  40567. //! Draw a Phong-shaded 2d triangle.
  40568. /**
  40569. \param x0 X-coordinate of the first vertex in the image instance.
  40570. \param y0 Y-coordinate of the first vertex in the image instance.
  40571. \param x1 X-coordinate of the second vertex in the image instance.
  40572. \param y1 Y-coordinate of the second vertex in the image instance.
  40573. \param x2 X-coordinate of the third vertex in the image instance.
  40574. \param y2 Y-coordinate of the third vertex in the image instance.
  40575. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  40576. \param light Light image.
  40577. \param lx0 X-coordinate of the first vertex in the light image.
  40578. \param ly0 Y-coordinate of the first vertex in the light image.
  40579. \param lx1 X-coordinate of the second vertex in the light image.
  40580. \param ly1 Y-coordinate of the second vertex in the light image.
  40581. \param lx2 X-coordinate of the third vertex in the light image.
  40582. \param ly2 Y-coordinate of the third vertex in the light image.
  40583. \param opacity Drawing opacity.
  40584. **/
  40585. template<typename tc, typename tl>
  40586. CImg<T>& draw_triangle(const int x0, const int y0,
  40587. const int x1, const int y1,
  40588. const int x2, const int y2,
  40589. const tc *const color,
  40590. const CImg<tl>& light,
  40591. const int lx0, const int ly0,
  40592. const int lx1, const int ly1,
  40593. const int lx2, const int ly2,
  40594. const float opacity=1) {
  40595. if (is_empty()) return *this;
  40596. if (!color)
  40597. throw CImgArgumentException(_cimg_instance
  40598. "draw_triangle(): Specified color is (null).",
  40599. cimg_instance);
  40600. if (light._depth>1 || light._spectrum<_spectrum)
  40601. throw CImgArgumentException(_cimg_instance
  40602. "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
  40603. cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
  40604. if (is_overlapped(light)) return draw_triangle(x0,y0,x1,y1,x2,y2,color,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
  40605. static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max());
  40606. const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
  40607. int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
  40608. nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
  40609. const ulongT
  40610. whd = (ulongT)_width*_height*_depth,
  40611. lwh = (ulongT)light._width*light._height,
  40612. offx = _spectrum*whd - 1;
  40613. if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nlx0,nlx1,nly0,nly1);
  40614. if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nlx0,nlx2,nly0,nly2);
  40615. if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nlx1,nlx2,nly1,nly2);
  40616. if (ny0>=height() || ny2<0) return *this;
  40617. _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y,
  40618. nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) {
  40619. int
  40620. xleft = xleft0, xright = xright0,
  40621. lxleft = lxleft0, lxright = lxright0,
  40622. lyleft = lyleft0, lyright = lyright0;
  40623. if (xright<xleft) cimg::swap(xleft,xright,lxleft,lxright,lyleft,lyright);
  40624. const int
  40625. dx = xright - xleft,
  40626. dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
  40627. dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
  40628. rlx = dx?(lxright - lxleft)/dx:0,
  40629. rly = dx?(lyright - lyleft)/dx:0,
  40630. slx = lxright>lxleft?1:-1,
  40631. sly = lyright>lyleft?1:-1,
  40632. ndlx = dlx - (dx?dx*(dlx/dx):0),
  40633. ndly = dly - (dx?dx*(dly/dx):0);
  40634. int errlx = dx>>1, errly = errlx;
  40635. if (xleft<0 && dx) {
  40636. lxleft-=xleft*(lxright - lxleft)/dx;
  40637. lyleft-=xleft*(lyright - lyleft)/dx;
  40638. }
  40639. if (xleft<0) xleft = 0;
  40640. if (xright>=width() - 1) xright = width() - 1;
  40641. T* ptrd = data(xleft,y,0,0);
  40642. if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
  40643. const tc *col = color;
  40644. const tl *lig = &light._atXY(lxleft,lyleft);
  40645. cimg_forC(*this,c) {
  40646. const tl l = *lig;
  40647. *ptrd = (T)(l<1?l**(col++):((2 - l)**(col++) + (l - 1)*maxval));
  40648. ptrd+=whd; lig+=lwh;
  40649. }
  40650. ptrd-=offx;
  40651. lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
  40652. lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
  40653. } else for (int x = xleft; x<=xright; ++x) {
  40654. const tc *col = color;
  40655. const tl *lig = &light._atXY(lxleft,lyleft);
  40656. cimg_forC(*this,c) {
  40657. const tl l = *lig;
  40658. const T val = (T)(l<1?l**(col++):((2 - l)**(col++) + (l - 1)*maxval));
  40659. *ptrd = (T)(nopacity*val + *ptrd*copacity);
  40660. ptrd+=whd; lig+=lwh;
  40661. }
  40662. ptrd-=offx;
  40663. lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
  40664. lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
  40665. }
  40666. }
  40667. return *this;
  40668. }
  40669. //! Draw a Phong-shaded 2d triangle, with z-buffering.
  40670. template<typename tz, typename tc, typename tl>
  40671. CImg<T>& draw_triangle(CImg<tz>& zbuffer,
  40672. const int x0, const int y0, const float z0,
  40673. const int x1, const int y1, const float z1,
  40674. const int x2, const int y2, const float z2,
  40675. const tc *const color,
  40676. const CImg<tl>& light,
  40677. const int lx0, const int ly0,
  40678. const int lx1, const int ly1,
  40679. const int lx2, const int ly2,
  40680. const float opacity=1) {
  40681. typedef typename cimg::superset<tz,float>::type tzfloat;
  40682. if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
  40683. if (!color)
  40684. throw CImgArgumentException(_cimg_instance
  40685. "draw_triangle(): Specified color is (null).",
  40686. cimg_instance);
  40687. if (light._depth>1 || light._spectrum<_spectrum)
  40688. throw CImgArgumentException(_cimg_instance
  40689. "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
  40690. cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
  40691. if (!is_sameXY(zbuffer))
  40692. throw CImgArgumentException(_cimg_instance
  40693. "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
  40694. "different dimensions.",
  40695. cimg_instance,
  40696. zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
  40697. if (is_overlapped(light)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,
  40698. +light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
  40699. static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max());
  40700. const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
  40701. const ulongT
  40702. whd = (ulongT)_width*_height*_depth,
  40703. lwh = (ulongT)light._width*light._height,
  40704. offx = _spectrum*whd;
  40705. int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
  40706. nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
  40707. tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
  40708. if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nlx0,nlx1,nly0,nly1,nz0,nz1);
  40709. if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nlx0,nlx2,nly0,nly2,nz0,nz2);
  40710. if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nlx1,nlx2,nly1,nly2,nz1,nz2);
  40711. if (ny0>=height() || ny2<0) return *this;
  40712. tzfloat
  40713. pzl = (nz1 - nz0)/(ny1 - ny0),
  40714. pzr = (nz2 - nz0)/(ny2 - ny0),
  40715. pzn = (nz2 - nz1)/(ny2 - ny1),
  40716. zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
  40717. zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
  40718. _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y,
  40719. nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) {
  40720. if (y==ny1) { zl = nz1; pzl = pzn; }
  40721. int
  40722. xleft = xleft0, xright = xright0,
  40723. lxleft = lxleft0, lxright = lxright0,
  40724. lyleft = lyleft0, lyright = lyright0;
  40725. tzfloat zleft = zl, zright = zr;
  40726. if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,lxleft,lxright,lyleft,lyright);
  40727. const int
  40728. dx = xright - xleft,
  40729. dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
  40730. dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
  40731. rlx = dx?(lxright - lxleft)/dx:0,
  40732. rly = dx?(lyright - lyleft)/dx:0,
  40733. slx = lxright>lxleft?1:-1,
  40734. sly = lyright>lyleft?1:-1,
  40735. ndlx = dlx - (dx?dx*(dlx/dx):0),
  40736. ndly = dly - (dx?dx*(dly/dx):0);
  40737. const tzfloat pentez = (zright - zleft)/dx;
  40738. int errlx = dx>>1, errly = errlx;
  40739. if (xleft<0 && dx) {
  40740. zleft-=xleft*(zright - zleft)/dx;
  40741. lxleft-=xleft*(lxright - lxleft)/dx;
  40742. lyleft-=xleft*(lyright - lyleft)/dx;
  40743. }
  40744. if (xleft<0) xleft = 0;
  40745. if (xright>=width() - 1) xright = width() - 1;
  40746. T *ptrd = data(xleft,y,0,0);
  40747. tz *ptrz = xleft<=xright?zbuffer.data(xleft,y):0;
  40748. if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
  40749. if (zleft>=(tzfloat)*ptrz) {
  40750. *ptrz = (tz)zleft;
  40751. const tc *col = color;
  40752. const tl *lig = &light._atXY(lxleft,lyleft);
  40753. cimg_forC(*this,c) {
  40754. const tl l = *lig;
  40755. const tc cval = *(col++);
  40756. *ptrd = (T)(l<1?l*cval:(2 - l)*cval + (l - 1)*maxval);
  40757. ptrd+=whd; lig+=lwh;
  40758. }
  40759. ptrd-=offx;
  40760. }
  40761. zleft+=pentez;
  40762. lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
  40763. lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
  40764. } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
  40765. if (zleft>=(tzfloat)*ptrz) {
  40766. *ptrz = (tz)zleft;
  40767. const tc *col = color;
  40768. const tl *lig = &light._atXY(lxleft,lyleft);
  40769. cimg_forC(*this,c) {
  40770. const tl l = *lig;
  40771. const tc cval = *(col++);
  40772. const T val = (T)(l<1?l*cval:(2 - l)*cval + (l - 1)*maxval);
  40773. *ptrd = (T)(nopacity*val + *ptrd*copacity);
  40774. ptrd+=whd; lig+=lwh;
  40775. }
  40776. ptrd-=offx;
  40777. }
  40778. zleft+=pentez;
  40779. lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
  40780. lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
  40781. }
  40782. zr+=pzr; zl+=pzl;
  40783. }
  40784. return *this;
  40785. }
  40786. //! Draw a textured Gouraud-shaded 2d triangle.
  40787. /**
  40788. \param x0 X-coordinate of the first vertex in the image instance.
  40789. \param y0 Y-coordinate of the first vertex in the image instance.
  40790. \param x1 X-coordinate of the second vertex in the image instance.
  40791. \param y1 Y-coordinate of the second vertex in the image instance.
  40792. \param x2 X-coordinate of the third vertex in the image instance.
  40793. \param y2 Y-coordinate of the third vertex in the image instance.
  40794. \param texture Texture image used to fill the triangle.
  40795. \param tx0 X-coordinate of the first vertex in the texture image.
  40796. \param ty0 Y-coordinate of the first vertex in the texture image.
  40797. \param tx1 X-coordinate of the second vertex in the texture image.
  40798. \param ty1 Y-coordinate of the second vertex in the texture image.
  40799. \param tx2 X-coordinate of the third vertex in the texture image.
  40800. \param ty2 Y-coordinate of the third vertex in the texture image.
  40801. \param brightness0 Brightness factor of the first vertex.
  40802. \param brightness1 Brightness factor of the second vertex.
  40803. \param brightness2 Brightness factor of the third vertex.
  40804. \param opacity Drawing opacity.
  40805. **/
  40806. template<typename tc>
  40807. CImg<T>& draw_triangle(const int x0, const int y0,
  40808. const int x1, const int y1,
  40809. const int x2, const int y2,
  40810. const CImg<tc>& texture,
  40811. const int tx0, const int ty0,
  40812. const int tx1, const int ty1,
  40813. const int tx2, const int ty2,
  40814. const float brightness0,
  40815. const float brightness1,
  40816. const float brightness2,
  40817. const float opacity=1) {
  40818. if (is_empty()) return *this;
  40819. if (texture._depth>1 || texture._spectrum<_spectrum)
  40820. throw CImgArgumentException(_cimg_instance
  40821. "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
  40822. cimg_instance,
  40823. texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
  40824. if (is_overlapped(texture))
  40825. return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
  40826. brightness0,brightness1,brightness2,opacity);
  40827. static const T maxval = (T)std::min(cimg::type<T>::max(),cimg::type<tc>::max());
  40828. const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
  40829. const ulongT
  40830. whd = (ulongT)_width*_height*_depth,
  40831. twh = (ulongT)texture._width*texture._height,
  40832. offx = _spectrum*whd - 1;
  40833. int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
  40834. ntx0 = tx0, nty0 = ty0, ntx1 = tx1, nty1 = ty1, ntx2 = tx2, nty2 = ty2,
  40835. nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
  40836. nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
  40837. nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
  40838. if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nc0,nc1);
  40839. if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nc0,nc2);
  40840. if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nc1,nc2);
  40841. if (ny0>=height() || ny2<0) return *this;
  40842. _cimg_for_triangle4(*this,xleft0,cleft0,txleft0,tyleft0,xright0,cright0,txright0,tyright0,y,
  40843. nx0,ny0,nc0,ntx0,nty0,nx1,ny1,nc1,ntx1,nty1,nx2,ny2,nc2,ntx2,nty2) {
  40844. int
  40845. xleft = xleft0, xright = xright0,
  40846. cleft = cleft0, cright = cright0,
  40847. txleft = txleft0, txright = txright0,
  40848. tyleft = tyleft0, tyright = tyright0;
  40849. if (xright<xleft) cimg::swap(xleft,xright,cleft,cright,txleft,txright,tyleft,tyright);
  40850. const int
  40851. dx = xright - xleft,
  40852. dc = cright>cleft?cright - cleft:cleft - cright,
  40853. dtx = txright>txleft?txright - txleft:txleft - txright,
  40854. dty = tyright>tyleft?tyright - tyleft:tyleft - tyright,
  40855. rc = dx?(cright - cleft)/dx:0,
  40856. rtx = dx?(txright - txleft)/dx:0,
  40857. rty = dx?(tyright - tyleft)/dx:0,
  40858. sc = cright>cleft?1:-1,
  40859. stx = txright>txleft?1:-1,
  40860. sty = tyright>tyleft?1:-1,
  40861. ndc = dc - (dx?dx*(dc/dx):0),
  40862. ndtx = dtx - (dx?dx*(dtx/dx):0),
  40863. ndty = dty - (dx?dx*(dty/dx):0);
  40864. int errc = dx>>1, errtx = errc, errty = errc;
  40865. if (xleft<0 && dx) {
  40866. cleft-=xleft*(cright - cleft)/dx;
  40867. txleft-=xleft*(txright - txleft)/dx;
  40868. tyleft-=xleft*(tyright - tyleft)/dx;
  40869. }
  40870. if (xleft<0) xleft = 0;
  40871. if (xright>=width() - 1) xright = width() - 1;
  40872. T* ptrd = data(xleft,y,0,0);
  40873. if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
  40874. const tc *col = &texture._atXY(txleft,tyleft);
  40875. cimg_forC(*this,c) {
  40876. *ptrd = (T)(cleft<256?cleft**col/256:((512 - cleft)**col + (cleft - 256)*maxval)/256);
  40877. ptrd+=whd; col+=twh;
  40878. }
  40879. ptrd-=offx;
  40880. cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
  40881. txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
  40882. tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
  40883. } else for (int x = xleft; x<=xright; ++x) {
  40884. const tc *col = &texture._atXY(txleft,tyleft);
  40885. cimg_forC(*this,c) {
  40886. const T val = (T)(cleft<256?cleft**col/256:((512 - cleft)**col + (cleft - 256)*maxval)/256);
  40887. *ptrd = (T)(nopacity*val + *ptrd*copacity);
  40888. ptrd+=whd; col+=twh;
  40889. }
  40890. ptrd-=offx;
  40891. cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
  40892. txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
  40893. tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
  40894. }
  40895. }
  40896. return *this;
  40897. }
  40898. //! Draw a textured Gouraud-shaded 2d triangle, with perspective correction \overloading.
  40899. template<typename tc>
  40900. CImg<T>& draw_triangle(const int x0, const int y0, const float z0,
  40901. const int x1, const int y1, const float z1,
  40902. const int x2, const int y2, const float z2,
  40903. const CImg<tc>& texture,
  40904. const int tx0, const int ty0,
  40905. const int tx1, const int ty1,
  40906. const int tx2, const int ty2,
  40907. const float brightness0,
  40908. const float brightness1,
  40909. const float brightness2,
  40910. const float opacity=1) {
  40911. if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
  40912. if (texture._depth>1 || texture._spectrum<_spectrum)
  40913. throw CImgArgumentException(_cimg_instance
  40914. "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
  40915. cimg_instance,
  40916. texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
  40917. if (is_overlapped(texture)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
  40918. brightness0,brightness1,brightness2,opacity);
  40919. static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max());
  40920. const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
  40921. const ulongT
  40922. whd = (ulongT)_width*_height*_depth,
  40923. twh = (ulongT)texture._width*texture._height,
  40924. offx = _spectrum*whd - 1;
  40925. int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
  40926. nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
  40927. nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
  40928. nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
  40929. float
  40930. ntx0 = tx0/z0, nty0 = ty0/z0,
  40931. ntx1 = tx1/z1, nty1 = ty1/z1,
  40932. ntx2 = tx2/z2, nty2 = ty2/z2,
  40933. nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2;
  40934. if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1,nc0,nc1);
  40935. if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2,nc0,nc2);
  40936. if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2,nc1,nc2);
  40937. if (ny0>=height() || ny2<0) return *this;
  40938. float
  40939. ptxl = (ntx1 - ntx0)/(ny1 - ny0),
  40940. ptxr = (ntx2 - ntx0)/(ny2 - ny0),
  40941. ptxn = (ntx2 - ntx1)/(ny2 - ny1),
  40942. ptyl = (nty1 - nty0)/(ny1 - ny0),
  40943. ptyr = (nty2 - nty0)/(ny2 - ny0),
  40944. ptyn = (nty2 - nty1)/(ny2 - ny1),
  40945. pzl = (nz1 - nz0)/(ny1 - ny0),
  40946. pzr = (nz2 - nz0)/(ny2 - ny0),
  40947. pzn = (nz2 - nz1)/(ny2 - ny1),
  40948. zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
  40949. txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
  40950. tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
  40951. zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))),
  40952. txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):
  40953. (ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
  40954. tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):
  40955. (ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
  40956. _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) {
  40957. if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
  40958. int
  40959. xleft = xleft0, xright = xright0,
  40960. cleft = cleft0, cright = cright0;
  40961. float
  40962. zleft = zl, zright = zr,
  40963. txleft = txl, txright = txr,
  40964. tyleft = tyl, tyright = tyr;
  40965. if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,cleft,cright);
  40966. const int
  40967. dx = xright - xleft,
  40968. dc = cright>cleft?cright - cleft:cleft - cright,
  40969. rc = dx?(cright - cleft)/dx:0,
  40970. sc = cright>cleft?1:-1,
  40971. ndc = dc - (dx?dx*(dc/dx):0);
  40972. const float
  40973. pentez = (zright - zleft)/dx,
  40974. pentetx = (txright - txleft)/dx,
  40975. pentety = (tyright - tyleft)/dx;
  40976. int errc = dx>>1;
  40977. if (xleft<0 && dx) {
  40978. cleft-=xleft*(cright - cleft)/dx;
  40979. zleft-=xleft*(zright - zleft)/dx;
  40980. txleft-=xleft*(txright - txleft)/dx;
  40981. tyleft-=xleft*(tyright - tyleft)/dx;
  40982. }
  40983. if (xleft<0) xleft = 0;
  40984. if (xright>=width() - 1) xright = width() - 1;
  40985. T* ptrd = data(xleft,y,0,0);
  40986. if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
  40987. const float invz = 1/zleft;
  40988. const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
  40989. cimg_forC(*this,c) {
  40990. *ptrd = (T)(cleft<256?cleft**col/256:((512 - cleft)**col + (cleft - 256)*maxval)/256);
  40991. ptrd+=whd; col+=twh;
  40992. }
  40993. ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
  40994. cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
  40995. } else for (int x = xleft; x<=xright; ++x) {
  40996. const float invz = 1/zleft;
  40997. const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
  40998. cimg_forC(*this,c) {
  40999. const T val = (T)(cleft<256?cleft**col/256:((512 - cleft)**col + (cleft - 256)*maxval)/256);
  41000. *ptrd = (T)(nopacity*val + *ptrd*copacity);
  41001. ptrd+=whd; col+=twh;
  41002. }
  41003. ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
  41004. cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
  41005. }
  41006. zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
  41007. }
  41008. return *this;
  41009. }
  41010. //! Draw a textured Gouraud-shaded 2d triangle, with perspective correction and z-buffering \overloading.
  41011. template<typename tz, typename tc>
  41012. CImg<T>& draw_triangle(CImg<tz>& zbuffer,
  41013. const int x0, const int y0, const float z0,
  41014. const int x1, const int y1, const float z1,
  41015. const int x2, const int y2, const float z2,
  41016. const CImg<tc>& texture,
  41017. const int tx0, const int ty0,
  41018. const int tx1, const int ty1,
  41019. const int tx2, const int ty2,
  41020. const float brightness0,
  41021. const float brightness1,
  41022. const float brightness2,
  41023. const float opacity=1) {
  41024. typedef typename cimg::superset<tz,float>::type tzfloat;
  41025. if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
  41026. if (!is_sameXY(zbuffer))
  41027. throw CImgArgumentException(_cimg_instance
  41028. "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
  41029. "different dimensions.",
  41030. cimg_instance,
  41031. zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
  41032. if (texture._depth>1 || texture._spectrum<_spectrum)
  41033. throw CImgArgumentException(_cimg_instance
  41034. "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
  41035. cimg_instance,
  41036. texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
  41037. if (is_overlapped(texture))
  41038. return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
  41039. brightness0,brightness1,brightness2,opacity);
  41040. static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max());
  41041. const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
  41042. const ulongT
  41043. whd = (ulongT)_width*_height*_depth,
  41044. twh = (ulongT)texture._width*texture._height,
  41045. offx = _spectrum*whd;
  41046. int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
  41047. nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
  41048. nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
  41049. nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
  41050. float
  41051. ntx0 = tx0/z0, nty0 = ty0/z0,
  41052. ntx1 = tx1/z1, nty1 = ty1/z1,
  41053. ntx2 = tx2/z2, nty2 = ty2/z2;
  41054. tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
  41055. if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1,nc0,nc1);
  41056. if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2,nc0,nc2);
  41057. if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2,nc1,nc2);
  41058. if (ny0>=height() || ny2<0) return *this;
  41059. float
  41060. ptxl = (ntx1 - ntx0)/(ny1 - ny0),
  41061. ptxr = (ntx2 - ntx0)/(ny2 - ny0),
  41062. ptxn = (ntx2 - ntx1)/(ny2 - ny1),
  41063. ptyl = (nty1 - nty0)/(ny1 - ny0),
  41064. ptyr = (nty2 - nty0)/(ny2 - ny0),
  41065. ptyn = (nty2 - nty1)/(ny2 - ny1),
  41066. txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
  41067. tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
  41068. txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):
  41069. (ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
  41070. tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):
  41071. (ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
  41072. tzfloat
  41073. pzl = (nz1 - nz0)/(ny1 - ny0),
  41074. pzr = (nz2 - nz0)/(ny2 - ny0),
  41075. pzn = (nz2 - nz1)/(ny2 - ny1),
  41076. zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
  41077. zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
  41078. _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) {
  41079. if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
  41080. int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0;
  41081. float txleft = txl, txright = txr, tyleft = tyl, tyright = tyr;
  41082. tzfloat zleft = zl, zright = zr;
  41083. if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,cleft,cright);
  41084. const int
  41085. dx = xright - xleft,
  41086. dc = cright>cleft?cright - cleft:cleft - cright,
  41087. rc = dx?(cright - cleft)/dx:0,
  41088. sc = cright>cleft?1:-1,
  41089. ndc = dc - (dx?dx*(dc/dx):0);
  41090. float pentetx = (txright - txleft)/dx, pentety = (tyright - tyleft)/dx;
  41091. const tzfloat pentez = (zright - zleft)/dx;
  41092. int errc = dx>>1;
  41093. if (xleft<0 && dx) {
  41094. cleft-=xleft*(cright - cleft)/dx;
  41095. zleft-=xleft*(zright - zleft)/dx;
  41096. txleft-=xleft*(txright - txleft)/dx;
  41097. tyleft-=xleft*(tyright - tyleft)/dx;
  41098. }
  41099. if (xleft<0) xleft = 0;
  41100. if (xright>=width() - 1) xright = width() - 1;
  41101. T* ptrd = data(xleft,y);
  41102. tz *ptrz = zbuffer.data(xleft,y);
  41103. if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) {
  41104. if (zleft>=(tzfloat)*ptrz) {
  41105. *ptrz = (tz)zleft;
  41106. const tzfloat invz = 1/zleft;
  41107. const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
  41108. cimg_forC(*this,c) {
  41109. *ptrd = (T)(cleft<256?cleft**col/256:((512 - cleft)**col + (cleft - 256)*maxval)/256);
  41110. ptrd+=whd; col+=twh;
  41111. }
  41112. ptrd-=offx;
  41113. }
  41114. zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
  41115. cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
  41116. } else for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) {
  41117. if (zleft>=(tzfloat)*ptrz) {
  41118. *ptrz = (tz)zleft;
  41119. const tzfloat invz = 1/zleft;
  41120. const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
  41121. cimg_forC(*this,c) {
  41122. const T val = (T)(cleft<256?cleft**col/256:((512 - cleft)**col + (cleft - 256)*maxval)/256);
  41123. *ptrd = (T)(nopacity*val + *ptrd*copacity);
  41124. ptrd+=whd; col+=twh;
  41125. }
  41126. ptrd-=offx;
  41127. }
  41128. zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
  41129. cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
  41130. }
  41131. zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
  41132. }
  41133. return *this;
  41134. }
  41135. //! Draw a textured Phong-shaded 2d triangle.
  41136. /**
  41137. \param x0 X-coordinate of the first vertex in the image instance.
  41138. \param y0 Y-coordinate of the first vertex in the image instance.
  41139. \param x1 X-coordinate of the second vertex in the image instance.
  41140. \param y1 Y-coordinate of the second vertex in the image instance.
  41141. \param x2 X-coordinate of the third vertex in the image instance.
  41142. \param y2 Y-coordinate of the third vertex in the image instance.
  41143. \param texture Texture image used to fill the triangle.
  41144. \param tx0 X-coordinate of the first vertex in the texture image.
  41145. \param ty0 Y-coordinate of the first vertex in the texture image.
  41146. \param tx1 X-coordinate of the second vertex in the texture image.
  41147. \param ty1 Y-coordinate of the second vertex in the texture image.
  41148. \param tx2 X-coordinate of the third vertex in the texture image.
  41149. \param ty2 Y-coordinate of the third vertex in the texture image.
  41150. \param light Light image.
  41151. \param lx0 X-coordinate of the first vertex in the light image.
  41152. \param ly0 Y-coordinate of the first vertex in the light image.
  41153. \param lx1 X-coordinate of the second vertex in the light image.
  41154. \param ly1 Y-coordinate of the second vertex in the light image.
  41155. \param lx2 X-coordinate of the third vertex in the light image.
  41156. \param ly2 Y-coordinate of the third vertex in the light image.
  41157. \param opacity Drawing opacity.
  41158. **/
  41159. template<typename tc, typename tl>
  41160. CImg<T>& draw_triangle(const int x0, const int y0,
  41161. const int x1, const int y1,
  41162. const int x2, const int y2,
  41163. const CImg<tc>& texture,
  41164. const int tx0, const int ty0,
  41165. const int tx1, const int ty1,
  41166. const int tx2, const int ty2,
  41167. const CImg<tl>& light,
  41168. const int lx0, const int ly0,
  41169. const int lx1, const int ly1,
  41170. const int lx2, const int ly2,
  41171. const float opacity=1) {
  41172. if (is_empty()) return *this;
  41173. if (texture._depth>1 || texture._spectrum<_spectrum)
  41174. throw CImgArgumentException(_cimg_instance
  41175. "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
  41176. cimg_instance,
  41177. texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
  41178. if (light._depth>1 || light._spectrum<_spectrum)
  41179. throw CImgArgumentException(_cimg_instance
  41180. "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
  41181. cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
  41182. if (is_overlapped(texture))
  41183. return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
  41184. if (is_overlapped(light))
  41185. return draw_triangle(x0,y0,x1,y1,x2,y2,texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
  41186. static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max());
  41187. const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
  41188. const ulongT
  41189. whd = (ulongT)_width*_height*_depth,
  41190. twh = (ulongT)texture._width*texture._height,
  41191. lwh = (ulongT)light._width*light._height,
  41192. offx = _spectrum*whd - 1;
  41193. int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
  41194. ntx0 = tx0, nty0 = ty0, ntx1 = tx1, nty1 = ty1, ntx2 = tx2, nty2 = ty2,
  41195. nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
  41196. if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nlx0,nlx1,nly0,nly1);
  41197. if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nlx0,nlx2,nly0,nly2);
  41198. if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nlx1,nlx2,nly1,nly2);
  41199. if (ny0>=height() || ny2<0) return *this;
  41200. const bool is_bump = texture._spectrum>=_spectrum + 2;
  41201. const ulongT obx = twh*_spectrum, oby = twh*(_spectrum + 1);
  41202. _cimg_for_triangle5(*this,xleft0,lxleft0,lyleft0,txleft0,tyleft0,xright0,lxright0,lyright0,txright0,tyright0,y,
  41203. nx0,ny0,nlx0,nly0,ntx0,nty0,nx1,ny1,nlx1,nly1,ntx1,nty1,nx2,ny2,nlx2,nly2,ntx2,nty2) {
  41204. int
  41205. xleft = xleft0, xright = xright0,
  41206. lxleft = lxleft0, lxright = lxright0,
  41207. lyleft = lyleft0, lyright = lyright0,
  41208. txleft = txleft0, txright = txright0,
  41209. tyleft = tyleft0, tyright = tyright0;
  41210. if (xright<xleft) cimg::swap(xleft,xright,lxleft,lxright,lyleft,lyright,txleft,txright,tyleft,tyright);
  41211. const int
  41212. dx = xright - xleft,
  41213. dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
  41214. dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
  41215. dtx = txright>txleft?txright - txleft:txleft - txright,
  41216. dty = tyright>tyleft?tyright - tyleft:tyleft - tyright,
  41217. rlx = dx?(lxright - lxleft)/dx:0,
  41218. rly = dx?(lyright - lyleft)/dx:0,
  41219. rtx = dx?(txright - txleft)/dx:0,
  41220. rty = dx?(tyright - tyleft)/dx:0,
  41221. slx = lxright>lxleft?1:-1,
  41222. sly = lyright>lyleft?1:-1,
  41223. stx = txright>txleft?1:-1,
  41224. sty = tyright>tyleft?1:-1,
  41225. ndlx = dlx - (dx?dx*(dlx/dx):0),
  41226. ndly = dly - (dx?dx*(dly/dx):0),
  41227. ndtx = dtx - (dx?dx*(dtx/dx):0),
  41228. ndty = dty - (dx?dx*(dty/dx):0);
  41229. int errlx = dx>>1, errly = errlx, errtx = errlx, errty = errlx;
  41230. if (xleft<0 && dx) {
  41231. lxleft-=xleft*(lxright - lxleft)/dx;
  41232. lyleft-=xleft*(lyright - lyleft)/dx;
  41233. txleft-=xleft*(txright - txleft)/dx;
  41234. tyleft-=xleft*(tyright - tyleft)/dx;
  41235. }
  41236. if (xleft<0) xleft = 0;
  41237. if (xright>=width() - 1) xright = width() - 1;
  41238. T* ptrd = data(xleft,y,0,0);
  41239. if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
  41240. const tc *col = &texture._atXY(txleft,tyleft);
  41241. const int bx = is_bump?128 - (int)col[obx]:0, by = is_bump?128 - (int)col[oby]:0;
  41242. const tl *lig = &light._atXY(lxleft + bx,lyleft + by);
  41243. cimg_forC(*this,c) {
  41244. const tl l = *lig;
  41245. *ptrd = (T)(l<1?l**col:(2 - l)**col + (l - 1)*maxval);
  41246. ptrd+=whd; col+=twh; lig+=lwh;
  41247. }
  41248. ptrd-=offx;
  41249. lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
  41250. lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
  41251. txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
  41252. tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
  41253. } else for (int x = xleft; x<=xright; ++x) {
  41254. const tc *col = &texture._atXY(txleft,tyleft);
  41255. const int bx = is_bump?128 - (int)col[obx]:0, by = is_bump?128 - (int)col[oby]:0;
  41256. const tl *lig = &light._atXY(lxleft + bx,lyleft + by);
  41257. cimg_forC(*this,c) {
  41258. const tl l = *lig;
  41259. const T val = (T)(l<1?l**col:(2 - l)**col + (l - 1)*maxval);
  41260. *ptrd = (T)(nopacity*val + *ptrd*copacity);
  41261. ptrd+=whd; col+=twh; lig+=lwh;
  41262. }
  41263. ptrd-=offx;
  41264. lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
  41265. lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
  41266. txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
  41267. tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
  41268. }
  41269. }
  41270. return *this;
  41271. }
  41272. //! Draw a textured Phong-shaded 2d triangle, with perspective correction.
  41273. template<typename tc, typename tl>
  41274. CImg<T>& draw_triangle(const int x0, const int y0, const float z0,
  41275. const int x1, const int y1, const float z1,
  41276. const int x2, const int y2, const float z2,
  41277. const CImg<tc>& texture,
  41278. const int tx0, const int ty0,
  41279. const int tx1, const int ty1,
  41280. const int tx2, const int ty2,
  41281. const CImg<tl>& light,
  41282. const int lx0, const int ly0,
  41283. const int lx1, const int ly1,
  41284. const int lx2, const int ly2,
  41285. const float opacity=1) {
  41286. if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
  41287. if (texture._depth>1 || texture._spectrum<_spectrum)
  41288. throw CImgArgumentException(_cimg_instance
  41289. "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
  41290. cimg_instance,
  41291. texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
  41292. if (light._depth>1 || light._spectrum<_spectrum)
  41293. throw CImgArgumentException(_cimg_instance
  41294. "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
  41295. cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
  41296. if (is_overlapped(texture))
  41297. return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
  41298. light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
  41299. if (is_overlapped(light))
  41300. return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,texture,tx0,ty0,tx1,ty1,tx2,ty2,
  41301. +light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
  41302. static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max());
  41303. const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
  41304. const ulongT
  41305. whd = (ulongT)_width*_height*_depth,
  41306. twh = (ulongT)texture._width*texture._height,
  41307. lwh = (ulongT)light._width*light._height,
  41308. offx = _spectrum*whd - 1;
  41309. int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
  41310. nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
  41311. float
  41312. ntx0 = tx0/z0, nty0 = ty0/z0,
  41313. ntx1 = tx1/z1, nty1 = ty1/z1,
  41314. ntx2 = tx2/z2, nty2 = ty2/z2,
  41315. nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2;
  41316. if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nlx0,nlx1,nly0,nly1,nz0,nz1);
  41317. if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nlx0,nlx2,nly0,nly2,nz0,nz2);
  41318. if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nlx1,nlx2,nly1,nly2,nz1,nz2);
  41319. if (ny0>=height() || ny2<0) return *this;
  41320. float
  41321. ptxl = (ntx1 - ntx0)/(ny1 - ny0),
  41322. ptxr = (ntx2 - ntx0)/(ny2 - ny0),
  41323. ptxn = (ntx2 - ntx1)/(ny2 - ny1),
  41324. ptyl = (nty1 - nty0)/(ny1 - ny0),
  41325. ptyr = (nty2 - nty0)/(ny2 - ny0),
  41326. ptyn = (nty2 - nty1)/(ny2 - ny1),
  41327. pzl = (nz1 - nz0)/(ny1 - ny0),
  41328. pzr = (nz2 - nz0)/(ny2 - ny0),
  41329. pzn = (nz2 - nz1)/(ny2 - ny1),
  41330. zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
  41331. txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
  41332. tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
  41333. zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))),
  41334. txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):
  41335. (ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
  41336. tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):
  41337. (ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
  41338. const bool is_bump = texture._spectrum>=_spectrum + 2;
  41339. const ulongT obx = twh*_spectrum, oby = twh*(_spectrum + 1);
  41340. _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y,
  41341. nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) {
  41342. if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
  41343. int
  41344. xleft = xleft0, xright = xright0,
  41345. lxleft = lxleft0, lxright = lxright0,
  41346. lyleft = lyleft0, lyright = lyright0;
  41347. float
  41348. zleft = zl, zright = zr,
  41349. txleft = txl, txright = txr,
  41350. tyleft = tyl, tyright = tyr;
  41351. if (xright<xleft)
  41352. cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,lxleft,lxright,lyleft,lyright);
  41353. const int
  41354. dx = xright - xleft,
  41355. dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
  41356. dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
  41357. rlx = dx?(lxright - lxleft)/dx:0,
  41358. rly = dx?(lyright - lyleft)/dx:0,
  41359. slx = lxright>lxleft?1:-1,
  41360. sly = lyright>lyleft?1:-1,
  41361. ndlx = dlx - (dx?dx*(dlx/dx):0),
  41362. ndly = dly - (dx?dx*(dly/dx):0);
  41363. const float
  41364. pentez = (zright - zleft)/dx,
  41365. pentetx = (txright - txleft)/dx,
  41366. pentety = (tyright - tyleft)/dx;
  41367. int errlx = dx>>1, errly = errlx;
  41368. if (xleft<0 && dx) {
  41369. zleft-=xleft*(zright - zleft)/dx;
  41370. lxleft-=xleft*(lxright - lxleft)/dx;
  41371. lyleft-=xleft*(lyright - lyleft)/dx;
  41372. txleft-=xleft*(txright - txleft)/dx;
  41373. tyleft-=xleft*(tyright - tyleft)/dx;
  41374. }
  41375. if (xleft<0) xleft = 0;
  41376. if (xright>=width() - 1) xright = width() - 1;
  41377. T* ptrd = data(xleft,y,0,0);
  41378. if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
  41379. const float invz = 1/zleft;
  41380. const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
  41381. const int bx = is_bump?128 - (int)col[obx]:0, by = is_bump?128 - (int)col[oby]:0;
  41382. const tl *lig = &light._atXY(lxleft + bx,lyleft + by);
  41383. cimg_forC(*this,c) {
  41384. const tl l = *lig;
  41385. *ptrd = (T)(l<1?l**col:(2 - l)**col + (l - 1)*maxval);
  41386. ptrd+=whd; col+=twh; lig+=lwh;
  41387. }
  41388. ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
  41389. lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
  41390. lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
  41391. } else for (int x = xleft; x<=xright; ++x) {
  41392. const float invz = 1/zleft;
  41393. const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
  41394. const int bx = is_bump?128 - (int)col[obx]:0, by = is_bump?128 - (int)col[oby]:0;
  41395. const tl *lig = &light._atXY(lxleft + bx,lyleft + by);
  41396. cimg_forC(*this,c) {
  41397. const tl l = *lig;
  41398. const T val = (T)(l<1?l**col:(2 - l)**col + (l - 1)*maxval);
  41399. *ptrd = (T)(nopacity*val + *ptrd*copacity);
  41400. ptrd+=whd; col+=twh; lig+=lwh;
  41401. }
  41402. ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
  41403. lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
  41404. lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
  41405. }
  41406. zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
  41407. }
  41408. return *this;
  41409. }
  41410. //! Draw a textured Phong-shaded 2d triangle, with perspective correction and z-buffering.
  41411. template<typename tz, typename tc, typename tl>
  41412. CImg<T>& draw_triangle(CImg<tz>& zbuffer,
  41413. const int x0, const int y0, const float z0,
  41414. const int x1, const int y1, const float z1,
  41415. const int x2, const int y2, const float z2,
  41416. const CImg<tc>& texture,
  41417. const int tx0, const int ty0,
  41418. const int tx1, const int ty1,
  41419. const int tx2, const int ty2,
  41420. const CImg<tl>& light,
  41421. const int lx0, const int ly0,
  41422. const int lx1, const int ly1,
  41423. const int lx2, const int ly2,
  41424. const float opacity=1) {
  41425. typedef typename cimg::superset<tz,float>::type tzfloat;
  41426. if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
  41427. if (!is_sameXY(zbuffer))
  41428. throw CImgArgumentException(_cimg_instance
  41429. "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
  41430. "different dimensions.",
  41431. cimg_instance,
  41432. zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
  41433. if (texture._depth>1 || texture._spectrum<_spectrum)
  41434. throw CImgArgumentException(_cimg_instance
  41435. "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
  41436. cimg_instance,
  41437. texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
  41438. if (light._depth>1 || light._spectrum<_spectrum)
  41439. throw CImgArgumentException(_cimg_instance
  41440. "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
  41441. cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
  41442. if (is_overlapped(texture))
  41443. return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,
  41444. +texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
  41445. if (is_overlapped(light))
  41446. return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,
  41447. texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
  41448. static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max());
  41449. const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
  41450. const ulongT
  41451. whd = (ulongT)_width*_height*_depth,
  41452. twh = (ulongT)texture._width*texture._height,
  41453. lwh = (ulongT)light._width*light._height,
  41454. offx = _spectrum*whd;
  41455. int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
  41456. nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
  41457. float
  41458. ntx0 = tx0/z0, nty0 = ty0/z0,
  41459. ntx1 = tx1/z1, nty1 = ty1/z1,
  41460. ntx2 = tx2/z2, nty2 = ty2/z2;
  41461. tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
  41462. if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nlx0,nlx1,nly0,nly1,nz0,nz1);
  41463. if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nlx0,nlx2,nly0,nly2,nz0,nz2);
  41464. if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nlx1,nlx2,nly1,nly2,nz1,nz2);
  41465. if (ny0>=height() || ny2<0) return *this;
  41466. float
  41467. ptxl = (ntx1 - ntx0)/(ny1 - ny0),
  41468. ptxr = (ntx2 - ntx0)/(ny2 - ny0),
  41469. ptxn = (ntx2 - ntx1)/(ny2 - ny1),
  41470. ptyl = (nty1 - nty0)/(ny1 - ny0),
  41471. ptyr = (nty2 - nty0)/(ny2 - ny0),
  41472. ptyn = (nty2 - nty1)/(ny2 - ny1),
  41473. txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
  41474. tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
  41475. txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):
  41476. (ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
  41477. tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):
  41478. (ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
  41479. tzfloat
  41480. pzl = (nz1 - nz0)/(ny1 - ny0),
  41481. pzr = (nz2 - nz0)/(ny2 - ny0),
  41482. pzn = (nz2 - nz1)/(ny2 - ny1),
  41483. zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
  41484. zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
  41485. const bool is_bump = texture._spectrum>=_spectrum + 2;
  41486. const ulongT obx = twh*_spectrum, oby = twh*(_spectrum + 1);
  41487. _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y,
  41488. nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) {
  41489. if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
  41490. int
  41491. xleft = xleft0, xright = xright0,
  41492. lxleft = lxleft0, lxright = lxright0,
  41493. lyleft = lyleft0, lyright = lyright0;
  41494. float txleft = txl, txright = txr, tyleft = tyl, tyright = tyr;
  41495. tzfloat zleft = zl, zright = zr;
  41496. if (xright<xleft)
  41497. cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,lxleft,lxright,lyleft,lyright);
  41498. const int
  41499. dx = xright - xleft,
  41500. dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
  41501. dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
  41502. rlx = dx?(lxright - lxleft)/dx:0,
  41503. rly = dx?(lyright - lyleft)/dx:0,
  41504. slx = lxright>lxleft?1:-1,
  41505. sly = lyright>lyleft?1:-1,
  41506. ndlx = dlx - (dx?dx*(dlx/dx):0),
  41507. ndly = dly - (dx?dx*(dly/dx):0);
  41508. float pentetx = (txright - txleft)/dx, pentety = (tyright - tyleft)/dx;
  41509. const tzfloat pentez = (zright - zleft)/dx;
  41510. int errlx = dx>>1, errly = errlx;
  41511. if (xleft<0 && dx) {
  41512. zleft-=xleft*(zright - zleft)/dx;
  41513. lxleft-=xleft*(lxright - lxleft)/dx;
  41514. lyleft-=xleft*(lyright - lyleft)/dx;
  41515. txleft-=xleft*(txright - txleft)/dx;
  41516. tyleft-=xleft*(tyright - tyleft)/dx;
  41517. }
  41518. if (xleft<0) xleft = 0;
  41519. if (xright>=width() - 1) xright = width() - 1;
  41520. T* ptrd = data(xleft,y);
  41521. tz *ptrz = zbuffer.data(xleft,y);
  41522. if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
  41523. if (zleft>=(tzfloat)*ptrz) {
  41524. *ptrz = (tz)zleft;
  41525. const tzfloat invz = 1/zleft;
  41526. const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
  41527. const int bx = is_bump?128 - (int)col[obx]:0, by = is_bump?128 - (int)col[oby]:0;
  41528. const tl *lig = &light._atXY(lxleft + bx,lyleft + by);
  41529. cimg_forC(*this,c) {
  41530. const tl l = *lig;
  41531. *ptrd = (T)(l<1?l**col:(2 - l)**col + (l - 1)*maxval);
  41532. ptrd+=whd; col+=twh; lig+=lwh;
  41533. }
  41534. ptrd-=offx;
  41535. }
  41536. zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
  41537. lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
  41538. lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
  41539. } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
  41540. if (zleft>=(tzfloat)*ptrz) {
  41541. *ptrz = (tz)zleft;
  41542. const tzfloat invz = 1/zleft;
  41543. const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
  41544. const int bx = is_bump?128 - (int)col[obx]:0, by = is_bump?128 - (int)col[oby]:0;
  41545. const tl *lig = &light._atXY(lxleft + bx,lyleft + by);
  41546. cimg_forC(*this,c) {
  41547. const tl l = *lig;
  41548. const T val = (T)(l<1?l**col:(2 - l)**col + (l - 1)*maxval);
  41549. *ptrd = (T)(nopacity*val + *ptrd*copacity);
  41550. ptrd+=whd; col+=twh; lig+=lwh;
  41551. }
  41552. ptrd-=offx;
  41553. }
  41554. zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
  41555. lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
  41556. lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
  41557. }
  41558. zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
  41559. }
  41560. return *this;
  41561. }
  41562. //! Draw a filled 4d rectangle.
  41563. /**
  41564. \param x0 X-coordinate of the upper-left rectangle corner.
  41565. \param y0 Y-coordinate of the upper-left rectangle corner.
  41566. \param z0 Z-coordinate of the upper-left rectangle corner.
  41567. \param c0 C-coordinate of the upper-left rectangle corner.
  41568. \param x1 X-coordinate of the lower-right rectangle corner.
  41569. \param y1 Y-coordinate of the lower-right rectangle corner.
  41570. \param z1 Z-coordinate of the lower-right rectangle corner.
  41571. \param c1 C-coordinate of the lower-right rectangle corner.
  41572. \param val Scalar value used to fill the rectangle area.
  41573. \param opacity Drawing opacity.
  41574. **/
  41575. CImg<T>& draw_rectangle(const int x0, const int y0, const int z0, const int c0,
  41576. const int x1, const int y1, const int z1, const int c1,
  41577. const T val, const float opacity=1) {
  41578. if (is_empty()) return *this;
  41579. const int
  41580. nx0 = x0<x1?x0:x1, nx1 = x0^x1^nx0,
  41581. ny0 = y0<y1?y0:y1, ny1 = y0^y1^ny0,
  41582. nz0 = z0<z1?z0:z1, nz1 = z0^z1^nz0,
  41583. nc0 = c0<c1?c0:c1, nc1 = c0^c1^nc0;
  41584. const int
  41585. lX = (1 + nx1 - nx0) + (nx1>=width()?width() - 1 - nx1:0) + (nx0<0?nx0:0),
  41586. lY = (1 + ny1 - ny0) + (ny1>=height()?height() - 1 - ny1:0) + (ny0<0?ny0:0),
  41587. lZ = (1 + nz1 - nz0) + (nz1>=depth()?depth() - 1 - nz1:0) + (nz0<0?nz0:0),
  41588. lC = (1 + nc1 - nc0) + (nc1>=spectrum()?spectrum() - 1 - nc1:0) + (nc0<0?nc0:0);
  41589. const ulongT
  41590. offX = (ulongT)_width - lX,
  41591. offY = (ulongT)_width*(_height - lY),
  41592. offZ = (ulongT)_width*_height*(_depth - lZ);
  41593. const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
  41594. T *ptrd = data(nx0<0?0:nx0,ny0<0?0:ny0,nz0<0?0:nz0,nc0<0?0:nc0);
  41595. if (lX>0 && lY>0 && lZ>0 && lC>0)
  41596. for (int v = 0; v<lC; ++v) {
  41597. for (int z = 0; z<lZ; ++z) {
  41598. for (int y = 0; y<lY; ++y) {
  41599. if (opacity>=1) {
  41600. if (sizeof(T)!=1) { for (int x = 0; x<lX; ++x) *(ptrd++) = val; ptrd+=offX; }
  41601. else { std::memset(ptrd,(int)val,lX); ptrd+=_width; }
  41602. } else { for (int x = 0; x<lX; ++x) { *ptrd = (T)(nopacity*val + *ptrd*copacity); ++ptrd; } ptrd+=offX; }
  41603. }
  41604. ptrd+=offY;
  41605. }
  41606. ptrd+=offZ;
  41607. }
  41608. return *this;
  41609. }
  41610. //! Draw a filled 3d rectangle.
  41611. /**
  41612. \param x0 X-coordinate of the upper-left rectangle corner.
  41613. \param y0 Y-coordinate of the upper-left rectangle corner.
  41614. \param z0 Z-coordinate of the upper-left rectangle corner.
  41615. \param x1 X-coordinate of the lower-right rectangle corner.
  41616. \param y1 Y-coordinate of the lower-right rectangle corner.
  41617. \param z1 Z-coordinate of the lower-right rectangle corner.
  41618. \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
  41619. \param opacity Drawing opacity.
  41620. **/
  41621. template<typename tc>
  41622. CImg<T>& draw_rectangle(const int x0, const int y0, const int z0,
  41623. const int x1, const int y1, const int z1,
  41624. const tc *const color, const float opacity=1) {
  41625. if (is_empty()) return *this;
  41626. if (!color)
  41627. throw CImgArgumentException(_cimg_instance
  41628. "draw_rectangle(): Specified color is (null).",
  41629. cimg_instance);
  41630. cimg_forC(*this,c) draw_rectangle(x0,y0,z0,c,x1,y1,z1,c,(T)color[c],opacity);
  41631. return *this;
  41632. }
  41633. //! Draw an outlined 3d rectangle \overloading.
  41634. template<typename tc>
  41635. CImg<T>& draw_rectangle(const int x0, const int y0, const int z0,
  41636. const int x1, const int y1, const int z1,
  41637. const tc *const color, const float opacity,
  41638. const unsigned int pattern) {
  41639. return draw_line(x0,y0,z0,x1,y0,z0,color,opacity,pattern,true).
  41640. draw_line(x1,y0,z0,x1,y1,z0,color,opacity,pattern,false).
  41641. draw_line(x1,y1,z0,x0,y1,z0,color,opacity,pattern,false).
  41642. draw_line(x0,y1,z0,x0,y0,z0,color,opacity,pattern,false).
  41643. draw_line(x0,y0,z1,x1,y0,z1,color,opacity,pattern,true).
  41644. draw_line(x1,y0,z1,x1,y1,z1,color,opacity,pattern,false).
  41645. draw_line(x1,y1,z1,x0,y1,z1,color,opacity,pattern,false).
  41646. draw_line(x0,y1,z1,x0,y0,z1,color,opacity,pattern,false).
  41647. draw_line(x0,y0,z0,x0,y0,z1,color,opacity,pattern,true).
  41648. draw_line(x1,y0,z0,x1,y0,z1,color,opacity,pattern,true).
  41649. draw_line(x1,y1,z0,x1,y1,z1,color,opacity,pattern,true).
  41650. draw_line(x0,y1,z0,x0,y1,z1,color,opacity,pattern,true);
  41651. }
  41652. //! Draw a filled 2d rectangle.
  41653. /**
  41654. \param x0 X-coordinate of the upper-left rectangle corner.
  41655. \param y0 Y-coordinate of the upper-left rectangle corner.
  41656. \param x1 X-coordinate of the lower-right rectangle corner.
  41657. \param y1 Y-coordinate of the lower-right rectangle corner.
  41658. \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
  41659. \param opacity Drawing opacity.
  41660. **/
  41661. template<typename tc>
  41662. CImg<T>& draw_rectangle(const int x0, const int y0,
  41663. const int x1, const int y1,
  41664. const tc *const color, const float opacity=1) {
  41665. return draw_rectangle(x0,y0,0,x1,y1,_depth - 1,color,opacity);
  41666. }
  41667. //! Draw a outlined 2d rectangle \overloading.
  41668. template<typename tc>
  41669. CImg<T>& draw_rectangle(const int x0, const int y0,
  41670. const int x1, const int y1,
  41671. const tc *const color, const float opacity,
  41672. const unsigned int pattern) {
  41673. if (is_empty()) return *this;
  41674. if (y0==y1) return draw_line(x0,y0,x1,y0,color,opacity,pattern,true);
  41675. if (x0==x1) return draw_line(x0,y0,x0,y1,color,opacity,pattern,true);
  41676. const int
  41677. nx0 = x0<x1?x0:x1, nx1 = x0^x1^nx0,
  41678. ny0 = y0<y1?y0:y1, ny1 = y0^y1^ny0;
  41679. if (ny1==ny0 + 1) return draw_line(nx0,ny0,nx1,ny0,color,opacity,pattern,true).
  41680. draw_line(nx1,ny1,nx0,ny1,color,opacity,pattern,false);
  41681. return draw_line(nx0,ny0,nx1,ny0,color,opacity,pattern,true).
  41682. draw_line(nx1,ny0 + 1,nx1,ny1 - 1,color,opacity,pattern,false).
  41683. draw_line(nx1,ny1,nx0,ny1,color,opacity,pattern,false).
  41684. draw_line(nx0,ny1 - 1,nx0,ny0 + 1,color,opacity,pattern,false);
  41685. }
  41686. //! Draw a filled 2d polygon.
  41687. /**
  41688. \param points Set of polygon vertices.
  41689. \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
  41690. \param opacity Drawing opacity.
  41691. **/
  41692. template<typename tp, typename tc>
  41693. CImg<T>& draw_polygon(const CImg<tp>& points,
  41694. const tc *const color, const float opacity=1) {
  41695. if (is_empty() || !points) return *this;
  41696. if (!color)
  41697. throw CImgArgumentException(_cimg_instance
  41698. "draw_polygon(): Specified color is (null).",
  41699. cimg_instance);
  41700. if (points._width==1) return draw_point((int)points(0,0),(int)points(0,1),color,opacity);
  41701. if (points._width==2) return draw_line((int)points(0,0),(int)points(0,1),
  41702. (int)points(1,0),(int)points(1,1),color,opacity);
  41703. if (points._width==3) return draw_triangle((int)points(0,0),(int)points(0,1),
  41704. (int)points(1,0),(int)points(1,1),
  41705. (int)points(2,0),(int)points(2,1),color,opacity);
  41706. cimg_init_scanline(color,opacity);
  41707. int
  41708. xmin = 0, ymin = 0,
  41709. xmax = points.get_shared_row(0).max_min(xmin),
  41710. ymax = points.get_shared_row(1).max_min(ymin);
  41711. if (xmax<0 || xmin>=width() || ymax<0 || ymin>=height()) return *this;
  41712. if (ymin==ymax) return draw_line(xmin,ymin,xmax,ymax,color,opacity);
  41713. ymin = std::max(0,ymin);
  41714. ymax = std::min(height() - 1,ymax);
  41715. CImg<intT> Xs(points._width,ymax - ymin + 1);
  41716. CImg<uintT> count(Xs._height,1,1,1,0);
  41717. unsigned int n = 0, nn = 1;
  41718. bool go_on = true;
  41719. while (go_on) {
  41720. unsigned int an = (nn + 1)%points._width;
  41721. const int
  41722. x0 = (int)points(n,0),
  41723. y0 = (int)points(n,1);
  41724. if (points(nn,1)==y0) while (points(an,1)==y0) { nn = an; (an+=1)%=points._width; }
  41725. const int
  41726. x1 = (int)points(nn,0),
  41727. y1 = (int)points(nn,1);
  41728. unsigned int tn = an;
  41729. while (points(tn,1)==y1) (tn+=1)%=points._width;
  41730. if (y0!=y1) {
  41731. const int
  41732. y2 = (int)points(tn,1),
  41733. x01 = x1 - x0, y01 = y1 - y0, y12 = y2 - y1,
  41734. dy = cimg::sign(y01),
  41735. tmax = std::max(1,cimg::abs(y01)),
  41736. tend = tmax - (dy==cimg::sign(y12));
  41737. unsigned int y = (unsigned int)y0 - ymin;
  41738. for (int t = 0; t<=tend; ++t, y+=dy)
  41739. if (y<Xs._height) Xs(count[y]++,y) = x0 + t*x01/tmax;
  41740. }
  41741. go_on = nn>n;
  41742. n = nn;
  41743. nn = an;
  41744. }
  41745. cimg_pragma_openmp(parallel for cimg_openmp_if(Xs._height>32))
  41746. cimg_forY(Xs,y) {
  41747. const CImg<intT> Xsy = Xs.get_shared_points(0,count[y] - 1,y).sort();
  41748. int px = width();
  41749. for (unsigned int n = 0; n<Xsy._width; n+=2) {
  41750. int x0 = Xsy[n];
  41751. const int x1 = Xsy[n + 1];
  41752. x0+=x0==px;
  41753. cimg_draw_scanline(x0,x1,y + ymin,color,opacity,1);
  41754. px = x1;
  41755. }
  41756. }
  41757. return *this;
  41758. }
  41759. //! Draw a outlined 2d polygon \overloading.
  41760. template<typename t, typename tc>
  41761. CImg<T>& draw_polygon(const CImg<t>& points,
  41762. const tc *const color, const float opacity, const unsigned int pattern) {
  41763. if (is_empty() || !points || points._width<3) return *this;
  41764. bool ninit_hatch = true;
  41765. switch (points._height) {
  41766. case 0 : case 1 :
  41767. throw CImgArgumentException(_cimg_instance
  41768. "draw_polygon(): Invalid specified point set.",
  41769. cimg_instance);
  41770. case 2 : { // 2d version.
  41771. CImg<intT> npoints(points._width,2);
  41772. int x = npoints(0,0) = (int)points(0,0), y = npoints(0,1) = (int)points(0,1);
  41773. unsigned int nb_points = 1;
  41774. for (unsigned int p = 1; p<points._width; ++p) {
  41775. const int nx = (int)points(p,0), ny = (int)points(p,1);
  41776. if (nx!=x || ny!=y) { npoints(nb_points,0) = nx; npoints(nb_points++,1) = ny; x = nx; y = ny; }
  41777. }
  41778. const int x0 = (int)npoints(0,0), y0 = (int)npoints(0,1);
  41779. int ox = x0, oy = y0;
  41780. for (unsigned int i = 1; i<nb_points; ++i) {
  41781. const int x = (int)npoints(i,0), y = (int)npoints(i,1);
  41782. draw_line(ox,oy,x,y,color,opacity,pattern,ninit_hatch);
  41783. ninit_hatch = false;
  41784. ox = x; oy = y;
  41785. }
  41786. draw_line(ox,oy,x0,y0,color,opacity,pattern,false);
  41787. } break;
  41788. default : { // 3d version.
  41789. CImg<intT> npoints(points._width,3);
  41790. int
  41791. x = npoints(0,0) = (int)points(0,0),
  41792. y = npoints(0,1) = (int)points(0,1),
  41793. z = npoints(0,2) = (int)points(0,2);
  41794. unsigned int nb_points = 1;
  41795. for (unsigned int p = 1; p<points._width; ++p) {
  41796. const int nx = (int)points(p,0), ny = (int)points(p,1), nz = (int)points(p,2);
  41797. if (nx!=x || ny!=y || nz!=z) {
  41798. npoints(nb_points,0) = nx; npoints(nb_points,1) = ny; npoints(nb_points++,2) = nz;
  41799. x = nx; y = ny; z = nz;
  41800. }
  41801. }
  41802. const int x0 = (int)npoints(0,0), y0 = (int)npoints(0,1), z0 = (int)npoints(0,2);
  41803. int ox = x0, oy = y0, oz = z0;
  41804. for (unsigned int i = 1; i<nb_points; ++i) {
  41805. const int x = (int)npoints(i,0), y = (int)npoints(i,1), z = (int)npoints(i,2);
  41806. draw_line(ox,oy,oz,x,y,z,color,opacity,pattern,ninit_hatch);
  41807. ninit_hatch = false;
  41808. ox = x; oy = y; oz = z;
  41809. }
  41810. draw_line(ox,oy,oz,x0,y0,z0,color,opacity,pattern,false);
  41811. }
  41812. }
  41813. return *this;
  41814. }
  41815. //! Draw a filled 2d ellipse.
  41816. /**
  41817. \param x0 X-coordinate of the ellipse center.
  41818. \param y0 Y-coordinate of the ellipse center.
  41819. \param r1 First radius of the ellipse.
  41820. \param r2 Second radius of the ellipse.
  41821. \param angle Angle of the first radius.
  41822. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  41823. \param opacity Drawing opacity.
  41824. **/
  41825. template<typename tc>
  41826. CImg<T>& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle,
  41827. const tc *const color, const float opacity=1) {
  41828. return _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,0U);
  41829. }
  41830. //! Draw a filled 2d ellipse \overloading.
  41831. /**
  41832. \param x0 X-coordinate of the ellipse center.
  41833. \param y0 Y-coordinate of the ellipse center.
  41834. \param tensor Diffusion tensor describing the ellipse.
  41835. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  41836. \param opacity Drawing opacity.
  41837. **/
  41838. template<typename t, typename tc>
  41839. CImg<T>& draw_ellipse(const int x0, const int y0, const CImg<t> &tensor,
  41840. const tc *const color, const float opacity=1) {
  41841. CImgList<t> eig = tensor.get_symmetric_eigen();
  41842. const CImg<t> &val = eig[0], &vec = eig[1];
  41843. return draw_ellipse(x0,y0,std::sqrt(val(0)),std::sqrt(val(1)),
  41844. std::atan2(vec(0,1),vec(0,0))*180/cimg::PI,
  41845. color,opacity);
  41846. }
  41847. //! Draw an outlined 2d ellipse.
  41848. /**
  41849. \param x0 X-coordinate of the ellipse center.
  41850. \param y0 Y-coordinate of the ellipse center.
  41851. \param r1 First radius of the ellipse.
  41852. \param r2 Second radius of the ellipse.
  41853. \param angle Angle of the first radius.
  41854. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  41855. \param opacity Drawing opacity.
  41856. \param pattern An integer whose bits describe the outline pattern.
  41857. **/
  41858. template<typename tc>
  41859. CImg<T>& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle,
  41860. const tc *const color, const float opacity, const unsigned int pattern) {
  41861. if (pattern) _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,pattern);
  41862. return *this;
  41863. }
  41864. //! Draw an outlined 2d ellipse \overloading.
  41865. /**
  41866. \param x0 X-coordinate of the ellipse center.
  41867. \param y0 Y-coordinate of the ellipse center.
  41868. \param tensor Diffusion tensor describing the ellipse.
  41869. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  41870. \param opacity Drawing opacity.
  41871. \param pattern An integer whose bits describe the outline pattern.
  41872. **/
  41873. template<typename t, typename tc>
  41874. CImg<T>& draw_ellipse(const int x0, const int y0, const CImg<t> &tensor,
  41875. const tc *const color, const float opacity,
  41876. const unsigned int pattern) {
  41877. CImgList<t> eig = tensor.get_symmetric_eigen();
  41878. const CImg<t> &val = eig[0], &vec = eig[1];
  41879. return draw_ellipse(x0,y0,std::sqrt(val(0)),std::sqrt(val(1)),
  41880. std::atan2(vec(0,1),vec(0,0))*180/cimg::PI,
  41881. color,opacity,pattern);
  41882. }
  41883. template<typename tc>
  41884. CImg<T>& _draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle,
  41885. const tc *const color, const float opacity,
  41886. const unsigned int pattern) {
  41887. if (is_empty()) return *this;
  41888. if (!color)
  41889. throw CImgArgumentException(_cimg_instance
  41890. "draw_ellipse(): Specified color is (null).",
  41891. cimg_instance);
  41892. if (r1<=0 || r2<=0) return draw_point(x0,y0,color,opacity);
  41893. if (r1==r2 && (float)(int)r1==r1) {
  41894. if (pattern) return draw_circle(x0,y0,(int)cimg::round(r1),color,opacity,pattern);
  41895. else return draw_circle(x0,y0,(int)cimg::round(r1),color,opacity);
  41896. }
  41897. cimg_init_scanline(color,opacity);
  41898. const float
  41899. nr1 = cimg::abs(r1) - 0.5, nr2 = cimg::abs(r2) - 0.5,
  41900. nangle = (float)(angle*cimg::PI/180),
  41901. u = (float)std::cos(nangle),
  41902. v = (float)std::sin(nangle),
  41903. rmax = std::max(nr1,nr2),
  41904. l1 = (float)std::pow(rmax/(nr1>0?nr1:1e-6),2),
  41905. l2 = (float)std::pow(rmax/(nr2>0?nr2:1e-6),2),
  41906. a = l1*u*u + l2*v*v,
  41907. b = u*v*(l1 - l2),
  41908. c = l1*v*v + l2*u*u;
  41909. const int
  41910. yb = (int)cimg::round(std::sqrt(a*rmax*rmax/(a*c - b*b))),
  41911. tymin = y0 - yb - 1,
  41912. tymax = y0 + yb + 1,
  41913. ymin = tymin<0?0:tymin,
  41914. ymax = tymax>=height()?height() - 1:tymax;
  41915. int oxmin = 0, oxmax = 0;
  41916. bool first_line = true;
  41917. for (int y = ymin; y<=ymax; ++y) {
  41918. const float
  41919. Y = y - y0 + (y<y0?0.5f:-0.5f),
  41920. delta = b*b*Y*Y - a*(c*Y*Y - rmax*rmax),
  41921. sdelta = delta>0?(float)std::sqrt(delta)/a:0.0f,
  41922. bY = b*Y/a,
  41923. fxmin = x0 - 0.5f - bY - sdelta,
  41924. fxmax = x0 + 0.5f - bY + sdelta;
  41925. const int xmin = (int)cimg::round(fxmin), xmax = (int)cimg::round(fxmax);
  41926. if (!pattern) cimg_draw_scanline(xmin,xmax,y,color,opacity,1);
  41927. else {
  41928. if (first_line) {
  41929. if (y0 - yb>=0) cimg_draw_scanline(xmin,xmax,y,color,opacity,1);
  41930. else draw_point(xmin,y,color,opacity).draw_point(xmax,y,color,opacity);
  41931. first_line = false;
  41932. } else {
  41933. if (xmin<oxmin) cimg_draw_scanline(xmin,oxmin - 1,y,color,opacity,1);
  41934. else cimg_draw_scanline(oxmin + (oxmin==xmin?0:1),xmin,y,color,opacity,1);
  41935. if (xmax<oxmax) cimg_draw_scanline(xmax,oxmax - 1,y,color,opacity,1);
  41936. else cimg_draw_scanline(oxmax + (oxmax==xmax?0:1),xmax,y,color,opacity,1);
  41937. if (y==tymax) cimg_draw_scanline(xmin + 1,xmax - 1,y,color,opacity,1);
  41938. }
  41939. }
  41940. oxmin = xmin; oxmax = xmax;
  41941. }
  41942. return *this;
  41943. }
  41944. //! Draw a filled 2d circle.
  41945. /**
  41946. \param x0 X-coordinate of the circle center.
  41947. \param y0 Y-coordinate of the circle center.
  41948. \param radius Circle radius.
  41949. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  41950. \param opacity Drawing opacity.
  41951. \note
  41952. - Circle version of the Bresenham's algorithm is used.
  41953. **/
  41954. template<typename tc>
  41955. CImg<T>& draw_circle(const int x0, const int y0, int radius,
  41956. const tc *const color, const float opacity=1) {
  41957. if (is_empty()) return *this;
  41958. if (!color)
  41959. throw CImgArgumentException(_cimg_instance
  41960. "draw_circle(): Specified color is (null).",
  41961. cimg_instance);
  41962. cimg_init_scanline(color,opacity);
  41963. if (radius<0 || x0 - radius>=width() || y0 + radius<0 || y0 - radius>=height()) return *this;
  41964. if (y0>=0 && y0<height()) cimg_draw_scanline(x0 - radius,x0 + radius,y0,color,opacity,1);
  41965. for (int f = 1 - radius, ddFx = 0, ddFy = -(radius<<1), x = 0, y = radius; x<y; ) {
  41966. if (f>=0) {
  41967. const int x1 = x0 - x, x2 = x0 + x, y1 = y0 - y, y2 = y0 + y;
  41968. if (y1>=0 && y1<height()) cimg_draw_scanline(x1,x2,y1,color,opacity,1);
  41969. if (y2>=0 && y2<height()) cimg_draw_scanline(x1,x2,y2,color,opacity,1);
  41970. f+=(ddFy+=2); --y;
  41971. }
  41972. const bool no_diag = y!=(x++);
  41973. ++(f+=(ddFx+=2));
  41974. const int x1 = x0 - y, x2 = x0 + y, y1 = y0 - x, y2 = y0 + x;
  41975. if (no_diag) {
  41976. if (y1>=0 && y1<height()) cimg_draw_scanline(x1,x2,y1,color,opacity,1);
  41977. if (y2>=0 && y2<height()) cimg_draw_scanline(x1,x2,y2,color,opacity,1);
  41978. }
  41979. }
  41980. return *this;
  41981. }
  41982. //! Draw an outlined 2d circle.
  41983. /**
  41984. \param x0 X-coordinate of the circle center.
  41985. \param y0 Y-coordinate of the circle center.
  41986. \param radius Circle radius.
  41987. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  41988. \param opacity Drawing opacity.
  41989. \param pattern An integer whose bits describe the outline pattern.
  41990. **/
  41991. template<typename tc>
  41992. CImg<T>& draw_circle(const int x0, const int y0, int radius,
  41993. const tc *const color, const float opacity,
  41994. const unsigned int pattern) {
  41995. cimg::unused(pattern);
  41996. if (is_empty()) return *this;
  41997. if (!color)
  41998. throw CImgArgumentException(_cimg_instance
  41999. "draw_circle(): Specified color is (null).",
  42000. cimg_instance);
  42001. if (radius<0 || x0 - radius>=width() || y0 + radius<0 || y0 - radius>=height()) return *this;
  42002. if (!radius) return draw_point(x0,y0,color,opacity);
  42003. draw_point(x0 - radius,y0,color,opacity).draw_point(x0 + radius,y0,color,opacity).
  42004. draw_point(x0,y0 - radius,color,opacity).draw_point(x0,y0 + radius,color,opacity);
  42005. if (radius==1) return *this;
  42006. for (int f = 1 - radius, ddFx = 0, ddFy = -(radius<<1), x = 0, y = radius; x<y; ) {
  42007. if (f>=0) { f+=(ddFy+=2); --y; }
  42008. ++x; ++(f+=(ddFx+=2));
  42009. if (x!=y + 1) {
  42010. const int x1 = x0 - y, x2 = x0 + y, y1 = y0 - x, y2 = y0 + x,
  42011. x3 = x0 - x, x4 = x0 + x, y3 = y0 - y, y4 = y0 + y;
  42012. draw_point(x1,y1,color,opacity).draw_point(x1,y2,color,opacity).
  42013. draw_point(x2,y1,color,opacity).draw_point(x2,y2,color,opacity);
  42014. if (x!=y)
  42015. draw_point(x3,y3,color,opacity).draw_point(x4,y4,color,opacity).
  42016. draw_point(x4,y3,color,opacity).draw_point(x3,y4,color,opacity);
  42017. }
  42018. }
  42019. return *this;
  42020. }
  42021. //! Draw an image.
  42022. /**
  42023. \param sprite Sprite image.
  42024. \param x0 X-coordinate of the sprite position.
  42025. \param y0 Y-coordinate of the sprite position.
  42026. \param z0 Z-coordinate of the sprite position.
  42027. \param c0 C-coordinate of the sprite position.
  42028. \param opacity Drawing opacity.
  42029. **/
  42030. template<typename t>
  42031. CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
  42032. const CImg<t>& sprite, const float opacity=1) {
  42033. if (is_empty() || !sprite) return *this;
  42034. if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity);
  42035. if (x0==0 && y0==0 && z0==0 && c0==0 && is_sameXYZC(sprite) && opacity>=1 && !is_shared())
  42036. return assign(sprite,false);
  42037. const bool bx = (x0<0), by = (y0<0), bz = (z0<0), bc = (c0<0);
  42038. const int
  42039. lX = sprite.width() - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0) + (bx?x0:0),
  42040. lY = sprite.height() - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0) + (by?y0:0),
  42041. lZ = sprite.depth() - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0) + (bz?z0:0),
  42042. lC = sprite.spectrum() - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0) + (bc?c0:0);
  42043. const t
  42044. *ptrs = sprite._data +
  42045. (bx?-x0:0) +
  42046. (by?-y0*(ulongT)sprite.width():0) +
  42047. (bz?-z0*(ulongT)sprite.width()*sprite.height():0) +
  42048. (bc?-c0*(ulongT)sprite.width()*sprite.height()*sprite.depth():0);
  42049. const ulongT
  42050. offX = (ulongT)_width - lX,
  42051. soffX = (ulongT)sprite._width - lX,
  42052. offY = (ulongT)_width*(_height - lY),
  42053. soffY = (ulongT)sprite._width*(sprite._height - lY),
  42054. offZ = (ulongT)_width*_height*(_depth - lZ),
  42055. soffZ = (ulongT)sprite._width*sprite._height*(sprite._depth - lZ);
  42056. const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
  42057. if (lX>0 && lY>0 && lZ>0 && lC>0) {
  42058. T *ptrd = data(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,c0<0?0:c0);
  42059. for (int v = 0; v<lC; ++v) {
  42060. for (int z = 0; z<lZ; ++z) {
  42061. for (int y = 0; y<lY; ++y) {
  42062. if (opacity>=1) for (int x = 0; x<lX; ++x) *(ptrd++) = (T)*(ptrs++);
  42063. else for (int x = 0; x<lX; ++x) { *ptrd = (T)(nopacity*(*(ptrs++)) + *ptrd*copacity); ++ptrd; }
  42064. ptrd+=offX; ptrs+=soffX;
  42065. }
  42066. ptrd+=offY; ptrs+=soffY;
  42067. }
  42068. ptrd+=offZ; ptrs+=soffZ;
  42069. }
  42070. }
  42071. return *this;
  42072. }
  42073. //! Draw an image \specialization.
  42074. CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
  42075. const CImg<T>& sprite, const float opacity=1) {
  42076. if (is_empty() || !sprite) return *this;
  42077. if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity);
  42078. if (x0==0 && y0==0 && z0==0 && c0==0 && is_sameXYZC(sprite) && opacity>=1 && !is_shared())
  42079. return assign(sprite,false);
  42080. const bool bx = (x0<0), by = (y0<0), bz = (z0<0), bc = (c0<0);
  42081. const int
  42082. lX = sprite.width() - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0) + (bx?x0:0),
  42083. lY = sprite.height() - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0) + (by?y0:0),
  42084. lZ = sprite.depth() - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0) + (bz?z0:0),
  42085. lC = sprite.spectrum() - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0) + (bc?c0:0);
  42086. const T
  42087. *ptrs = sprite._data +
  42088. (bx?-x0:0) +
  42089. (by?-y0*(ulongT)sprite.width():0) +
  42090. (bz?-z0*(ulongT)sprite.width()*sprite.height():0) +
  42091. (bc?-c0*(ulongT)sprite.width()*sprite.height()*sprite.depth():0);
  42092. const ulongT
  42093. offX = (ulongT)_width - lX,
  42094. soffX = (ulongT)sprite._width - lX,
  42095. offY = (ulongT)_width*(_height - lY),
  42096. soffY = (ulongT)sprite._width*(sprite._height - lY),
  42097. offZ = (ulongT)_width*_height*(_depth - lZ),
  42098. soffZ = (ulongT)sprite._width*sprite._height*(sprite._depth - lZ),
  42099. slX = lX*sizeof(T);
  42100. const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
  42101. if (lX>0 && lY>0 && lZ>0 && lC>0) {
  42102. T *ptrd = data(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,c0<0?0:c0);
  42103. for (int v = 0; v<lC; ++v) {
  42104. for (int z = 0; z<lZ; ++z) {
  42105. if (opacity>=1)
  42106. for (int y = 0; y<lY; ++y) { std::memcpy(ptrd,ptrs,slX); ptrd+=_width; ptrs+=sprite._width; }
  42107. else for (int y = 0; y<lY; ++y) {
  42108. for (int x = 0; x<lX; ++x) { *ptrd = (T)(nopacity*(*(ptrs++)) + *ptrd*copacity); ++ptrd; }
  42109. ptrd+=offX; ptrs+=soffX;
  42110. }
  42111. ptrd+=offY; ptrs+=soffY;
  42112. }
  42113. ptrd+=offZ; ptrs+=soffZ;
  42114. }
  42115. }
  42116. return *this;
  42117. }
  42118. //! Draw an image \overloading.
  42119. template<typename t>
  42120. CImg<T>& draw_image(const int x0, const int y0, const int z0,
  42121. const CImg<t>& sprite, const float opacity=1) {
  42122. return draw_image(x0,y0,z0,0,sprite,opacity);
  42123. }
  42124. //! Draw an image \overloading.
  42125. template<typename t>
  42126. CImg<T>& draw_image(const int x0, const int y0,
  42127. const CImg<t>& sprite, const float opacity=1) {
  42128. return draw_image(x0,y0,0,sprite,opacity);
  42129. }
  42130. //! Draw an image \overloading.
  42131. template<typename t>
  42132. CImg<T>& draw_image(const int x0,
  42133. const CImg<t>& sprite, const float opacity=1) {
  42134. return draw_image(x0,0,sprite,opacity);
  42135. }
  42136. //! Draw an image \overloading.
  42137. template<typename t>
  42138. CImg<T>& draw_image(const CImg<t>& sprite, const float opacity=1) {
  42139. return draw_image(0,sprite,opacity);
  42140. }
  42141. //! Draw a masked image.
  42142. /**
  42143. \param sprite Sprite image.
  42144. \param mask Mask image.
  42145. \param x0 X-coordinate of the sprite position in the image instance.
  42146. \param y0 Y-coordinate of the sprite position in the image instance.
  42147. \param z0 Z-coordinate of the sprite position in the image instance.
  42148. \param c0 C-coordinate of the sprite position in the image instance.
  42149. \param mask_max_value Maximum pixel value of the mask image \c mask.
  42150. \param opacity Drawing opacity.
  42151. \note
  42152. - Pixel values of \c mask set the opacity of the corresponding pixels in \c sprite.
  42153. - Dimensions along x,y and z of \p sprite and \p mask must be the same.
  42154. **/
  42155. template<typename ti, typename tm>
  42156. CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
  42157. const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
  42158. const float mask_max_value=1) {
  42159. if (is_empty() || !sprite || !mask) return *this;
  42160. if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,mask,opacity,mask_max_value);
  42161. if (is_overlapped(mask)) return draw_image(x0,y0,z0,c0,sprite,+mask,opacity,mask_max_value);
  42162. if (mask._width!=sprite._width || mask._height!=sprite._height || mask._depth!=sprite._depth)
  42163. throw CImgArgumentException(_cimg_instance
  42164. "draw_image(): Sprite (%u,%u,%u,%u,%p) and mask (%u,%u,%u,%u,%p) have "
  42165. "incompatible dimensions.",
  42166. cimg_instance,
  42167. sprite._width,sprite._height,sprite._depth,sprite._spectrum,sprite._data,
  42168. mask._width,mask._height,mask._depth,mask._spectrum,mask._data);
  42169. const bool bx = (x0<0), by = (y0<0), bz = (z0<0), bc = (c0<0);
  42170. const int
  42171. lX = sprite.width() - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0) + (bx?x0:0),
  42172. lY = sprite.height() - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0) + (by?y0:0),
  42173. lZ = sprite.depth() - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0) + (bz?z0:0),
  42174. lC = sprite.spectrum() - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0) + (bc?c0:0);
  42175. const ulongT
  42176. coff = (bx?-x0:0) +
  42177. (by?-y0*(ulongT)mask.width():0) +
  42178. (bz?-z0*(ulongT)mask.width()*mask.height():0) +
  42179. (bc?-c0*(ulongT)mask.width()*mask.height()*mask.depth():0),
  42180. ssize = (ulongT)mask.width()*mask.height()*mask.depth()*mask.spectrum();
  42181. const ti *ptrs = sprite._data + coff;
  42182. const tm *ptrm = mask._data + coff;
  42183. const ulongT
  42184. offX = (ulongT)_width - lX,
  42185. soffX = (ulongT)sprite._width - lX,
  42186. offY = (ulongT)_width*(_height - lY),
  42187. soffY = (ulongT)sprite._width*(sprite._height - lY),
  42188. offZ = (ulongT)_width*_height*(_depth - lZ),
  42189. soffZ = (ulongT)sprite._width*sprite._height*(sprite._depth - lZ);
  42190. if (lX>0 && lY>0 && lZ>0 && lC>0) {
  42191. T *ptrd = data(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,c0<0?0:c0);
  42192. for (int c = 0; c<lC; ++c) {
  42193. ptrm = mask._data + (ptrm - mask._data)%ssize;
  42194. for (int z = 0; z<lZ; ++z) {
  42195. for (int y = 0; y<lY; ++y) {
  42196. for (int x = 0; x<lX; ++x) {
  42197. const float mopacity = (float)(*(ptrm++)*opacity),
  42198. nopacity = cimg::abs(mopacity), copacity = mask_max_value - std::max(mopacity,0.0f);
  42199. *ptrd = (T)((nopacity*(*(ptrs++)) + *ptrd*copacity)/mask_max_value);
  42200. ++ptrd;
  42201. }
  42202. ptrd+=offX; ptrs+=soffX; ptrm+=soffX;
  42203. }
  42204. ptrd+=offY; ptrs+=soffY; ptrm+=soffY;
  42205. }
  42206. ptrd+=offZ; ptrs+=soffZ; ptrm+=soffZ;
  42207. }
  42208. }
  42209. return *this;
  42210. }
  42211. //! Draw a masked image \overloading.
  42212. template<typename ti, typename tm>
  42213. CImg<T>& draw_image(const int x0, const int y0, const int z0,
  42214. const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
  42215. const float mask_max_value=1) {
  42216. return draw_image(x0,y0,z0,0,sprite,mask,opacity,mask_max_value);
  42217. }
  42218. //! Draw a image \overloading.
  42219. template<typename ti, typename tm>
  42220. CImg<T>& draw_image(const int x0, const int y0,
  42221. const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
  42222. const float mask_max_value=1) {
  42223. return draw_image(x0,y0,0,sprite,mask,opacity,mask_max_value);
  42224. }
  42225. //! Draw a image \overloading.
  42226. template<typename ti, typename tm>
  42227. CImg<T>& draw_image(const int x0,
  42228. const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
  42229. const float mask_max_value=1) {
  42230. return draw_image(x0,0,sprite,mask,opacity,mask_max_value);
  42231. }
  42232. //! Draw an image.
  42233. template<typename ti, typename tm>
  42234. CImg<T>& draw_image(const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
  42235. const float mask_max_value=1) {
  42236. return draw_image(0,sprite,mask,opacity,mask_max_value);
  42237. }
  42238. //! Draw a text string.
  42239. /**
  42240. \param x0 X-coordinate of the text in the image instance.
  42241. \param y0 Y-coordinate of the text in the image instance.
  42242. \param text Format of the text ('printf'-style format string).
  42243. \param foreground_color Pointer to \c spectrum() consecutive values, defining the foreground drawing color.
  42244. \param background_color Pointer to \c spectrum() consecutive values, defining the background drawing color.
  42245. \param opacity Drawing opacity.
  42246. \param font Font used for drawing text.
  42247. **/
  42248. template<typename tc1, typename tc2, typename t>
  42249. CImg<T>& draw_text(const int x0, const int y0,
  42250. const char *const text,
  42251. const tc1 *const foreground_color, const tc2 *const background_color,
  42252. const float opacity, const CImgList<t>& font, ...) {
  42253. if (!font) return *this;
  42254. CImg<charT> tmp(2048);
  42255. std::va_list ap; va_start(ap,font);
  42256. cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
  42257. return _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font,false);
  42258. }
  42259. //! Draw a text string \overloading.
  42260. /**
  42261. \note A transparent background is used for the text.
  42262. **/
  42263. template<typename tc, typename t>
  42264. CImg<T>& draw_text(const int x0, const int y0,
  42265. const char *const text,
  42266. const tc *const foreground_color, const int,
  42267. const float opacity, const CImgList<t>& font, ...) {
  42268. if (!font) return *this;
  42269. CImg<charT> tmp(2048);
  42270. std::va_list ap; va_start(ap,font);
  42271. cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
  42272. return _draw_text(x0,y0,tmp,foreground_color,(tc*)0,opacity,font,false);
  42273. }
  42274. //! Draw a text string \overloading.
  42275. /**
  42276. \note A transparent foreground is used for the text.
  42277. **/
  42278. template<typename tc, typename t>
  42279. CImg<T>& draw_text(const int x0, const int y0,
  42280. const char *const text,
  42281. const int, const tc *const background_color,
  42282. const float opacity, const CImgList<t>& font, ...) {
  42283. if (!font) return *this;
  42284. CImg<charT> tmp(2048);
  42285. std::va_list ap; va_start(ap,font);
  42286. cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
  42287. return _draw_text(x0,y0,tmp,(tc*)0,background_color,opacity,font,false);
  42288. }
  42289. //! Draw a text string \overloading.
  42290. /**
  42291. \param x0 X-coordinate of the text in the image instance.
  42292. \param y0 Y-coordinate of the text in the image instance.
  42293. \param text Format of the text ('printf'-style format string).
  42294. \param foreground_color Array of spectrum() values of type \c T,
  42295. defining the foreground color (0 means 'transparent').
  42296. \param background_color Array of spectrum() values of type \c T,
  42297. defining the background color (0 means 'transparent').
  42298. \param opacity Drawing opacity.
  42299. \param font_height Height of the text font (exact match for 13,23,53,103, interpolated otherwise).
  42300. **/
  42301. template<typename tc1, typename tc2>
  42302. CImg<T>& draw_text(const int x0, const int y0,
  42303. const char *const text,
  42304. const tc1 *const foreground_color, const tc2 *const background_color,
  42305. const float opacity=1, const unsigned int font_height=13, ...) {
  42306. if (!font_height) return *this;
  42307. CImg<charT> tmp(2048);
  42308. std::va_list ap; va_start(ap,font_height);
  42309. cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
  42310. const CImgList<ucharT>& font = CImgList<ucharT>::font(font_height,true);
  42311. _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font,true);
  42312. return *this;
  42313. }
  42314. //! Draw a text string \overloading.
  42315. template<typename tc>
  42316. CImg<T>& draw_text(const int x0, const int y0,
  42317. const char *const text,
  42318. const tc *const foreground_color, const int background_color=0,
  42319. const float opacity=1, const unsigned int font_height=13, ...) {
  42320. if (!font_height) return *this;
  42321. cimg::unused(background_color);
  42322. CImg<charT> tmp(2048);
  42323. std::va_list ap; va_start(ap,font_height);
  42324. cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
  42325. return draw_text(x0,y0,"%s",foreground_color,(const tc*)0,opacity,font_height,tmp._data);
  42326. }
  42327. //! Draw a text string \overloading.
  42328. template<typename tc>
  42329. CImg<T>& draw_text(const int x0, const int y0,
  42330. const char *const text,
  42331. const int, const tc *const background_color,
  42332. const float opacity=1, const unsigned int font_height=13, ...) {
  42333. if (!font_height) return *this;
  42334. CImg<charT> tmp(2048);
  42335. std::va_list ap; va_start(ap,font_height);
  42336. cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
  42337. return draw_text(x0,y0,"%s",(tc*)0,background_color,opacity,font_height,tmp._data);
  42338. }
  42339. template<typename tc1, typename tc2, typename t>
  42340. CImg<T>& _draw_text(const int x0, const int y0,
  42341. const char *const text,
  42342. const tc1 *const foreground_color, const tc2 *const background_color,
  42343. const float opacity, const CImgList<t>& font,
  42344. const bool is_native_font) {
  42345. if (!text) return *this;
  42346. if (!font)
  42347. throw CImgArgumentException(_cimg_instance
  42348. "draw_text(): Empty specified font.",
  42349. cimg_instance);
  42350. const unsigned int text_length = (unsigned int)std::strlen(text);
  42351. const bool _is_empty = is_empty();
  42352. if (_is_empty) {
  42353. // If needed, pre-compute necessary size of the image
  42354. int x = 0, y = 0, w = 0;
  42355. unsigned char c = 0;
  42356. for (unsigned int i = 0; i<text_length; ++i) {
  42357. c = (unsigned char)text[i];
  42358. switch (c) {
  42359. case '\n' : y+=font[0]._height; if (x>w) w = x; x = 0; break;
  42360. case '\t' : x+=4*font[' ']._width; break;
  42361. default : if (c<font._width) x+=font[c]._width;
  42362. }
  42363. }
  42364. if (x!=0 || c=='\n') {
  42365. if (x>w) w=x;
  42366. y+=font[0]._height;
  42367. }
  42368. assign(x0 + w,y0 + y,1,is_native_font?1:font[0]._spectrum,(T)0);
  42369. }
  42370. int x = x0, y = y0;
  42371. for (unsigned int i = 0; i<text_length; ++i) {
  42372. const unsigned char c = (unsigned char)text[i];
  42373. switch (c) {
  42374. case '\n' : y+=font[0]._height; x = x0; break;
  42375. case '\t' : x+=4*font[' ']._width; break;
  42376. default : if (c<font._width) {
  42377. CImg<T> letter = font[c];
  42378. if (letter) {
  42379. if (is_native_font && _spectrum>letter._spectrum) letter.resize(-100,-100,1,_spectrum,0,2);
  42380. const unsigned int cmin = std::min(_spectrum,letter._spectrum);
  42381. if (foreground_color)
  42382. for (unsigned int c = 0; c<cmin; ++c)
  42383. if (foreground_color[c]!=1) letter.get_shared_channel(c)*=foreground_color[c];
  42384. if (c + 256<font.width()) { // Letter has mask.
  42385. if (background_color)
  42386. for (unsigned int c = 0; c<cmin; ++c)
  42387. draw_rectangle(x,y,0,c,x + letter._width - 1,y + letter._height - 1,0,c,
  42388. background_color[c],opacity);
  42389. draw_image(x,y,letter,font[c + 256],opacity,255.0f);
  42390. } else draw_image(x,y,letter,opacity); // Letter has no mask.
  42391. x+=letter._width;
  42392. }
  42393. }
  42394. }
  42395. }
  42396. return *this;
  42397. }
  42398. //! Draw a 2d vector field.
  42399. /**
  42400. \param flow Image of 2d vectors used as input data.
  42401. \param color Image of spectrum()-D vectors corresponding to the color of each arrow.
  42402. \param opacity Drawing opacity.
  42403. \param sampling Length (in pixels) between each arrow.
  42404. \param factor Length factor of each arrow (if <0, computed as a percentage of the maximum length).
  42405. \param is_arrow Tells if arrows must be drawn, instead of oriented segments.
  42406. \param pattern Used pattern to draw lines.
  42407. \note Clipping is supported.
  42408. **/
  42409. template<typename t1, typename t2>
  42410. CImg<T>& draw_quiver(const CImg<t1>& flow,
  42411. const t2 *const color, const float opacity=1,
  42412. const unsigned int sampling=25, const float factor=-20,
  42413. const bool is_arrow=true, const unsigned int pattern=~0U) {
  42414. return draw_quiver(flow,CImg<t2>(color,_spectrum,1,1,1,true),opacity,sampling,factor,is_arrow,pattern);
  42415. }
  42416. //! Draw a 2d vector field, using a field of colors.
  42417. /**
  42418. \param flow Image of 2d vectors used as input data.
  42419. \param color Image of spectrum()-D vectors corresponding to the color of each arrow.
  42420. \param opacity Opacity of the drawing.
  42421. \param sampling Length (in pixels) between each arrow.
  42422. \param factor Length factor of each arrow (if <0, computed as a percentage of the maximum length).
  42423. \param is_arrow Tells if arrows must be drawn, instead of oriented segments.
  42424. \param pattern Used pattern to draw lines.
  42425. \note Clipping is supported.
  42426. **/
  42427. template<typename t1, typename t2>
  42428. CImg<T>& draw_quiver(const CImg<t1>& flow,
  42429. const CImg<t2>& color, const float opacity=1,
  42430. const unsigned int sampling=25, const float factor=-20,
  42431. const bool is_arrow=true, const unsigned int pattern=~0U) {
  42432. if (is_empty()) return *this;
  42433. if (!flow || flow._spectrum!=2)
  42434. throw CImgArgumentException(_cimg_instance
  42435. "draw_quiver(): Invalid dimensions of specified flow (%u,%u,%u,%u,%p).",
  42436. cimg_instance,
  42437. flow._width,flow._height,flow._depth,flow._spectrum,flow._data);
  42438. if (sampling<=0)
  42439. throw CImgArgumentException(_cimg_instance
  42440. "draw_quiver(): Invalid sampling value %g "
  42441. "(should be >0)",
  42442. cimg_instance,
  42443. sampling);
  42444. const bool colorfield = (color._width==flow._width && color._height==flow._height &&
  42445. color._depth==1 && color._spectrum==_spectrum);
  42446. if (is_overlapped(flow)) return draw_quiver(+flow,color,opacity,sampling,factor,is_arrow,pattern);
  42447. float vmax,fact;
  42448. if (factor<=0) {
  42449. float m, M = (float)flow.get_norm(2).max_min(m);
  42450. vmax = (float)std::max(cimg::abs(m),cimg::abs(M));
  42451. if (!vmax) vmax = 1;
  42452. fact = -factor;
  42453. } else { fact = factor; vmax = 1; }
  42454. for (unsigned int y = sampling/2; y<_height; y+=sampling)
  42455. for (unsigned int x = sampling/2; x<_width; x+=sampling) {
  42456. const unsigned int X = x*flow._width/_width, Y = y*flow._height/_height;
  42457. float u = (float)flow(X,Y,0,0)*fact/vmax, v = (float)flow(X,Y,0,1)*fact/vmax;
  42458. if (is_arrow) {
  42459. const int xx = (int)(x + u), yy = (int)(y + v);
  42460. if (colorfield) draw_arrow(x,y,xx,yy,color.get_vector_at(X,Y)._data,opacity,45,sampling/5.0f,pattern);
  42461. else draw_arrow(x,y,xx,yy,color._data,opacity,45,sampling/5.0f,pattern);
  42462. } else {
  42463. if (colorfield)
  42464. draw_line((int)(x - 0.5*u),(int)(y - 0.5*v),(int)(x + 0.5*u),(int)(y + 0.5*v),
  42465. color.get_vector_at(X,Y)._data,opacity,pattern);
  42466. else draw_line((int)(x - 0.5*u),(int)(y - 0.5*v),(int)(x + 0.5*u),(int)(y + 0.5*v),
  42467. color._data,opacity,pattern);
  42468. }
  42469. }
  42470. return *this;
  42471. }
  42472. //! Draw a labeled horizontal axis.
  42473. /**
  42474. \param values_x Values along the horizontal axis.
  42475. \param y Y-coordinate of the horizontal axis in the image instance.
  42476. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  42477. \param opacity Drawing opacity.
  42478. \param pattern Drawing pattern.
  42479. \param font_height Height of the labels (exact match for 13,23,53,103, interpolated otherwise).
  42480. \param allow_zero Enable/disable the drawing of label '0' if found.
  42481. **/
  42482. template<typename t, typename tc>
  42483. CImg<T>& draw_axis(const CImg<t>& values_x, const int y,
  42484. const tc *const color, const float opacity=1,
  42485. const unsigned int pattern=~0U, const unsigned int font_height=13,
  42486. const bool allow_zero=true) {
  42487. if (is_empty()) return *this;
  42488. const int yt = (y + 3 + font_height)<_height?y + 3:y - 2 - (int)font_height;
  42489. const int siz = (int)values_x.size() - 1;
  42490. CImg<charT> txt(32);
  42491. CImg<T> label;
  42492. if (siz<=0) { // Degenerated case.
  42493. draw_line(0,y,_width - 1,y,color,opacity,pattern);
  42494. if (!siz) {
  42495. cimg_snprintf(txt,txt._width,"%g",(double)*values_x);
  42496. label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
  42497. const int
  42498. _xt = (width() - label.width())/2,
  42499. xt = _xt<3?3:_xt + label.width()>=width() - 2?width() - 3 - label.width():_xt;
  42500. draw_point(width()/2,y - 1,color,opacity).draw_point(width()/2,y + 1,color,opacity);
  42501. if (allow_zero || *txt!='0' || txt[1]!=0)
  42502. draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
  42503. }
  42504. } else { // Regular case.
  42505. if (values_x[0]<values_x[siz]) draw_arrow(0,y,_width - 1,y,color,opacity,30,5,pattern);
  42506. else draw_arrow(_width - 1,y,0,y,color,opacity,30,5,pattern);
  42507. cimg_foroff(values_x,x) {
  42508. cimg_snprintf(txt,txt._width,"%g",(double)values_x(x));
  42509. label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
  42510. const int
  42511. xi = (int)(x*(_width - 1)/siz),
  42512. _xt = xi - label.width()/2,
  42513. xt = _xt<3?3:_xt + label.width()>=width() - 2?width() - 3 - label.width():_xt;
  42514. draw_point(xi,y - 1,color,opacity).draw_point(xi,y + 1,color,opacity);
  42515. if (allow_zero || *txt!='0' || txt[1]!=0)
  42516. draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
  42517. }
  42518. }
  42519. return *this;
  42520. }
  42521. //! Draw a labeled vertical axis.
  42522. /**
  42523. \param x X-coordinate of the vertical axis in the image instance.
  42524. \param values_y Values along the Y-axis.
  42525. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  42526. \param opacity Drawing opacity.
  42527. \param pattern Drawing pattern.
  42528. \param font_height Height of the labels (exact match for 13,23,53,103, interpolated otherwise).
  42529. \param allow_zero Enable/disable the drawing of label '0' if found.
  42530. **/
  42531. template<typename t, typename tc>
  42532. CImg<T>& draw_axis(const int x, const CImg<t>& values_y,
  42533. const tc *const color, const float opacity=1,
  42534. const unsigned int pattern=~0U, const unsigned int font_height=13,
  42535. const bool allow_zero=true) {
  42536. if (is_empty()) return *this;
  42537. int siz = (int)values_y.size() - 1;
  42538. CImg<charT> txt(32);
  42539. CImg<T> label;
  42540. if (siz<=0) { // Degenerated case.
  42541. draw_line(x,0,x,_height - 1,color,opacity,pattern);
  42542. if (!siz) {
  42543. cimg_snprintf(txt,txt._width,"%g",(double)*values_y);
  42544. label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
  42545. const int
  42546. _yt = (height() - label.height())/2,
  42547. yt = _yt<0?0:_yt + label.height()>=height()?height() - 1-label.height():_yt,
  42548. _xt = x - 2 - label.width(),
  42549. xt = _xt>=0?_xt:x + 3;
  42550. draw_point(x - 1,height()/2,color,opacity).draw_point(x + 1,height()/2,color,opacity);
  42551. if (allow_zero || *txt!='0' || txt[1]!=0)
  42552. draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
  42553. }
  42554. } else { // Regular case.
  42555. if (values_y[0]<values_y[siz]) draw_arrow(x,0,x,_height - 1,color,opacity,30,5,pattern);
  42556. else draw_arrow(x,_height - 1,x,0,color,opacity,30,5,pattern);
  42557. cimg_foroff(values_y,y) {
  42558. cimg_snprintf(txt,txt._width,"%g",(double)values_y(y));
  42559. label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
  42560. const int
  42561. yi = (int)(y*(_height - 1)/siz),
  42562. _yt = yi - label.height()/2,
  42563. yt = _yt<0?0:_yt + label.height()>=height()?height() - 1-label.height():_yt,
  42564. _xt = x - 2 - label.width(),
  42565. xt = _xt>=0?_xt:x + 3;
  42566. draw_point(x - 1,yi,color,opacity).draw_point(x + 1,yi,color,opacity);
  42567. if (allow_zero || *txt!='0' || txt[1]!=0)
  42568. draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
  42569. }
  42570. }
  42571. return *this;
  42572. }
  42573. //! Draw labeled horizontal and vertical axes.
  42574. /**
  42575. \param values_x Values along the X-axis.
  42576. \param values_y Values along the Y-axis.
  42577. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  42578. \param opacity Drawing opacity.
  42579. \param pattern_x Drawing pattern for the X-axis.
  42580. \param pattern_y Drawing pattern for the Y-axis.
  42581. \param font_height Height of the labels (exact match for 13,23,53,103, interpolated otherwise).
  42582. \param allow_zero Enable/disable the drawing of label '0' if found.
  42583. **/
  42584. template<typename tx, typename ty, typename tc>
  42585. CImg<T>& draw_axes(const CImg<tx>& values_x, const CImg<ty>& values_y,
  42586. const tc *const color, const float opacity=1,
  42587. const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U,
  42588. const unsigned int font_height=13, const bool allow_zero=true) {
  42589. if (is_empty()) return *this;
  42590. const CImg<tx> nvalues_x(values_x._data,values_x.size(),1,1,1,true);
  42591. const int sizx = (int)values_x.size() - 1, wm1 = width() - 1;
  42592. if (sizx>=0) {
  42593. float ox = (float)*nvalues_x;
  42594. for (unsigned int x = sizx?1U:0U; x<_width; ++x) {
  42595. const float nx = (float)nvalues_x._linear_atX((float)x*sizx/wm1);
  42596. if (nx*ox<=0) { draw_axis(nx==0?x:x - 1,values_y,color,opacity,pattern_y,font_height,allow_zero); break; }
  42597. ox = nx;
  42598. }
  42599. }
  42600. const CImg<ty> nvalues_y(values_y._data,values_y.size(),1,1,1,true);
  42601. const int sizy = (int)values_y.size() - 1, hm1 = height() - 1;
  42602. if (sizy>0) {
  42603. float oy = (float)nvalues_y[0];
  42604. for (unsigned int y = sizy?1U:0U; y<_height; ++y) {
  42605. const float ny = (float)nvalues_y._linear_atX((float)y*sizy/hm1);
  42606. if (ny*oy<=0) { draw_axis(values_x,ny==0?y:y - 1,color,opacity,pattern_x,font_height,allow_zero); break; }
  42607. oy = ny;
  42608. }
  42609. }
  42610. return *this;
  42611. }
  42612. //! Draw labeled horizontal and vertical axes \overloading.
  42613. template<typename tc>
  42614. CImg<T>& draw_axes(const float x0, const float x1, const float y0, const float y1,
  42615. const tc *const color, const float opacity=1,
  42616. const int subdivisionx=-60, const int subdivisiony=-60,
  42617. const float precisionx=0, const float precisiony=0,
  42618. const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U,
  42619. const unsigned int font_height=13) {
  42620. if (is_empty()) return *this;
  42621. const bool allow_zero = (x0*x1>0) || (y0*y1>0);
  42622. const float
  42623. dx = cimg::abs(x1 - x0), dy = cimg::abs(y1 - y0),
  42624. px = dx<=0?1:precisionx==0?(float)std::pow(10.0,(int)std::log10(dx) - 2.0):precisionx,
  42625. py = dy<=0?1:precisiony==0?(float)std::pow(10.0,(int)std::log10(dy) - 2.0):precisiony;
  42626. if (x0!=x1 && y0!=y1)
  42627. draw_axes(CImg<floatT>::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1).round(px),
  42628. CImg<floatT>::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1).round(py),
  42629. color,opacity,pattern_x,pattern_y,font_height,allow_zero);
  42630. else if (x0==x1 && y0!=y1)
  42631. draw_axis((int)x0,CImg<floatT>::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1).round(py),
  42632. color,opacity,pattern_y,font_height);
  42633. else if (x0!=x1 && y0==y1)
  42634. draw_axis(CImg<floatT>::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1).round(px),(int)y0,
  42635. color,opacity,pattern_x,font_height);
  42636. return *this;
  42637. }
  42638. //! Draw 2d grid.
  42639. /**
  42640. \param values_x X-coordinates of the vertical lines.
  42641. \param values_y Y-coordinates of the horizontal lines.
  42642. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  42643. \param opacity Drawing opacity.
  42644. \param pattern_x Drawing pattern for vertical lines.
  42645. \param pattern_y Drawing pattern for horizontal lines.
  42646. **/
  42647. template<typename tx, typename ty, typename tc>
  42648. CImg<T>& draw_grid(const CImg<tx>& values_x, const CImg<ty>& values_y,
  42649. const tc *const color, const float opacity=1,
  42650. const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U) {
  42651. if (is_empty()) return *this;
  42652. if (values_x) cimg_foroff(values_x,x) {
  42653. const int xi = (int)values_x[x];
  42654. if (xi>=0 && xi<width()) draw_line(xi,0,xi,_height - 1,color,opacity,pattern_x);
  42655. }
  42656. if (values_y) cimg_foroff(values_y,y) {
  42657. const int yi = (int)values_y[y];
  42658. if (yi>=0 && yi<height()) draw_line(0,yi,_width - 1,yi,color,opacity,pattern_y);
  42659. }
  42660. return *this;
  42661. }
  42662. //! Draw 2d grid \simplification.
  42663. template<typename tc>
  42664. CImg<T>& draw_grid(const float delta_x, const float delta_y,
  42665. const float offsetx, const float offsety,
  42666. const bool invertx, const bool inverty,
  42667. const tc *const color, const float opacity=1,
  42668. const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U) {
  42669. if (is_empty()) return *this;
  42670. CImg<uintT> seqx, seqy;
  42671. if (delta_x!=0) {
  42672. const float dx = delta_x>0?delta_x:_width*-delta_x/100;
  42673. const unsigned int nx = (unsigned int)(_width/dx);
  42674. seqx = CImg<uintT>::sequence(1 + nx,0,(unsigned int)(dx*nx));
  42675. if (offsetx) cimg_foroff(seqx,x) seqx(x) = (unsigned int)cimg::mod(seqx(x) + offsetx,(float)_width);
  42676. if (invertx) cimg_foroff(seqx,x) seqx(x) = _width - 1 - seqx(x);
  42677. }
  42678. if (delta_y!=0) {
  42679. const float dy = delta_y>0?delta_y:_height*-delta_y/100;
  42680. const unsigned int ny = (unsigned int)(_height/dy);
  42681. seqy = CImg<uintT>::sequence(1 + ny,0,(unsigned int)(dy*ny));
  42682. if (offsety) cimg_foroff(seqy,y) seqy(y) = (unsigned int)cimg::mod(seqy(y) + offsety,(float)_height);
  42683. if (inverty) cimg_foroff(seqy,y) seqy(y) = _height - 1 - seqy(y);
  42684. }
  42685. return draw_grid(seqx,seqy,color,opacity,pattern_x,pattern_y);
  42686. }
  42687. //! Draw 1d graph.
  42688. /**
  42689. \param data Image containing the graph values I = f(x).
  42690. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  42691. \param opacity Drawing opacity.
  42692. \param plot_type Define the type of the plot:
  42693. - 0 = No plot.
  42694. - 1 = Plot using segments.
  42695. - 2 = Plot using cubic splines.
  42696. - 3 = Plot with bars.
  42697. \param vertex_type Define the type of points:
  42698. - 0 = No points.
  42699. - 1 = Point.
  42700. - 2 = Straight cross.
  42701. - 3 = Diagonal cross.
  42702. - 4 = Filled circle.
  42703. - 5 = Outlined circle.
  42704. - 6 = Square.
  42705. - 7 = Diamond.
  42706. \param ymin Lower bound of the y-range.
  42707. \param ymax Upper bound of the y-range.
  42708. \param pattern Drawing pattern.
  42709. \note
  42710. - if \c ymin==ymax==0, the y-range is computed automatically from the input samples.
  42711. **/
  42712. template<typename t, typename tc>
  42713. CImg<T>& draw_graph(const CImg<t>& data,
  42714. const tc *const color, const float opacity=1,
  42715. const unsigned int plot_type=1, const int vertex_type=1,
  42716. const double ymin=0, const double ymax=0, const unsigned int pattern=~0U) {
  42717. if (is_empty() || _height<=1) return *this;
  42718. if (!color)
  42719. throw CImgArgumentException(_cimg_instance
  42720. "draw_graph(): Specified color is (null).",
  42721. cimg_instance);
  42722. // Create shaded colors for displaying bar plots.
  42723. CImg<tc> color1, color2;
  42724. if (plot_type==3) {
  42725. color1.assign(_spectrum); color2.assign(_spectrum);
  42726. cimg_forC(*this,c) {
  42727. color1[c] = (tc)std::min((float)cimg::type<tc>::max(),(float)color[c]*1.2f);
  42728. color2[c] = (tc)(color[c]*0.4f);
  42729. }
  42730. }
  42731. // Compute min/max and normalization factors.
  42732. const ulongT
  42733. siz = data.size(),
  42734. _siz1 = siz - (plot_type!=3),
  42735. siz1 = _siz1?_siz1:1;
  42736. const unsigned int
  42737. _width1 = _width - (plot_type!=3),
  42738. width1 = _width1?_width1:1;
  42739. double m = ymin, M = ymax;
  42740. if (ymin==ymax) m = (double)data.max_min(M);
  42741. if (m==M) { --m; ++M; }
  42742. const float ca = (float)(M-m)/(_height - 1);
  42743. bool init_hatch = true;
  42744. // Draw graph edges
  42745. switch (plot_type%4) {
  42746. case 1 : { // Segments
  42747. int oX = 0, oY = (int)((data[0] - m)/ca);
  42748. if (siz==1) {
  42749. const int Y = (int)((*data - m)/ca);
  42750. draw_line(0,Y,width() - 1,Y,color,opacity,pattern);
  42751. } else {
  42752. const float fx = (float)_width/siz1;
  42753. for (ulongT off = 1; off<siz; ++off) {
  42754. const int
  42755. X = (int)(off*fx) - 1,
  42756. Y = (int)((data[off]-m)/ca);
  42757. draw_line(oX,oY,X,Y,color,opacity,pattern,init_hatch);
  42758. oX = X; oY = Y;
  42759. init_hatch = false;
  42760. }
  42761. }
  42762. } break;
  42763. case 2 : { // Spline
  42764. const CImg<t> ndata(data._data,siz,1,1,1,true);
  42765. int oY = (int)((data[0] - m)/ca);
  42766. cimg_forX(*this,x) {
  42767. const int Y = (int)((ndata._cubic_atX((float)x*siz1/width1)-m)/ca);
  42768. if (x>0) draw_line(x,oY,x + 1,Y,color,opacity,pattern,init_hatch);
  42769. init_hatch = false;
  42770. oY = Y;
  42771. }
  42772. } break;
  42773. case 3 : { // Bars
  42774. const int Y0 = (int)(-m/ca);
  42775. const float fx = (float)_width/siz1;
  42776. int oX = 0;
  42777. cimg_foroff(data,off) {
  42778. const int
  42779. X = (int)((off + 1)*fx) - 1,
  42780. Y = (int)((data[off] - m)/ca);
  42781. draw_rectangle(oX,Y0,X,Y,color,opacity).
  42782. draw_line(oX,Y,oX,Y0,color2.data(),opacity).
  42783. draw_line(oX,Y0,X,Y0,Y<=Y0?color2.data():color1.data(),opacity).
  42784. draw_line(X,Y,X,Y0,color1.data(),opacity).
  42785. draw_line(oX,Y,X,Y,Y<=Y0?color1.data():color2.data(),opacity);
  42786. oX = X + 1;
  42787. }
  42788. } break;
  42789. default : break; // No edges
  42790. }
  42791. // Draw graph points
  42792. const unsigned int wb2 = plot_type==3?_width1/(2*siz):0;
  42793. const float fx = (float)_width1/siz1;
  42794. switch (vertex_type%8) {
  42795. case 1 : { // Point
  42796. cimg_foroff(data,off) {
  42797. const int
  42798. X = (int)(off*fx + wb2),
  42799. Y = (int)((data[off]-m)/ca);
  42800. draw_point(X,Y,color,opacity);
  42801. }
  42802. } break;
  42803. case 2 : { // Straight Cross
  42804. cimg_foroff(data,off) {
  42805. const int
  42806. X = (int)(off*fx + wb2),
  42807. Y = (int)((data[off]-m)/ca);
  42808. draw_line(X - 3,Y,X + 3,Y,color,opacity).draw_line(X,Y - 3,X,Y + 3,color,opacity);
  42809. }
  42810. } break;
  42811. case 3 : { // Diagonal Cross
  42812. cimg_foroff(data,off) {
  42813. const int
  42814. X = (int)(off*fx + wb2),
  42815. Y = (int)((data[off]-m)/ca);
  42816. draw_line(X - 3,Y - 3,X + 3,Y + 3,color,opacity).draw_line(X - 3,Y + 3,X + 3,Y - 3,color,opacity);
  42817. }
  42818. } break;
  42819. case 4 : { // Filled Circle
  42820. cimg_foroff(data,off) {
  42821. const int
  42822. X = (int)(off*fx + wb2),
  42823. Y = (int)((data[off]-m)/ca);
  42824. draw_circle(X,Y,3,color,opacity);
  42825. }
  42826. } break;
  42827. case 5 : { // Outlined circle
  42828. cimg_foroff(data,off) {
  42829. const int
  42830. X = (int)(off*fx + wb2),
  42831. Y = (int)((data[off]-m)/ca);
  42832. draw_circle(X,Y,3,color,opacity,0U);
  42833. }
  42834. } break;
  42835. case 6 : { // Square
  42836. cimg_foroff(data,off) {
  42837. const int
  42838. X = (int)(off*fx + wb2),
  42839. Y = (int)((data[off]-m)/ca);
  42840. draw_rectangle(X - 3,Y - 3,X + 3,Y + 3,color,opacity,~0U);
  42841. }
  42842. } break;
  42843. case 7 : { // Diamond
  42844. cimg_foroff(data,off) {
  42845. const int
  42846. X = (int)(off*fx + wb2),
  42847. Y = (int)((data[off]-m)/ca);
  42848. draw_line(X,Y - 4,X + 4,Y,color,opacity).
  42849. draw_line(X + 4,Y,X,Y + 4,color,opacity).
  42850. draw_line(X,Y + 4,X - 4,Y,color,opacity).
  42851. draw_line(X - 4,Y,X,Y - 4,color,opacity);
  42852. }
  42853. } break;
  42854. default : break; // No points
  42855. }
  42856. return *this;
  42857. }
  42858. bool _draw_fill(const int x, const int y, const int z,
  42859. const CImg<T>& ref, const float tolerance2) const {
  42860. const T *ptr1 = data(x,y,z), *ptr2 = ref._data;
  42861. const unsigned long off = _width*_height*_depth;
  42862. float diff = 0;
  42863. cimg_forC(*this,c) { diff += cimg::sqr(*ptr1 - *(ptr2++)); ptr1+=off; }
  42864. return diff<=tolerance2;
  42865. }
  42866. //! Draw filled 3d region with the flood fill algorithm.
  42867. /**
  42868. \param x0 X-coordinate of the starting point of the region to fill.
  42869. \param y0 Y-coordinate of the starting point of the region to fill.
  42870. \param z0 Z-coordinate of the starting point of the region to fill.
  42871. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  42872. \param[out] region Image that will contain the mask of the filled region mask, as an output.
  42873. \param tolerance Tolerance concerning neighborhood values.
  42874. \param opacity Opacity of the drawing.
  42875. \param is_high_connectivity Tells if 8-connexity must be used.
  42876. \return \c region is initialized with the binary mask of the filled region.
  42877. **/
  42878. template<typename tc, typename t>
  42879. CImg<T>& draw_fill(const int x0, const int y0, const int z0,
  42880. const tc *const color, const float opacity,
  42881. CImg<t> &region,
  42882. const float tolerance = 0,
  42883. const bool is_high_connectivity = false) {
  42884. #define _draw_fill_push(x,y,z) if (N>=stack._width) stack.resize(2*N + 1,1,1,3,0); \
  42885. stack[N] = x; stack(N,1) = y; stack(N++,2) = z
  42886. #define _draw_fill_pop(x,y,z) x = stack[--N]; y = stack(N,1); z = stack(N,2)
  42887. #define _draw_fill_is_inside(x,y,z) !_region(x,y,z) && _draw_fill(x,y,z,ref,tolerance2)
  42888. if (!containsXYZC(x0,y0,z0,0)) return *this;
  42889. const float nopacity = cimg::abs((float)opacity), copacity = 1 - std::max((float)opacity,0.0f);
  42890. const float tolerance2 = cimg::sqr(tolerance);
  42891. const CImg<T> ref = get_vector_at(x0,y0,z0);
  42892. CImg<uintT> stack(256,1,1,3);
  42893. CImg<ucharT> _region(_width,_height,_depth,1,0);
  42894. unsigned int N = 0;
  42895. int x, y, z;
  42896. _draw_fill_push(x0,y0,z0);
  42897. while (N>0) {
  42898. _draw_fill_pop(x,y,z);
  42899. if (!_region(x,y,z)) {
  42900. const int yp = y - 1, yn = y + 1, zp = z - 1, zn = z + 1;
  42901. int xl = x, xr = x;
  42902. // Using these booleans reduces the number of pushes drastically.
  42903. bool is_yp = false, is_yn = false, is_zp = false, is_zn = false;
  42904. for (int step = -1; step<2; step+=2) {
  42905. while (x>=0 && x<width() && _draw_fill_is_inside(x,y,z)) {
  42906. if (yp>=0 && _draw_fill_is_inside(x,yp,z)) {
  42907. if (!is_yp) { _draw_fill_push(x,yp,z); is_yp = true; }
  42908. } else is_yp = false;
  42909. if (yn<height() && _draw_fill_is_inside(x,yn,z)) {
  42910. if (!is_yn) { _draw_fill_push(x,yn,z); is_yn = true; }
  42911. } else is_yn = false;
  42912. if (depth()>1) {
  42913. if (zp>=0 && _draw_fill_is_inside(x,y,zp)) {
  42914. if (!is_zp) { _draw_fill_push(x,y,zp); is_zp = true; }
  42915. } else is_zp = false;
  42916. if (zn<depth() && _draw_fill_is_inside(x,y,zn)) {
  42917. if (!is_zn) { _draw_fill_push(x,y,zn); is_zn = true; }
  42918. } else is_zn = false;
  42919. }
  42920. if (is_high_connectivity) {
  42921. const int xp = x - 1, xn = x + 1;
  42922. if (yp>=0 && !is_yp) {
  42923. if (xp>=0 && _draw_fill_is_inside(xp,yp,z)) {
  42924. _draw_fill_push(xp,yp,z); if (step<0) is_yp = true;
  42925. }
  42926. if (xn<width() && _draw_fill_is_inside(xn,yp,z)) {
  42927. _draw_fill_push(xn,yp,z); if (step>0) is_yp = true;
  42928. }
  42929. }
  42930. if (yn<height() && !is_yn) {
  42931. if (xp>=0 && _draw_fill_is_inside(xp,yn,z)) {
  42932. _draw_fill_push(xp,yn,z); if (step<0) is_yn = true;
  42933. }
  42934. if (xn<width() && _draw_fill_is_inside(xn,yn,z)) {
  42935. _draw_fill_push(xn,yn,z); if (step>0) is_yn = true;
  42936. }
  42937. }
  42938. if (depth()>1) {
  42939. if (zp>=0 && !is_zp) {
  42940. if (xp>=0 && _draw_fill_is_inside(xp,y,zp)) {
  42941. _draw_fill_push(xp,y,zp); if (step<0) is_zp = true;
  42942. }
  42943. if (xn<width() && _draw_fill_is_inside(xn,y,zp)) {
  42944. _draw_fill_push(xn,y,zp); if (step>0) is_zp = true;
  42945. }
  42946. if (yp>=0 && !is_yp) {
  42947. if (_draw_fill_is_inside(x,yp,zp)) { _draw_fill_push(x,yp,zp); }
  42948. if (xp>=0 && _draw_fill_is_inside(xp,yp,zp)) { _draw_fill_push(xp,yp,zp); }
  42949. if (xn<width() && _draw_fill_is_inside(xn,yp,zp)) { _draw_fill_push(xn,yp,zp); }
  42950. }
  42951. if (yn<height() && !is_yn) {
  42952. if (_draw_fill_is_inside(x,yn,zp)) { _draw_fill_push(x,yn,zp); }
  42953. if (xp>=0 && _draw_fill_is_inside(xp,yn,zp)) { _draw_fill_push(xp,yn,zp); }
  42954. if (xn<width() && _draw_fill_is_inside(xn,yn,zp)) { _draw_fill_push(xn,yn,zp); }
  42955. }
  42956. }
  42957. if (zn<depth() && !is_zn) {
  42958. if (xp>=0 && _draw_fill_is_inside(xp,y,zn)) {
  42959. _draw_fill_push(xp,y,zn); if (step<0) is_zn = true;
  42960. }
  42961. if (xn<width() && _draw_fill_is_inside(xn,y,zn)) {
  42962. _draw_fill_push(xn,y,zn); if (step>0) is_zn = true;
  42963. }
  42964. if (yp>=0 && !is_yp) {
  42965. if (_draw_fill_is_inside(x,yp,zn)) { _draw_fill_push(x,yp,zn); }
  42966. if (xp>=0 && _draw_fill_is_inside(xp,yp,zn)) { _draw_fill_push(xp,yp,zn); }
  42967. if (xn<width() && _draw_fill_is_inside(xn,yp,zn)) { _draw_fill_push(xn,yp,zn); }
  42968. }
  42969. if (yn<height() && !is_yn) {
  42970. if (_draw_fill_is_inside(x,yn,zn)) { _draw_fill_push(x,yn,zn); }
  42971. if (xp>=0 && _draw_fill_is_inside(xp,yn,zn)) { _draw_fill_push(xp,yn,zn); }
  42972. if (xn<width() && _draw_fill_is_inside(xn,yn,zn)) { _draw_fill_push(xn,yn,zn); }
  42973. }
  42974. }
  42975. }
  42976. }
  42977. x+=step;
  42978. }
  42979. if (step<0) { xl = ++x; x = xr + 1; is_yp = is_yn = is_zp = is_zn = false; }
  42980. else xr = --x;
  42981. }
  42982. std::memset(_region.data(xl,y,z),1,xr - xl + 1);
  42983. if (opacity==1) {
  42984. if (sizeof(T)==1) {
  42985. const int dx = xr - xl + 1;
  42986. cimg_forC(*this,c) std::memset(data(xl,y,z,c),(int)color[c],dx);
  42987. } else cimg_forC(*this,c) {
  42988. const T val = (T)color[c];
  42989. T *ptri = data(xl,y,z,c); for (int k = xl; k<=xr; ++k) *(ptri++) = val;
  42990. }
  42991. } else cimg_forC(*this,c) {
  42992. const T val = (T)(color[c]*nopacity);
  42993. T *ptri = data(xl,y,z,c); for (int k = xl; k<=xr; ++k) { *ptri = (T)(val + *ptri*copacity); ++ptri; }
  42994. }
  42995. }
  42996. }
  42997. _region.move_to(region);
  42998. return *this;
  42999. }
  43000. //! Draw filled 3d region with the flood fill algorithm \simplification.
  43001. template<typename tc>
  43002. CImg<T>& draw_fill(const int x0, const int y0, const int z0,
  43003. const tc *const color, const float opacity=1,
  43004. const float tolerance=0, const bool is_high_connexity=false) {
  43005. CImg<ucharT> tmp;
  43006. return draw_fill(x0,y0,z0,color,opacity,tmp,tolerance,is_high_connexity);
  43007. }
  43008. //! Draw filled 2d region with the flood fill algorithm \simplification.
  43009. template<typename tc>
  43010. CImg<T>& draw_fill(const int x0, const int y0,
  43011. const tc *const color, const float opacity=1,
  43012. const float tolerance=0, const bool is_high_connexity=false) {
  43013. CImg<ucharT> tmp;
  43014. return draw_fill(x0,y0,0,color,opacity,tmp,tolerance,is_high_connexity);
  43015. }
  43016. //! Draw a random plasma texture.
  43017. /**
  43018. \param alpha Alpha-parameter.
  43019. \param beta Beta-parameter.
  43020. \param scale Scale-parameter.
  43021. \note Use the mid-point algorithm to render.
  43022. **/
  43023. CImg<T>& draw_plasma(const float alpha=1, const float beta=0, const unsigned int scale=8) {
  43024. if (is_empty()) return *this;
  43025. const int w = width(), h = height();
  43026. const Tfloat m = (Tfloat)cimg::type<T>::min(), M = (Tfloat)cimg::type<T>::max();
  43027. cimg_forZC(*this,z,c) {
  43028. CImg<T> ref = get_shared_slice(z,c);
  43029. for (int delta = 1<<std::min(scale,31U); delta>1; delta>>=1) {
  43030. const int delta2 = delta>>1;
  43031. const float r = alpha*delta + beta;
  43032. // Square step.
  43033. for (int y0 = 0; y0<h; y0+=delta)
  43034. for (int x0 = 0; x0<w; x0+=delta) {
  43035. const int x1 = (x0 + delta)%w, y1 = (y0 + delta)%h, xc = (x0 + delta2)%w, yc = (y0 + delta2)%h;
  43036. const Tfloat val = (Tfloat)(0.25f*(ref(x0,y0) + ref(x0,y1) + ref(x0,y1) + ref(x1,y1)) +
  43037. r*cimg::rand(-1,1));
  43038. ref(xc,yc) = (T)(val<m?m:val>M?M:val);
  43039. }
  43040. // Diamond steps.
  43041. for (int y = -delta2; y<h; y+=delta)
  43042. for (int x0=0; x0<w; x0+=delta) {
  43043. const int y0 = cimg::mod(y,h), x1 = (x0 + delta)%w, y1 = (y + delta)%h,
  43044. xc = (x0 + delta2)%w, yc = (y + delta2)%h;
  43045. const Tfloat val = (Tfloat)(0.25f*(ref(xc,y0) + ref(x0,yc) + ref(xc,y1) + ref(x1,yc)) +
  43046. r*cimg::rand(-1,1));
  43047. ref(xc,yc) = (T)(val<m?m:val>M?M:val);
  43048. }
  43049. for (int y0 = 0; y0<h; y0+=delta)
  43050. for (int x = -delta2; x<w; x+=delta) {
  43051. const int x0 = cimg::mod(x,w), x1 = (x + delta)%w, y1 = (y0 + delta)%h,
  43052. xc = (x + delta2)%w, yc = (y0 + delta2)%h;
  43053. const Tfloat val = (Tfloat)(0.25f*(ref(xc,y0) + ref(x0,yc) + ref(xc,y1) + ref(x1,yc)) +
  43054. r*cimg::rand(-1,1));
  43055. ref(xc,yc) = (T)(val<m?m:val>M?M:val);
  43056. }
  43057. for (int y = -delta2; y<h; y+=delta)
  43058. for (int x = -delta2; x<w; x+=delta) {
  43059. const int x0 = cimg::mod(x,w), y0 = cimg::mod(y,h), x1 = (x + delta)%w, y1 = (y + delta)%h,
  43060. xc = (x + delta2)%w, yc = (y + delta2)%h;
  43061. const Tfloat val = (Tfloat)(0.25f*(ref(xc,y0) + ref(x0,yc) + ref(xc,y1) + ref(x1,yc)) +
  43062. r*cimg::rand(-1,1));
  43063. ref(xc,yc) = (T)(val<m?m:val>M?M:val);
  43064. }
  43065. }
  43066. }
  43067. return *this;
  43068. }
  43069. //! Draw a quadratic Mandelbrot or Julia 2d fractal.
  43070. /**
  43071. \param x0 X-coordinate of the upper-left pixel.
  43072. \param y0 Y-coordinate of the upper-left pixel.
  43073. \param x1 X-coordinate of the lower-right pixel.
  43074. \param y1 Y-coordinate of the lower-right pixel.
  43075. \param colormap Colormap.
  43076. \param opacity Drawing opacity.
  43077. \param z0r Real part of the upper-left fractal vertex.
  43078. \param z0i Imaginary part of the upper-left fractal vertex.
  43079. \param z1r Real part of the lower-right fractal vertex.
  43080. \param z1i Imaginary part of the lower-right fractal vertex.
  43081. \param iteration_max Maximum number of iterations for each estimated point.
  43082. \param is_normalized_iteration Tells if iterations are normalized.
  43083. \param is_julia_set Tells if the Mandelbrot or Julia set is rendered.
  43084. \param param_r Real part of the Julia set parameter.
  43085. \param param_i Imaginary part of the Julia set parameter.
  43086. \note Fractal rendering is done by the Escape Time Algorithm.
  43087. **/
  43088. template<typename tc>
  43089. CImg<T>& draw_mandelbrot(const int x0, const int y0, const int x1, const int y1,
  43090. const CImg<tc>& colormap, const float opacity=1,
  43091. const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2,
  43092. const unsigned int iteration_max=255,
  43093. const bool is_normalized_iteration=false,
  43094. const bool is_julia_set=false,
  43095. const double param_r=0, const double param_i=0) {
  43096. if (is_empty()) return *this;
  43097. CImg<tc> palette;
  43098. if (colormap) palette.assign(colormap._data,colormap.size()/colormap._spectrum,1,1,colormap._spectrum,true);
  43099. if (palette && palette._spectrum!=_spectrum)
  43100. throw CImgArgumentException(_cimg_instance
  43101. "draw_mandelbrot(): Instance and specified colormap (%u,%u,%u,%u,%p) have "
  43102. "incompatible dimensions.",
  43103. cimg_instance,
  43104. colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data);
  43105. const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f), ln2 = (float)std::log(2.0);
  43106. const int
  43107. _x0 = cimg::cut(x0,0,width() - 1),
  43108. _y0 = cimg::cut(y0,0,height() - 1),
  43109. _x1 = cimg::cut(x1,0,width() - 1),
  43110. _y1 = cimg::cut(y1,0,height() - 1);
  43111. cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if((1 + _x1 - _x0)*(1 + _y1 - _y0)>=2048))
  43112. for (int q = _y0; q<=_y1; ++q)
  43113. for (int p = _x0; p<=_x1; ++p) {
  43114. unsigned int iteration = 0;
  43115. const double x = z0r + p*(z1r-z0r)/_width, y = z0i + q*(z1i-z0i)/_height;
  43116. double zr, zi, cr, ci;
  43117. if (is_julia_set) { zr = x; zi = y; cr = param_r; ci = param_i; }
  43118. else { zr = param_r; zi = param_i; cr = x; ci = y; }
  43119. for (iteration=1; zr*zr + zi*zi<=4 && iteration<=iteration_max; ++iteration) {
  43120. const double temp = zr*zr - zi*zi + cr;
  43121. zi = 2*zr*zi + ci;
  43122. zr = temp;
  43123. }
  43124. if (iteration>iteration_max) {
  43125. if (palette) {
  43126. if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette(0,c);
  43127. else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette(0,c)*nopacity + (*this)(p,q,0,c)*copacity);
  43128. } else {
  43129. if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)0;
  43130. else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)((*this)(p,q,0,c)*copacity);
  43131. }
  43132. } else if (is_normalized_iteration) {
  43133. const float
  43134. normz = (float)cimg::abs(zr*zr + zi*zi),
  43135. niteration = (float)(iteration + 1 - std::log(std::log(normz))/ln2);
  43136. if (palette) {
  43137. if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette._linear_atX(niteration,c);
  43138. else cimg_forC(*this,c)
  43139. (*this)(p,q,0,c) = (T)(palette._linear_atX(niteration,c)*nopacity + (*this)(p,q,0,c)*copacity);
  43140. } else {
  43141. if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)niteration;
  43142. else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(niteration*nopacity + (*this)(p,q,0,c)*copacity);
  43143. }
  43144. } else {
  43145. if (palette) {
  43146. if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette._atX(iteration,c);
  43147. else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette(iteration,c)*nopacity + (*this)(p,q,0,c)*copacity);
  43148. } else {
  43149. if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)iteration;
  43150. else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(iteration*nopacity + (*this)(p,q,0,c)*copacity);
  43151. }
  43152. }
  43153. }
  43154. return *this;
  43155. }
  43156. //! Draw a quadratic Mandelbrot or Julia 2d fractal \overloading.
  43157. template<typename tc>
  43158. CImg<T>& draw_mandelbrot(const CImg<tc>& colormap, const float opacity=1,
  43159. const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2,
  43160. const unsigned int iteration_max=255,
  43161. const bool is_normalized_iteration=false,
  43162. const bool is_julia_set=false,
  43163. const double param_r=0, const double param_i=0) {
  43164. return draw_mandelbrot(0,0,_width - 1,_height - 1,colormap,opacity,
  43165. z0r,z0i,z1r,z1i,iteration_max,is_normalized_iteration,is_julia_set,param_r,param_i);
  43166. }
  43167. //! Draw a 1d gaussian function.
  43168. /**
  43169. \param xc X-coordinate of the gaussian center.
  43170. \param sigma Standard variation of the gaussian distribution.
  43171. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  43172. \param opacity Drawing opacity.
  43173. **/
  43174. template<typename tc>
  43175. CImg<T>& draw_gaussian(const float xc, const float sigma,
  43176. const tc *const color, const float opacity=1) {
  43177. if (is_empty()) return *this;
  43178. if (!color)
  43179. throw CImgArgumentException(_cimg_instance
  43180. "draw_gaussian(): Specified color is (null).",
  43181. cimg_instance);
  43182. const float sigma2 = 2*sigma*sigma, nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
  43183. const ulongT whd = (ulongT)_width*_height*_depth;
  43184. const tc *col = color;
  43185. cimg_forX(*this,x) {
  43186. const float dx = (x - xc), val = (float)std::exp(-dx*dx/sigma2);
  43187. T *ptrd = data(x,0,0,0);
  43188. if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
  43189. else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
  43190. col-=_spectrum;
  43191. }
  43192. return *this;
  43193. }
  43194. //! Draw a 2d gaussian function.
  43195. /**
  43196. \param xc X-coordinate of the gaussian center.
  43197. \param yc Y-coordinate of the gaussian center.
  43198. \param tensor Covariance matrix (must be 2x2).
  43199. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  43200. \param opacity Drawing opacity.
  43201. **/
  43202. template<typename t, typename tc>
  43203. CImg<T>& draw_gaussian(const float xc, const float yc, const CImg<t>& tensor,
  43204. const tc *const color, const float opacity=1) {
  43205. if (is_empty()) return *this;
  43206. if (tensor._width!=2 || tensor._height!=2 || tensor._depth!=1 || tensor._spectrum!=1)
  43207. throw CImgArgumentException(_cimg_instance
  43208. "draw_gaussian(): Specified tensor (%u,%u,%u,%u,%p) is not a 2x2 matrix.",
  43209. cimg_instance,
  43210. tensor._width,tensor._height,tensor._depth,tensor._spectrum,tensor._data);
  43211. if (!color)
  43212. throw CImgArgumentException(_cimg_instance
  43213. "draw_gaussian(): Specified color is (null).",
  43214. cimg_instance);
  43215. typedef typename CImg<t>::Tfloat tfloat;
  43216. const CImg<tfloat> invT = tensor.get_invert(), invT2 = (invT*invT)/(-2.0);
  43217. const tfloat a = invT2(0,0), b = 2*invT2(1,0), c = invT2(1,1);
  43218. const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
  43219. const ulongT whd = (ulongT)_width*_height*_depth;
  43220. const tc *col = color;
  43221. float dy = -yc;
  43222. cimg_forY(*this,y) {
  43223. float dx = -xc;
  43224. cimg_forX(*this,x) {
  43225. const float val = (float)std::exp(a*dx*dx + b*dx*dy + c*dy*dy);
  43226. T *ptrd = data(x,y,0,0);
  43227. if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
  43228. else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
  43229. col-=_spectrum;
  43230. ++dx;
  43231. }
  43232. ++dy;
  43233. }
  43234. return *this;
  43235. }
  43236. //! Draw a 2d gaussian function \overloading.
  43237. template<typename tc>
  43238. CImg<T>& draw_gaussian(const int xc, const int yc, const float r1, const float r2, const float ru, const float rv,
  43239. const tc *const color, const float opacity=1) {
  43240. const double
  43241. a = r1*ru*ru + r2*rv*rv,
  43242. b = (r1-r2)*ru*rv,
  43243. c = r1*rv*rv + r2*ru*ru;
  43244. const CImg<Tfloat> tensor(2,2,1,1, a,b,b,c);
  43245. return draw_gaussian(xc,yc,tensor,color,opacity);
  43246. }
  43247. //! Draw a 2d gaussian function \overloading.
  43248. template<typename tc>
  43249. CImg<T>& draw_gaussian(const float xc, const float yc, const float sigma,
  43250. const tc *const color, const float opacity=1) {
  43251. return draw_gaussian(xc,yc,CImg<floatT>::diagonal(sigma,sigma),color,opacity);
  43252. }
  43253. //! Draw a 3d gaussian function \overloading.
  43254. template<typename t, typename tc>
  43255. CImg<T>& draw_gaussian(const float xc, const float yc, const float zc, const CImg<t>& tensor,
  43256. const tc *const color, const float opacity=1) {
  43257. if (is_empty()) return *this;
  43258. typedef typename CImg<t>::Tfloat tfloat;
  43259. if (tensor._width!=3 || tensor._height!=3 || tensor._depth!=1 || tensor._spectrum!=1)
  43260. throw CImgArgumentException(_cimg_instance
  43261. "draw_gaussian(): Specified tensor (%u,%u,%u,%u,%p) is not a 3x3 matrix.",
  43262. cimg_instance,
  43263. tensor._width,tensor._height,tensor._depth,tensor._spectrum,tensor._data);
  43264. const CImg<tfloat> invT = tensor.get_invert(), invT2 = (invT*invT)/(-2.0);
  43265. 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);
  43266. const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
  43267. const ulongT whd = (ulongT)_width*_height*_depth;
  43268. const tc *col = color;
  43269. cimg_forXYZ(*this,x,y,z) {
  43270. const float
  43271. dx = (x - xc), dy = (y - yc), dz = (z - zc),
  43272. val = (float)std::exp(a*dx*dx + b*dx*dy + c*dx*dz + d*dy*dy + e*dy*dz + f*dz*dz);
  43273. T *ptrd = data(x,y,z,0);
  43274. if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
  43275. else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
  43276. col-=_spectrum;
  43277. }
  43278. return *this;
  43279. }
  43280. //! Draw a 3d gaussian function \overloading.
  43281. template<typename tc>
  43282. CImg<T>& draw_gaussian(const float xc, const float yc, const float zc, const float sigma,
  43283. const tc *const color, const float opacity=1) {
  43284. return draw_gaussian(xc,yc,zc,CImg<floatT>::diagonal(sigma,sigma,sigma),color,opacity);
  43285. }
  43286. //! Draw a 3d object.
  43287. /**
  43288. \param x0 X-coordinate of the 3d object position
  43289. \param y0 Y-coordinate of the 3d object position
  43290. \param z0 Z-coordinate of the 3d object position
  43291. \param vertices Image Nx3 describing 3d point coordinates
  43292. \param primitives List of P primitives
  43293. \param colors List of P color (or textures)
  43294. \param opacities Image or list of P opacities
  43295. \param render_type d Render type (0=Points, 1=Lines, 2=Faces (no light), 3=Faces (flat), 4=Faces(Gouraud)
  43296. \param is_double_sided Tells if object faces have two sides or are oriented.
  43297. \param focale length of the focale (0 for parallel projection)
  43298. \param lightx X-coordinate of the light
  43299. \param lighty Y-coordinate of the light
  43300. \param lightz Z-coordinate of the light
  43301. \param specular_lightness Amount of specular light.
  43302. \param specular_shininess Shininess of the object
  43303. **/
  43304. template<typename tp, typename tf, typename tc, typename to>
  43305. CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
  43306. const CImg<tp>& vertices, const CImgList<tf>& primitives,
  43307. const CImgList<tc>& colors, const CImg<to>& opacities,
  43308. const unsigned int render_type=4,
  43309. const bool is_double_sided=false, const float focale=700,
  43310. const float lightx=0, const float lighty=0, const float lightz=-5e8,
  43311. const float specular_lightness=0.2f, const float specular_shininess=0.1f) {
  43312. return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type,
  43313. is_double_sided,focale,lightx,lighty,lightz,
  43314. specular_lightness,specular_shininess,CImg<floatT>::empty());
  43315. }
  43316. //! Draw a 3d object \simplification.
  43317. template<typename tp, typename tf, typename tc, typename to, typename tz>
  43318. CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
  43319. const CImg<tp>& vertices, const CImgList<tf>& primitives,
  43320. const CImgList<tc>& colors, const CImg<to>& opacities,
  43321. const unsigned int render_type,
  43322. const bool is_double_sided, const float focale,
  43323. const float lightx, const float lighty, const float lightz,
  43324. const float specular_lightness, const float specular_shininess,
  43325. CImg<tz>& zbuffer) {
  43326. return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
  43327. render_type,is_double_sided,focale,lightx,lighty,lightz,
  43328. specular_lightness,specular_shininess,1);
  43329. }
  43330. #ifdef cimg_use_board
  43331. template<typename tp, typename tf, typename tc, typename to>
  43332. CImg<T>& draw_object3d(LibBoard::Board& board,
  43333. const float x0, const float y0, const float z0,
  43334. const CImg<tp>& vertices, const CImgList<tf>& primitives,
  43335. const CImgList<tc>& colors, const CImg<to>& opacities,
  43336. const unsigned int render_type=4,
  43337. const bool is_double_sided=false, const float focale=700,
  43338. const float lightx=0, const float lighty=0, const float lightz=-5e8,
  43339. const float specular_lightness=0.2f, const float specular_shininess=0.1f) {
  43340. return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type,
  43341. is_double_sided,focale,lightx,lighty,lightz,
  43342. specular_lightness,specular_shininess,CImg<floatT>::empty());
  43343. }
  43344. template<typename tp, typename tf, typename tc, typename to, typename tz>
  43345. CImg<T>& draw_object3d(LibBoard::Board& board,
  43346. const float x0, const float y0, const float z0,
  43347. const CImg<tp>& vertices, const CImgList<tf>& primitives,
  43348. const CImgList<tc>& colors, const CImg<to>& opacities,
  43349. const unsigned int render_type,
  43350. const bool is_double_sided, const float focale,
  43351. const float lightx, const float lighty, const float lightz,
  43352. const float specular_lightness, const float specular_shininess,
  43353. CImg<tz>& zbuffer) {
  43354. return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
  43355. render_type,is_double_sided,focale,lightx,lighty,lightz,
  43356. specular_lightness,specular_shininess,1);
  43357. }
  43358. #endif
  43359. //! Draw a 3d object \simplification.
  43360. template<typename tp, typename tf, typename tc, typename to>
  43361. CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
  43362. const CImg<tp>& vertices, const CImgList<tf>& primitives,
  43363. const CImgList<tc>& colors, const CImgList<to>& opacities,
  43364. const unsigned int render_type=4,
  43365. const bool is_double_sided=false, const float focale=700,
  43366. const float lightx=0, const float lighty=0, const float lightz=-5e8,
  43367. const float specular_lightness=0.2f, const float specular_shininess=0.1f) {
  43368. return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type,
  43369. is_double_sided,focale,lightx,lighty,lightz,
  43370. specular_lightness,specular_shininess,CImg<floatT>::empty());
  43371. }
  43372. //! Draw a 3d object \simplification.
  43373. template<typename tp, typename tf, typename tc, typename to, typename tz>
  43374. CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
  43375. const CImg<tp>& vertices, const CImgList<tf>& primitives,
  43376. const CImgList<tc>& colors, const CImgList<to>& opacities,
  43377. const unsigned int render_type,
  43378. const bool is_double_sided, const float focale,
  43379. const float lightx, const float lighty, const float lightz,
  43380. const float specular_lightness, const float specular_shininess,
  43381. CImg<tz>& zbuffer) {
  43382. return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
  43383. render_type,is_double_sided,focale,lightx,lighty,lightz,
  43384. specular_lightness,specular_shininess,1);
  43385. }
  43386. #ifdef cimg_use_board
  43387. template<typename tp, typename tf, typename tc, typename to>
  43388. CImg<T>& draw_object3d(LibBoard::Board& board,
  43389. const float x0, const float y0, const float z0,
  43390. const CImg<tp>& vertices, const CImgList<tf>& primitives,
  43391. const CImgList<tc>& colors, const CImgList<to>& opacities,
  43392. const unsigned int render_type=4,
  43393. const bool is_double_sided=false, const float focale=700,
  43394. const float lightx=0, const float lighty=0, const float lightz=-5e8,
  43395. const float specular_lightness=0.2f, const float specular_shininess=0.1f) {
  43396. return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type,
  43397. is_double_sided,focale,lightx,lighty,lightz,
  43398. specular_lightness,specular_shininess,CImg<floatT>::empty());
  43399. }
  43400. template<typename tp, typename tf, typename tc, typename to, typename tz>
  43401. CImg<T>& draw_object3d(LibBoard::Board& board,
  43402. const float x0, const float y0, const float z0,
  43403. const CImg<tp>& vertices, const CImgList<tf>& primitives,
  43404. const CImgList<tc>& colors, const CImgList<to>& opacities,
  43405. const unsigned int render_type,
  43406. const bool is_double_sided, const float focale,
  43407. const float lightx, const float lighty, const float lightz,
  43408. const float specular_lightness, const float specular_shininess,
  43409. CImg<tz>& zbuffer) {
  43410. return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
  43411. render_type,is_double_sided,focale,lightx,lighty,lightz,
  43412. specular_lightness,specular_shininess,1);
  43413. }
  43414. #endif
  43415. //! Draw a 3d object \simplification.
  43416. template<typename tp, typename tf, typename tc>
  43417. CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
  43418. const CImg<tp>& vertices, const CImgList<tf>& primitives,
  43419. const CImgList<tc>& colors,
  43420. const unsigned int render_type=4,
  43421. const bool is_double_sided=false, const float focale=700,
  43422. const float lightx=0, const float lighty=0, const float lightz=-5e8,
  43423. const float specular_lightness=0.2f, const float specular_shininess=0.1f) {
  43424. return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::const_empty(),
  43425. render_type,is_double_sided,focale,lightx,lighty,lightz,
  43426. specular_lightness,specular_shininess,CImg<floatT>::empty());
  43427. }
  43428. //! Draw a 3d object \simplification.
  43429. template<typename tp, typename tf, typename tc, typename tz>
  43430. CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
  43431. const CImg<tp>& vertices, const CImgList<tf>& primitives,
  43432. const CImgList<tc>& colors,
  43433. const unsigned int render_type,
  43434. const bool is_double_sided, const float focale,
  43435. const float lightx, const float lighty, const float lightz,
  43436. const float specular_lightness, const float specular_shininess,
  43437. CImg<tz>& zbuffer) {
  43438. return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::const_empty(),
  43439. render_type,is_double_sided,focale,lightx,lighty,lightz,
  43440. specular_lightness,specular_shininess,zbuffer);
  43441. }
  43442. #ifdef cimg_use_board
  43443. template<typename tp, typename tf, typename tc, typename to>
  43444. CImg<T>& draw_object3d(LibBoard::Board& board,
  43445. const float x0, const float y0, const float z0,
  43446. const CImg<tp>& vertices, const CImgList<tf>& primitives,
  43447. const CImgList<tc>& colors,
  43448. const unsigned int render_type=4,
  43449. const bool is_double_sided=false, const float focale=700,
  43450. const float lightx=0, const float lighty=0, const float lightz=-5e8,
  43451. const float specular_lightness=0.2f, const float specular_shininess=0.1f) {
  43452. return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::const_empty(),
  43453. render_type,is_double_sided,focale,lightx,lighty,lightz,
  43454. specular_lightness,specular_shininess,CImg<floatT>::empty());
  43455. }
  43456. template<typename tp, typename tf, typename tc, typename to, typename tz>
  43457. CImg<T>& draw_object3d(LibBoard::Board& board,
  43458. const float x0, const float y0, const float z0,
  43459. const CImg<tp>& vertices, const CImgList<tf>& primitives,
  43460. const CImgList<tc>& colors,
  43461. const unsigned int render_type,
  43462. const bool is_double_sided, const float focale,
  43463. const float lightx, const float lighty, const float lightz,
  43464. const float specular_lightness, const float specular_shininess,
  43465. CImg<tz>& zbuffer) {
  43466. return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::const_empty(),
  43467. render_type,is_double_sided,focale,lightx,lighty,lightz,
  43468. specular_lightness,specular_shininess,zbuffer);
  43469. }
  43470. #endif
  43471. template<typename t, typename to>
  43472. static float __draw_object3d(const CImgList<t>& opacities, const unsigned int n_primitive, CImg<to>& opacity) {
  43473. if (n_primitive>=opacities._width || opacities[n_primitive].is_empty()) { opacity.assign(); return 1; }
  43474. if (opacities[n_primitive].size()==1) { opacity.assign(); return opacities(n_primitive,0); }
  43475. opacity.assign(opacities[n_primitive],true);
  43476. return 1.0f;
  43477. }
  43478. template<typename t, typename to>
  43479. static float __draw_object3d(const CImg<t>& opacities, const unsigned int n_primitive, CImg<to>& opacity) {
  43480. opacity.assign();
  43481. return n_primitive>=opacities._width?1.0f:(float)opacities[n_primitive];
  43482. }
  43483. template<typename t>
  43484. static float ___draw_object3d(const CImgList<t>& opacities, const unsigned int n_primitive) {
  43485. return n_primitive<opacities._width && opacities[n_primitive].size()==1?(float)opacities(n_primitive,0):1.0f;
  43486. }
  43487. template<typename t>
  43488. static float ___draw_object3d(const CImg<t>& opacities, const unsigned int n_primitive) {
  43489. return n_primitive<opacities._width?(float)opacities[n_primitive]:1.0f;
  43490. }
  43491. template<typename tz, typename tp, typename tf, typename tc, typename to>
  43492. CImg<T>& _draw_object3d(void *const pboard, CImg<tz>& zbuffer,
  43493. const float X, const float Y, const float Z,
  43494. const CImg<tp>& vertices,
  43495. const CImgList<tf>& primitives,
  43496. const CImgList<tc>& colors,
  43497. const to& opacities,
  43498. const unsigned int render_type,
  43499. const bool is_double_sided, const float focale,
  43500. const float lightx, const float lighty, const float lightz,
  43501. const float specular_lightness, const float specular_shininess,
  43502. const float sprite_scale) {
  43503. typedef typename cimg::superset2<tp,tz,float>::type tpfloat;
  43504. typedef typename to::value_type _to;
  43505. if (is_empty() || !vertices || !primitives) return *this;
  43506. CImg<char> error_message(1024);
  43507. if (!vertices.is_object3d(primitives,colors,opacities,false,error_message))
  43508. throw CImgArgumentException(_cimg_instance
  43509. "draw_object3d(): Invalid specified 3d object (%u,%u) (%s).",
  43510. cimg_instance,vertices._width,primitives._width,error_message.data());
  43511. #ifndef cimg_use_board
  43512. if (pboard) return *this;
  43513. #endif
  43514. if (render_type==5) cimg::mutex(10); // Static variable used in this case, breaks thread-safety.
  43515. const float
  43516. nspec = 1 - (specular_lightness<0.0f?0.0f:(specular_lightness>1.0f?1.0f:specular_lightness)),
  43517. nspec2 = 1 + (specular_shininess<0.0f?0.0f:specular_shininess),
  43518. nsl1 = (nspec2 - 1)/cimg::sqr(nspec - 1),
  43519. nsl2 = 1 - 2*nsl1*nspec,
  43520. nsl3 = nspec2 - nsl1 - nsl2;
  43521. // Create light texture for phong-like rendering.
  43522. CImg<floatT> light_texture;
  43523. if (render_type==5) {
  43524. if (colors._width>primitives._width) {
  43525. static CImg<floatT> default_light_texture;
  43526. static const tc *lptr = 0;
  43527. static tc ref_values[64] = { 0 };
  43528. const CImg<tc>& img = colors.back();
  43529. bool is_same_texture = (lptr==img._data);
  43530. if (is_same_texture)
  43531. for (unsigned int r = 0, j = 0; j<8; ++j)
  43532. for (unsigned int i = 0; i<8; ++i)
  43533. if (ref_values[r++]!=img(i*img._width/9,j*img._height/9,0,(i + j)%img._spectrum)) {
  43534. is_same_texture = false; break;
  43535. }
  43536. if (!is_same_texture || default_light_texture._spectrum<_spectrum) {
  43537. (default_light_texture.assign(img,false)/=255).resize(-100,-100,1,_spectrum);
  43538. lptr = colors.back().data();
  43539. for (unsigned int r = 0, j = 0; j<8; ++j)
  43540. for (unsigned int i = 0; i<8; ++i)
  43541. ref_values[r++] = img(i*img._width/9,j*img._height/9,0,(i + j)%img._spectrum);
  43542. }
  43543. light_texture.assign(default_light_texture,true);
  43544. } else {
  43545. static CImg<floatT> default_light_texture;
  43546. static float olightx = 0, olighty = 0, olightz = 0, ospecular_shininess = 0;
  43547. if (!default_light_texture ||
  43548. lightx!=olightx || lighty!=olighty || lightz!=olightz ||
  43549. specular_shininess!=ospecular_shininess || default_light_texture._spectrum<_spectrum) {
  43550. default_light_texture.assign(512,512);
  43551. const float
  43552. dlx = lightx - X,
  43553. dly = lighty - Y,
  43554. dlz = lightz - Z,
  43555. nl = cimg::hypot(dlx,dly,dlz),
  43556. nlx = (default_light_texture._width - 1)/2*(1 + dlx/nl),
  43557. nly = (default_light_texture._height - 1)/2*(1 + dly/nl),
  43558. white[] = { 1 };
  43559. default_light_texture.draw_gaussian(nlx,nly,default_light_texture._width/3.0f,white);
  43560. cimg_forXY(default_light_texture,x,y) {
  43561. const float factor = default_light_texture(x,y);
  43562. if (factor>nspec) default_light_texture(x,y) = std::min(2.0f,nsl1*factor*factor + nsl2*factor + nsl3);
  43563. }
  43564. default_light_texture.resize(-100,-100,1,_spectrum);
  43565. olightx = lightx; olighty = lighty; olightz = lightz; ospecular_shininess = specular_shininess;
  43566. }
  43567. light_texture.assign(default_light_texture,true);
  43568. }
  43569. }
  43570. // Compute 3d to 2d projection.
  43571. CImg<tpfloat> projections(vertices._width,2);
  43572. tpfloat parallzmin = cimg::type<tpfloat>::max();
  43573. const float absfocale = focale?cimg::abs(focale):0;
  43574. if (absfocale) {
  43575. cimg_pragma_openmp(parallel for cimg_openmp_if(projections.size()>4096))
  43576. cimg_forX(projections,l) { // Perspective projection
  43577. const tpfloat
  43578. x = (tpfloat)vertices(l,0),
  43579. y = (tpfloat)vertices(l,1),
  43580. z = (tpfloat)vertices(l,2);
  43581. const tpfloat projectedz = z + Z + absfocale;
  43582. projections(l,1) = Y + absfocale*y/projectedz;
  43583. projections(l,0) = X + absfocale*x/projectedz;
  43584. }
  43585. } else {
  43586. cimg_pragma_openmp(parallel for cimg_openmp_if(projections.size()>4096))
  43587. cimg_forX(projections,l) { // Parallel projection
  43588. const tpfloat
  43589. x = (tpfloat)vertices(l,0),
  43590. y = (tpfloat)vertices(l,1),
  43591. z = (tpfloat)vertices(l,2);
  43592. if (z<parallzmin) parallzmin = z;
  43593. projections(l,1) = Y + y;
  43594. projections(l,0) = X + x;
  43595. }
  43596. }
  43597. const float _focale = absfocale?absfocale:(1e5f-parallzmin);
  43598. float zmax = 0;
  43599. if (zbuffer) zmax = vertices.get_shared_row(2).max();
  43600. // Compute visible primitives.
  43601. CImg<uintT> visibles(primitives._width,1,1,1,~0U);
  43602. CImg<tpfloat> zrange(primitives._width);
  43603. const tpfloat zmin = absfocale?(tpfloat)(1.5f - absfocale):cimg::type<tpfloat>::min();
  43604. bool is_forward = zbuffer?true:false;
  43605. cimg_pragma_openmp(parallel for cimg_openmp_if(primitives.size()>4096))
  43606. cimglist_for(primitives,l) {
  43607. const CImg<tf>& primitive = primitives[l];
  43608. switch (primitive.size()) {
  43609. case 1 : { // Point
  43610. CImg<_to> _opacity;
  43611. __draw_object3d(opacities,l,_opacity);
  43612. if (l<=colors.width() && (colors[l].size()!=_spectrum || _opacity)) is_forward = false;
  43613. const unsigned int i0 = (unsigned int)primitive(0);
  43614. const tpfloat z0 = Z + vertices(i0,2);
  43615. if (z0>zmin) {
  43616. visibles(l) = (unsigned int)l;
  43617. zrange(l) = z0;
  43618. }
  43619. } break;
  43620. case 5 : { // Sphere
  43621. const unsigned int
  43622. i0 = (unsigned int)primitive(0),
  43623. i1 = (unsigned int)primitive(1);
  43624. const tpfloat
  43625. Xc = 0.5f*((float)vertices(i0,0) + (float)vertices(i1,0)),
  43626. Yc = 0.5f*((float)vertices(i0,1) + (float)vertices(i1,1)),
  43627. Zc = 0.5f*((float)vertices(i0,2) + (float)vertices(i1,2)),
  43628. _zc = Z + Zc,
  43629. zc = _zc + _focale,
  43630. xc = X + Xc*(absfocale?absfocale/zc:1),
  43631. yc = Y + Yc*(absfocale?absfocale/zc:1),
  43632. radius = 0.5f*cimg::hypot(vertices(i1,0) - vertices(i0,0),
  43633. vertices(i1,1) - vertices(i0,1),
  43634. vertices(i1,2) - vertices(i0,2))*(absfocale?absfocale/zc:1),
  43635. xm = xc - radius,
  43636. ym = yc - radius,
  43637. xM = xc + radius,
  43638. yM = yc + radius;
  43639. if (xM>=0 && xm<_width && yM>=0 && ym<_height && _zc>zmin) {
  43640. visibles(l) = (unsigned int)l;
  43641. zrange(l) = _zc;
  43642. }
  43643. is_forward = false;
  43644. } break;
  43645. case 2 : // Segment
  43646. case 6 : {
  43647. const unsigned int
  43648. i0 = (unsigned int)primitive(0),
  43649. i1 = (unsigned int)primitive(1);
  43650. const tpfloat
  43651. x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
  43652. x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2);
  43653. tpfloat xm, xM, ym, yM;
  43654. if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
  43655. if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
  43656. if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin) {
  43657. visibles(l) = (unsigned int)l;
  43658. zrange(l) = (z0 + z1)/2;
  43659. }
  43660. } break;
  43661. case 3 : // Triangle
  43662. case 9 : {
  43663. const unsigned int
  43664. i0 = (unsigned int)primitive(0),
  43665. i1 = (unsigned int)primitive(1),
  43666. i2 = (unsigned int)primitive(2);
  43667. const tpfloat
  43668. x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
  43669. x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2),
  43670. x2 = projections(i2,0), y2 = projections(i2,1), z2 = Z + vertices(i2,2);
  43671. tpfloat xm, xM, ym, yM;
  43672. if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
  43673. if (x2<xm) xm = x2;
  43674. if (x2>xM) xM = x2;
  43675. if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
  43676. if (y2<ym) ym = y2;
  43677. if (y2>yM) yM = y2;
  43678. if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin) {
  43679. const tpfloat d = (x1-x0)*(y2-y0) - (x2-x0)*(y1-y0);
  43680. if (is_double_sided || d<0) {
  43681. visibles(l) = (unsigned int)l;
  43682. zrange(l) = (z0 + z1 + z2)/3;
  43683. }
  43684. }
  43685. } break;
  43686. case 4 : // Rectangle
  43687. case 12 : {
  43688. const unsigned int
  43689. i0 = (unsigned int)primitive(0),
  43690. i1 = (unsigned int)primitive(1),
  43691. i2 = (unsigned int)primitive(2),
  43692. i3 = (unsigned int)primitive(3);
  43693. const tpfloat
  43694. x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
  43695. x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2),
  43696. x2 = projections(i2,0), y2 = projections(i2,1), z2 = Z + vertices(i2,2),
  43697. x3 = projections(i3,0), y3 = projections(i3,1), z3 = Z + vertices(i3,2);
  43698. tpfloat xm, xM, ym, yM;
  43699. if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
  43700. if (x2<xm) xm = x2;
  43701. if (x2>xM) xM = x2;
  43702. if (x3<xm) xm = x3;
  43703. if (x3>xM) xM = x3;
  43704. if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
  43705. if (y2<ym) ym = y2;
  43706. if (y2>yM) yM = y2;
  43707. if (y3<ym) ym = y3;
  43708. if (y3>yM) yM = y3;
  43709. if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin && z3>zmin) {
  43710. const float d = (x1 - x0)*(y2 - y0) - (x2 - x0)*(y1 - y0);
  43711. if (is_double_sided || d<0) {
  43712. visibles(l) = (unsigned int)l;
  43713. zrange(l) = (z0 + z1 + z2 + z3)/4;
  43714. }
  43715. }
  43716. } break;
  43717. default :
  43718. if (render_type==5) cimg::mutex(10,0);
  43719. throw CImgArgumentException(_cimg_instance
  43720. "draw_object3d(): Invalid primitive[%u] with size %u "
  43721. "(should have size 1,2,3,4,5,6,9 or 12).",
  43722. cimg_instance,
  43723. l,primitive.size());
  43724. }
  43725. }
  43726. // Force transparent primitives to be drawn last when zbuffer is activated
  43727. // (and if object contains no spheres or sprites).
  43728. if (is_forward)
  43729. cimglist_for(primitives,l)
  43730. if (___draw_object3d(opacities,l)!=1) zrange(l) = 2*zmax - zrange(l);
  43731. // Sort only visibles primitives.
  43732. unsigned int *p_visibles = visibles._data;
  43733. tpfloat *p_zrange = zrange._data;
  43734. const tpfloat *ptrz = p_zrange;
  43735. cimg_for(visibles,ptr,unsigned int) {
  43736. if (*ptr!=~0U) { *(p_visibles++) = *ptr; *(p_zrange++) = *ptrz; }
  43737. ++ptrz;
  43738. }
  43739. const unsigned int nb_visibles = (unsigned int)(p_zrange - zrange._data);
  43740. if (!nb_visibles) {
  43741. if (render_type==5) cimg::mutex(10,0);
  43742. return *this;
  43743. }
  43744. CImg<uintT> permutations;
  43745. CImg<tpfloat>(zrange._data,nb_visibles,1,1,1,true).sort(permutations,is_forward);
  43746. // Compute light properties
  43747. CImg<floatT> lightprops;
  43748. switch (render_type) {
  43749. case 3 : { // Flat Shading
  43750. lightprops.assign(nb_visibles);
  43751. cimg_pragma_openmp(parallel for cimg_openmp_if(nb_visibles>4096))
  43752. cimg_forX(lightprops,l) {
  43753. const CImg<tf>& primitive = primitives(visibles(permutations(l)));
  43754. const unsigned int psize = (unsigned int)primitive.size();
  43755. if (psize==3 || psize==4 || psize==9 || psize==12) {
  43756. const unsigned int
  43757. i0 = (unsigned int)primitive(0),
  43758. i1 = (unsigned int)primitive(1),
  43759. i2 = (unsigned int)primitive(2);
  43760. const tpfloat
  43761. x0 = (tpfloat)vertices(i0,0), y0 = (tpfloat)vertices(i0,1), z0 = (tpfloat)vertices(i0,2),
  43762. x1 = (tpfloat)vertices(i1,0), y1 = (tpfloat)vertices(i1,1), z1 = (tpfloat)vertices(i1,2),
  43763. x2 = (tpfloat)vertices(i2,0), y2 = (tpfloat)vertices(i2,1), z2 = (tpfloat)vertices(i2,2),
  43764. dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0,
  43765. dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0,
  43766. nx = dy1*dz2 - dz1*dy2,
  43767. ny = dz1*dx2 - dx1*dz2,
  43768. nz = dx1*dy2 - dy1*dx2,
  43769. norm = 1e-5f + cimg::hypot(nx,ny,nz),
  43770. lx = X + (x0 + x1 + x2)/3 - lightx,
  43771. ly = Y + (y0 + y1 + y2)/3 - lighty,
  43772. lz = Z + (z0 + z1 + z2)/3 - lightz,
  43773. nl = 1e-5f + cimg::hypot(lx,ly,lz),
  43774. factor = std::max(cimg::abs(-lx*nx - ly*ny - lz*nz)/(norm*nl),(tpfloat)0);
  43775. lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3);
  43776. } else lightprops[l] = 1;
  43777. }
  43778. } break;
  43779. case 4 : // Gouraud Shading
  43780. case 5 : { // Phong-Shading
  43781. CImg<tpfloat> vertices_normals(vertices._width,6,1,1,0);
  43782. cimg_pragma_openmp(parallel for cimg_openmp_if(nb_visibles>4096))
  43783. for (unsigned int l = 0; l<nb_visibles; ++l) {
  43784. const CImg<tf>& primitive = primitives[visibles(l)];
  43785. const unsigned int psize = (unsigned int)primitive.size();
  43786. const bool
  43787. triangle_flag = (psize==3) || (psize==9),
  43788. rectangle_flag = (psize==4) || (psize==12);
  43789. if (triangle_flag || rectangle_flag) {
  43790. const unsigned int
  43791. i0 = (unsigned int)primitive(0),
  43792. i1 = (unsigned int)primitive(1),
  43793. i2 = (unsigned int)primitive(2),
  43794. i3 = rectangle_flag?(unsigned int)primitive(3):0;
  43795. const tpfloat
  43796. x0 = (tpfloat)vertices(i0,0), y0 = (tpfloat)vertices(i0,1), z0 = (tpfloat)vertices(i0,2),
  43797. x1 = (tpfloat)vertices(i1,0), y1 = (tpfloat)vertices(i1,1), z1 = (tpfloat)vertices(i1,2),
  43798. x2 = (tpfloat)vertices(i2,0), y2 = (tpfloat)vertices(i2,1), z2 = (tpfloat)vertices(i2,2),
  43799. dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0,
  43800. dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0,
  43801. nnx = dy1*dz2 - dz1*dy2,
  43802. nny = dz1*dx2 - dx1*dz2,
  43803. nnz = dx1*dy2 - dy1*dx2,
  43804. norm = 1e-5f + cimg::hypot(nnx,nny,nnz),
  43805. nx = nnx/norm,
  43806. ny = nny/norm,
  43807. nz = nnz/norm;
  43808. unsigned int ix = 0, iy = 1, iz = 2;
  43809. if (is_double_sided && nz>0) { ix = 3; iy = 4; iz = 5; }
  43810. vertices_normals(i0,ix)+=nx; vertices_normals(i0,iy)+=ny; vertices_normals(i0,iz)+=nz;
  43811. vertices_normals(i1,ix)+=nx; vertices_normals(i1,iy)+=ny; vertices_normals(i1,iz)+=nz;
  43812. vertices_normals(i2,ix)+=nx; vertices_normals(i2,iy)+=ny; vertices_normals(i2,iz)+=nz;
  43813. if (rectangle_flag) {
  43814. vertices_normals(i3,ix)+=nx; vertices_normals(i3,iy)+=ny; vertices_normals(i3,iz)+=nz;
  43815. }
  43816. }
  43817. }
  43818. if (is_double_sided) cimg_forX(vertices_normals,p) {
  43819. const float
  43820. nx0 = vertices_normals(p,0), ny0 = vertices_normals(p,1), nz0 = vertices_normals(p,2),
  43821. nx1 = vertices_normals(p,3), ny1 = vertices_normals(p,4), nz1 = vertices_normals(p,5),
  43822. n0 = nx0*nx0 + ny0*ny0 + nz0*nz0, n1 = nx1*nx1 + ny1*ny1 + nz1*nz1;
  43823. if (n1>n0) {
  43824. vertices_normals(p,0) = -nx1;
  43825. vertices_normals(p,1) = -ny1;
  43826. vertices_normals(p,2) = -nz1;
  43827. }
  43828. }
  43829. if (render_type==4) {
  43830. lightprops.assign(vertices._width);
  43831. cimg_pragma_openmp(parallel for cimg_openmp_if(nb_visibles>4096))
  43832. cimg_forX(lightprops,l) {
  43833. const tpfloat
  43834. nx = vertices_normals(l,0),
  43835. ny = vertices_normals(l,1),
  43836. nz = vertices_normals(l,2),
  43837. norm = 1e-5f + cimg::hypot(nx,ny,nz),
  43838. lx = X + vertices(l,0) - lightx,
  43839. ly = Y + vertices(l,1) - lighty,
  43840. lz = Z + vertices(l,2) - lightz,
  43841. nl = 1e-5f + cimg::hypot(lx,ly,lz),
  43842. factor = std::max((-lx*nx - ly*ny - lz*nz)/(norm*nl),(tpfloat)0);
  43843. lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3);
  43844. }
  43845. } else {
  43846. const unsigned int
  43847. lw2 = light_texture._width/2 - 1,
  43848. lh2 = light_texture._height/2 - 1;
  43849. lightprops.assign(vertices._width,2);
  43850. cimg_pragma_openmp(parallel for cimg_openmp_if(nb_visibles>4096))
  43851. cimg_forX(lightprops,l) {
  43852. const tpfloat
  43853. nx = vertices_normals(l,0),
  43854. ny = vertices_normals(l,1),
  43855. nz = vertices_normals(l,2),
  43856. norm = 1e-5f + cimg::hypot(nx,ny,nz),
  43857. nnx = nx/norm,
  43858. nny = ny/norm;
  43859. lightprops(l,0) = lw2*(1 + nnx);
  43860. lightprops(l,1) = lh2*(1 + nny);
  43861. }
  43862. }
  43863. } break;
  43864. }
  43865. // Draw visible primitives
  43866. const CImg<tc> default_color(1,_spectrum,1,1,(tc)200);
  43867. CImg<_to> _opacity;
  43868. for (unsigned int l = 0; l<nb_visibles; ++l) {
  43869. const unsigned int n_primitive = visibles(permutations(l));
  43870. const CImg<tf>& primitive = primitives[n_primitive];
  43871. const CImg<tc>
  43872. &__color = n_primitive<colors._width?colors[n_primitive]:CImg<tc>(),
  43873. _color = (__color && __color.size()!=_spectrum && __color._spectrum<_spectrum)?
  43874. __color.get_resize(-100,-100,-100,_spectrum,0):CImg<tc>(),
  43875. &color = _color?_color:(__color?__color:default_color);
  43876. const tc *const pcolor = color._data;
  43877. const float opacity = __draw_object3d(opacities,n_primitive,_opacity);
  43878. #ifdef cimg_use_board
  43879. LibBoard::Board &board = *(LibBoard::Board*)pboard;
  43880. #endif
  43881. switch (primitive.size()) {
  43882. case 1 : { // Colored point or sprite
  43883. const unsigned int n0 = (unsigned int)primitive[0];
  43884. const int x0 = (int)projections(n0,0), y0 = (int)projections(n0,1);
  43885. if (_opacity.is_empty()) { // Scalar opacity.
  43886. if (color.size()==_spectrum) { // Colored point.
  43887. draw_point(x0,y0,pcolor,opacity);
  43888. #ifdef cimg_use_board
  43889. if (pboard) {
  43890. board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
  43891. board.drawDot((float)x0,height()-(float)y0);
  43892. }
  43893. #endif
  43894. } else { // Sprite.
  43895. const tpfloat z = Z + vertices(n0,2);
  43896. const float factor = focale<0?1:sprite_scale*(absfocale?absfocale/(z + absfocale):1);
  43897. const unsigned int
  43898. _sw = (unsigned int)(color._width*factor),
  43899. _sh = (unsigned int)(color._height*factor),
  43900. sw = _sw?_sw:1, sh = _sh?_sh:1;
  43901. const int nx0 = x0 - (int)sw/2, ny0 = y0 - (int)sh/2;
  43902. if (sw<=3*_width/2 && sh<=3*_height/2 &&
  43903. (nx0 + (int)sw/2>=0 || nx0 - (int)sw/2<width() || ny0 + (int)sh/2>=0 || ny0 - (int)sh/2<height())) {
  43904. const CImg<tc>
  43905. _sprite = (sw!=color._width || sh!=color._height)?
  43906. color.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<tc>(),
  43907. &sprite = _sprite?_sprite:color;
  43908. draw_image(nx0,ny0,sprite,opacity);
  43909. #ifdef cimg_use_board
  43910. if (pboard) {
  43911. board.setPenColorRGBi(128,128,128);
  43912. board.setFillColor(LibBoard::Color::Null);
  43913. board.drawRectangle((float)nx0,height() - (float)ny0,sw,sh);
  43914. }
  43915. #endif
  43916. }
  43917. }
  43918. } else { // Opacity mask.
  43919. const tpfloat z = Z + vertices(n0,2);
  43920. const float factor = focale<0?1:sprite_scale*(absfocale?absfocale/(z + absfocale):1);
  43921. const unsigned int
  43922. _sw = (unsigned int)(std::max(color._width,_opacity._width)*factor),
  43923. _sh = (unsigned int)(std::max(color._height,_opacity._height)*factor),
  43924. sw = _sw?_sw:1, sh = _sh?_sh:1;
  43925. const int nx0 = x0 - (int)sw/2, ny0 = y0 - (int)sh/2;
  43926. if (sw<=3*_width/2 && sh<=3*_height/2 &&
  43927. (nx0 + (int)sw/2>=0 || nx0 - (int)sw/2<width() || ny0 + (int)sh/2>=0 || ny0 - (int)sh/2<height())) {
  43928. const CImg<tc>
  43929. _sprite = (sw!=color._width || sh!=color._height)?
  43930. color.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<tc>(),
  43931. &sprite = _sprite?_sprite:color;
  43932. const CImg<_to>
  43933. _nopacity = (sw!=_opacity._width || sh!=_opacity._height)?
  43934. _opacity.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<_to>(),
  43935. &nopacity = _nopacity?_nopacity:_opacity;
  43936. draw_image(nx0,ny0,sprite,nopacity);
  43937. #ifdef cimg_use_board
  43938. if (pboard) {
  43939. board.setPenColorRGBi(128,128,128);
  43940. board.setFillColor(LibBoard::Color::Null);
  43941. board.drawRectangle((float)nx0,height() - (float)ny0,sw,sh);
  43942. }
  43943. #endif
  43944. }
  43945. }
  43946. } break;
  43947. case 2 : { // Colored line
  43948. const unsigned int
  43949. n0 = (unsigned int)primitive[0],
  43950. n1 = (unsigned int)primitive[1];
  43951. const int
  43952. x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
  43953. x1 = (int)projections(n1,0), y1 = (int)projections(n1,1);
  43954. const float
  43955. z0 = vertices(n0,2) + Z + _focale,
  43956. z1 = vertices(n1,2) + Z + _focale;
  43957. if (render_type) {
  43958. if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity);
  43959. else draw_line(x0,y0,x1,y1,pcolor,opacity);
  43960. #ifdef cimg_use_board
  43961. if (pboard) {
  43962. board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
  43963. board.drawLine((float)x0,height() - (float)y0,x1,height() - (float)y1);
  43964. }
  43965. #endif
  43966. } else {
  43967. draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity);
  43968. #ifdef cimg_use_board
  43969. if (pboard) {
  43970. board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
  43971. board.drawDot((float)x0,height() - (float)y0);
  43972. board.drawDot((float)x1,height() - (float)y1);
  43973. }
  43974. #endif
  43975. }
  43976. } break;
  43977. case 5 : { // Colored sphere
  43978. const unsigned int
  43979. n0 = (unsigned int)primitive[0],
  43980. n1 = (unsigned int)primitive[1],
  43981. is_wireframe = (unsigned int)primitive[2];
  43982. const float
  43983. Xc = 0.5f*((float)vertices(n0,0) + (float)vertices(n1,0)),
  43984. Yc = 0.5f*((float)vertices(n0,1) + (float)vertices(n1,1)),
  43985. Zc = 0.5f*((float)vertices(n0,2) + (float)vertices(n1,2)),
  43986. zc = Z + Zc + _focale,
  43987. xc = X + Xc*(absfocale?absfocale/zc:1),
  43988. yc = Y + Yc*(absfocale?absfocale/zc:1),
  43989. radius = 0.5f*cimg::hypot(vertices(n1,0) - vertices(n0,0),
  43990. vertices(n1,1) - vertices(n0,1),
  43991. vertices(n1,2) - vertices(n0,2))*(absfocale?absfocale/zc:1);
  43992. switch (render_type) {
  43993. case 0 :
  43994. draw_point((int)xc,(int)yc,pcolor,opacity);
  43995. #ifdef cimg_use_board
  43996. if (pboard) {
  43997. board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
  43998. board.drawDot(xc,height() - yc);
  43999. }
  44000. #endif
  44001. break;
  44002. case 1 :
  44003. draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity,~0U);
  44004. #ifdef cimg_use_board
  44005. if (pboard) {
  44006. board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
  44007. board.setFillColor(LibBoard::Color::Null);
  44008. board.drawCircle(xc,height() - yc,radius);
  44009. }
  44010. #endif
  44011. break;
  44012. default :
  44013. if (is_wireframe) draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity,~0U);
  44014. else draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity);
  44015. #ifdef cimg_use_board
  44016. if (pboard) {
  44017. board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
  44018. if (!is_wireframe) board.fillCircle(xc,height() - yc,radius);
  44019. else {
  44020. board.setFillColor(LibBoard::Color::Null);
  44021. board.drawCircle(xc,height() - yc,radius);
  44022. }
  44023. }
  44024. #endif
  44025. break;
  44026. }
  44027. } break;
  44028. case 6 : { // Textured line
  44029. if (!__color) {
  44030. if (render_type==5) cimg::mutex(10,0);
  44031. throw CImgArgumentException(_cimg_instance
  44032. "draw_object3d(): Undefined texture for line primitive [%u].",
  44033. cimg_instance,n_primitive);
  44034. }
  44035. const unsigned int
  44036. n0 = (unsigned int)primitive[0],
  44037. n1 = (unsigned int)primitive[1];
  44038. const int
  44039. tx0 = (int)primitive[2], ty0 = (int)primitive[3],
  44040. tx1 = (int)primitive[4], ty1 = (int)primitive[5],
  44041. x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
  44042. x1 = (int)projections(n1,0), y1 = (int)projections(n1,1);
  44043. const float
  44044. z0 = vertices(n0,2) + Z + _focale,
  44045. z1 = vertices(n1,2) + Z + _focale;
  44046. if (render_type) {
  44047. if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity);
  44048. else draw_line(x0,y0,x1,y1,color,tx0,ty0,tx1,ty1,opacity);
  44049. #ifdef cimg_use_board
  44050. if (pboard) {
  44051. board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
  44052. board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
  44053. }
  44054. #endif
  44055. } else {
  44056. draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0,
  44057. ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity).
  44058. draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1,
  44059. ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity);
  44060. #ifdef cimg_use_board
  44061. if (pboard) {
  44062. board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
  44063. board.drawDot((float)x0,height() - (float)y0);
  44064. board.drawDot((float)x1,height() - (float)y1);
  44065. }
  44066. #endif
  44067. }
  44068. } break;
  44069. case 3 : { // Colored triangle
  44070. const unsigned int
  44071. n0 = (unsigned int)primitive[0],
  44072. n1 = (unsigned int)primitive[1],
  44073. n2 = (unsigned int)primitive[2];
  44074. const int
  44075. x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
  44076. x1 = (int)projections(n1,0), y1 = (int)projections(n1,1),
  44077. x2 = (int)projections(n2,0), y2 = (int)projections(n2,1);
  44078. const float
  44079. z0 = vertices(n0,2) + Z + _focale,
  44080. z1 = vertices(n1,2) + Z + _focale,
  44081. z2 = vertices(n2,2) + Z + _focale;
  44082. switch (render_type) {
  44083. case 0 :
  44084. draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity).draw_point(x2,y2,pcolor,opacity);
  44085. #ifdef cimg_use_board
  44086. if (pboard) {
  44087. board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
  44088. board.drawDot((float)x0,height() - (float)y0);
  44089. board.drawDot((float)x1,height() - (float)y1);
  44090. board.drawDot((float)x2,height() - (float)y2);
  44091. }
  44092. #endif
  44093. break;
  44094. case 1 :
  44095. if (zbuffer)
  44096. draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity).draw_line(zbuffer,x0,y0,z0,x2,y2,z2,pcolor,opacity).
  44097. draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opacity);
  44098. else
  44099. draw_line(x0,y0,x1,y1,pcolor,opacity).draw_line(x0,y0,x2,y2,pcolor,opacity).
  44100. draw_line(x1,y1,x2,y2,pcolor,opacity);
  44101. #ifdef cimg_use_board
  44102. if (pboard) {
  44103. board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
  44104. board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
  44105. board.drawLine((float)x0,height() - (float)y0,(float)x2,height() - (float)y2);
  44106. board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2);
  44107. }
  44108. #endif
  44109. break;
  44110. case 2 :
  44111. if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity);
  44112. else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity);
  44113. #ifdef cimg_use_board
  44114. if (pboard) {
  44115. board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
  44116. board.fillTriangle((float)x0,height() - (float)y0,
  44117. (float)x1,height() - (float)y1,
  44118. (float)x2,height() - (float)y2);
  44119. }
  44120. #endif
  44121. break;
  44122. case 3 :
  44123. if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity,lightprops(l));
  44124. else _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity,lightprops(l));
  44125. #ifdef cimg_use_board
  44126. if (pboard) {
  44127. const float lp = std::min(lightprops(l),1);
  44128. board.setPenColorRGBi((unsigned char)(color[0]*lp),
  44129. (unsigned char)(color[1]*lp),
  44130. (unsigned char)(color[2]*lp),
  44131. (unsigned char)(opacity*255));
  44132. board.fillTriangle((float)x0,height() - (float)y0,
  44133. (float)x1,height() - (float)y1,
  44134. (float)x2,height() - (float)y2);
  44135. }
  44136. #endif
  44137. break;
  44138. case 4 :
  44139. if (zbuffer)
  44140. draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,
  44141. lightprops(n0),lightprops(n1),lightprops(n2),opacity);
  44142. else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,lightprops(n0),lightprops(n1),lightprops(n2),opacity);
  44143. #ifdef cimg_use_board
  44144. if (pboard) {
  44145. board.setPenColorRGBi((unsigned char)(color[0]),
  44146. (unsigned char)(color[1]),
  44147. (unsigned char)(color[2]),
  44148. (unsigned char)(opacity*255));
  44149. board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprops(n0),
  44150. (float)x1,height() - (float)y1,lightprops(n1),
  44151. (float)x2,height() - (float)y2,lightprops(n2));
  44152. }
  44153. #endif
  44154. break;
  44155. case 5 : {
  44156. const unsigned int
  44157. lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1),
  44158. lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1),
  44159. lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1);
  44160. if (zbuffer)
  44161. draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
  44162. else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
  44163. #ifdef cimg_use_board
  44164. if (pboard) {
  44165. const float
  44166. l0 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n0,0))),
  44167. (int)(light_texture.height()/2*(1 + lightprops(n0,1)))),
  44168. l1 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n1,0))),
  44169. (int)(light_texture.height()/2*(1 + lightprops(n1,1)))),
  44170. l2 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n2,0))),
  44171. (int)(light_texture.height()/2*(1 + lightprops(n2,1))));
  44172. board.setPenColorRGBi((unsigned char)(color[0]),
  44173. (unsigned char)(color[1]),
  44174. (unsigned char)(color[2]),
  44175. (unsigned char)(opacity*255));
  44176. board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
  44177. (float)x1,height() - (float)y1,l1,
  44178. (float)x2,height() - (float)y2,l2);
  44179. }
  44180. #endif
  44181. } break;
  44182. }
  44183. } break;
  44184. case 4 : { // Colored rectangle
  44185. const unsigned int
  44186. n0 = (unsigned int)primitive[0],
  44187. n1 = (unsigned int)primitive[1],
  44188. n2 = (unsigned int)primitive[2],
  44189. n3 = (unsigned int)primitive[3];
  44190. const int
  44191. x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
  44192. x1 = (int)projections(n1,0), y1 = (int)projections(n1,1),
  44193. x2 = (int)projections(n2,0), y2 = (int)projections(n2,1),
  44194. x3 = (int)projections(n3,0), y3 = (int)projections(n3,1);
  44195. const float
  44196. z0 = vertices(n0,2) + Z + _focale,
  44197. z1 = vertices(n1,2) + Z + _focale,
  44198. z2 = vertices(n2,2) + Z + _focale,
  44199. z3 = vertices(n3,2) + Z + _focale;
  44200. switch (render_type) {
  44201. case 0 :
  44202. draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity).
  44203. draw_point(x2,y2,pcolor,opacity).draw_point(x3,y3,pcolor,opacity);
  44204. #ifdef cimg_use_board
  44205. if (pboard) {
  44206. board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
  44207. board.drawDot((float)x0,height() - (float)y0);
  44208. board.drawDot((float)x1,height() - (float)y1);
  44209. board.drawDot((float)x2,height() - (float)y2);
  44210. board.drawDot((float)x3,height() - (float)y3);
  44211. }
  44212. #endif
  44213. break;
  44214. case 1 :
  44215. if (zbuffer)
  44216. draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity).draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opacity).
  44217. draw_line(zbuffer,x2,y2,z2,x3,y3,z3,pcolor,opacity).draw_line(zbuffer,x3,y3,z3,x0,y0,z0,pcolor,opacity);
  44218. else
  44219. draw_line(x0,y0,x1,y1,pcolor,opacity).draw_line(x1,y1,x2,y2,pcolor,opacity).
  44220. draw_line(x2,y2,x3,y3,pcolor,opacity).draw_line(x3,y3,x0,y0,pcolor,opacity);
  44221. #ifdef cimg_use_board
  44222. if (pboard) {
  44223. board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
  44224. board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
  44225. board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2);
  44226. board.drawLine((float)x2,height() - (float)y2,(float)x3,height() - (float)y3);
  44227. board.drawLine((float)x3,height() - (float)y3,(float)x0,height() - (float)y0);
  44228. }
  44229. #endif
  44230. break;
  44231. case 2 :
  44232. if (zbuffer)
  44233. draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity).
  44234. draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opacity);
  44235. else
  44236. draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity).draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opacity);
  44237. #ifdef cimg_use_board
  44238. if (pboard) {
  44239. board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
  44240. board.fillTriangle((float)x0,height() - (float)y0,
  44241. (float)x1,height() - (float)y1,
  44242. (float)x2,height() - (float)y2);
  44243. board.fillTriangle((float)x0,height() - (float)y0,
  44244. (float)x2,height() - (float)y2,
  44245. (float)x3,height() - (float)y3);
  44246. }
  44247. #endif
  44248. break;
  44249. case 3 :
  44250. if (zbuffer)
  44251. draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity,lightprops(l)).
  44252. draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opacity,lightprops(l));
  44253. else
  44254. _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity,lightprops(l)).
  44255. _draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opacity,lightprops(l));
  44256. #ifdef cimg_use_board
  44257. if (pboard) {
  44258. const float lp = std::min(lightprops(l),1);
  44259. board.setPenColorRGBi((unsigned char)(color[0]*lp),
  44260. (unsigned char)(color[1]*lp),
  44261. (unsigned char)(color[2]*lp),(unsigned char)(opacity*255));
  44262. board.fillTriangle((float)x0,height() - (float)y0,
  44263. (float)x1,height() - (float)y1,
  44264. (float)x2,height() - (float)y2);
  44265. board.fillTriangle((float)x0,height() - (float)y0,
  44266. (float)x2,height() - (float)y2,
  44267. (float)x3,height() - (float)y3);
  44268. }
  44269. #endif
  44270. break;
  44271. case 4 : {
  44272. const float
  44273. lightprop0 = lightprops(n0), lightprop1 = lightprops(n1),
  44274. lightprop2 = lightprops(n2), lightprop3 = lightprops(n3);
  44275. if (zbuffer)
  44276. draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,lightprop0,lightprop1,lightprop2,opacity).
  44277. draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,lightprop0,lightprop2,lightprop3,opacity);
  44278. else
  44279. draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,lightprop0,lightprop1,lightprop2,opacity).
  44280. draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,lightprop0,lightprop2,lightprop3,opacity);
  44281. #ifdef cimg_use_board
  44282. if (pboard) {
  44283. board.setPenColorRGBi((unsigned char)(color[0]),
  44284. (unsigned char)(color[1]),
  44285. (unsigned char)(color[2]),
  44286. (unsigned char)(opacity*255));
  44287. board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0,
  44288. (float)x1,height() - (float)y1,lightprop1,
  44289. (float)x2,height() - (float)y2,lightprop2);
  44290. board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0,
  44291. (float)x2,height() - (float)y2,lightprop2,
  44292. (float)x3,height() - (float)y3,lightprop3);
  44293. }
  44294. #endif
  44295. } break;
  44296. case 5 : {
  44297. const unsigned int
  44298. lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1),
  44299. lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1),
  44300. lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1),
  44301. lx3 = (unsigned int)lightprops(n3,0), ly3 = (unsigned int)lightprops(n3,1);
  44302. if (zbuffer)
  44303. draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity).
  44304. draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opacity);
  44305. else
  44306. draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity).
  44307. draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opacity);
  44308. #ifdef cimg_use_board
  44309. if (pboard) {
  44310. const float
  44311. l0 = light_texture((int)(light_texture.width()/2*(1 + lx0)), (int)(light_texture.height()/2*(1 + ly0))),
  44312. l1 = light_texture((int)(light_texture.width()/2*(1 + lx1)), (int)(light_texture.height()/2*(1 + ly1))),
  44313. l2 = light_texture((int)(light_texture.width()/2*(1 + lx2)), (int)(light_texture.height()/2*(1 + ly2))),
  44314. l3 = light_texture((int)(light_texture.width()/2*(1 + lx3)), (int)(light_texture.height()/2*(1 + ly3)));
  44315. board.setPenColorRGBi((unsigned char)(color[0]),
  44316. (unsigned char)(color[1]),
  44317. (unsigned char)(color[2]),
  44318. (unsigned char)(opacity*255));
  44319. board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
  44320. (float)x1,height() - (float)y1,l1,
  44321. (float)x2,height() - (float)y2,l2);
  44322. board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
  44323. (float)x2,height() - (float)y2,l2,
  44324. (float)x3,height() - (float)y3,l3);
  44325. }
  44326. #endif
  44327. } break;
  44328. }
  44329. } break;
  44330. case 9 : { // Textured triangle
  44331. if (!__color) {
  44332. if (render_type==5) cimg::mutex(10,0);
  44333. throw CImgArgumentException(_cimg_instance
  44334. "draw_object3d(): Undefined texture for triangle primitive [%u].",
  44335. cimg_instance,n_primitive);
  44336. }
  44337. const unsigned int
  44338. n0 = (unsigned int)primitive[0],
  44339. n1 = (unsigned int)primitive[1],
  44340. n2 = (unsigned int)primitive[2];
  44341. const int
  44342. tx0 = (int)primitive[3], ty0 = (int)primitive[4],
  44343. tx1 = (int)primitive[5], ty1 = (int)primitive[6],
  44344. tx2 = (int)primitive[7], ty2 = (int)primitive[8],
  44345. x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
  44346. x1 = (int)projections(n1,0), y1 = (int)projections(n1,1),
  44347. x2 = (int)projections(n2,0), y2 = (int)projections(n2,1);
  44348. const float
  44349. z0 = vertices(n0,2) + Z + _focale,
  44350. z1 = vertices(n1,2) + Z + _focale,
  44351. z2 = vertices(n2,2) + Z + _focale;
  44352. switch (render_type) {
  44353. case 0 :
  44354. draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0,
  44355. ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity).
  44356. draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1,
  44357. ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity).
  44358. draw_point(x2,y2,color.get_vector_at(tx2<=0?0:tx2>=color.width()?color.width() - 1:tx2,
  44359. ty2<=0?0:ty2>=color.height()?color.height() - 1:ty2)._data,opacity);
  44360. #ifdef cimg_use_board
  44361. if (pboard) {
  44362. board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
  44363. board.drawDot((float)x0,height() - (float)y0);
  44364. board.drawDot((float)x1,height() - (float)y1);
  44365. board.drawDot((float)x2,height() - (float)y2);
  44366. }
  44367. #endif
  44368. break;
  44369. case 1 :
  44370. if (zbuffer)
  44371. draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
  44372. draw_line(zbuffer,x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opacity).
  44373. draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity);
  44374. else
  44375. draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
  44376. draw_line(x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opacity).
  44377. draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity);
  44378. #ifdef cimg_use_board
  44379. if (pboard) {
  44380. board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
  44381. board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
  44382. board.drawLine((float)x0,height() - (float)y0,(float)x2,height() - (float)y2);
  44383. board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2);
  44384. }
  44385. #endif
  44386. break;
  44387. case 2 :
  44388. if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity);
  44389. else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity);
  44390. #ifdef cimg_use_board
  44391. if (pboard) {
  44392. board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
  44393. board.fillTriangle((float)x0,height() - (float)y0,
  44394. (float)x1,height() - (float)y1,
  44395. (float)x2,height() - (float)y2);
  44396. }
  44397. #endif
  44398. break;
  44399. case 3 :
  44400. if (zbuffer)
  44401. draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l));
  44402. else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l));
  44403. #ifdef cimg_use_board
  44404. if (pboard) {
  44405. const float lp = std::min(lightprops(l),1);
  44406. board.setPenColorRGBi((unsigned char)(128*lp),
  44407. (unsigned char)(128*lp),
  44408. (unsigned char)(128*lp),
  44409. (unsigned char)(opacity*255));
  44410. board.fillTriangle((float)x0,height() - (float)y0,
  44411. (float)x1,height() - (float)y1,
  44412. (float)x2,height() - (float)y2);
  44413. }
  44414. #endif
  44415. break;
  44416. case 4 :
  44417. if (zbuffer)
  44418. draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
  44419. lightprops(n0),lightprops(n1),lightprops(n2),opacity);
  44420. else
  44421. draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
  44422. lightprops(n0),lightprops(n1),lightprops(n2),opacity);
  44423. #ifdef cimg_use_board
  44424. if (pboard) {
  44425. board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
  44426. board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprops(n0),
  44427. (float)x1,height() - (float)y1,lightprops(n1),
  44428. (float)x2,height() - (float)y2,lightprops(n2));
  44429. }
  44430. #endif
  44431. break;
  44432. case 5 :
  44433. if (zbuffer)
  44434. draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,
  44435. (unsigned int)lightprops(n0,0),(unsigned int)lightprops(n0,1),
  44436. (unsigned int)lightprops(n1,0),(unsigned int)lightprops(n1,1),
  44437. (unsigned int)lightprops(n2,0),(unsigned int)lightprops(n2,1),
  44438. opacity);
  44439. else
  44440. draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,
  44441. (unsigned int)lightprops(n0,0),(unsigned int)lightprops(n0,1),
  44442. (unsigned int)lightprops(n1,0),(unsigned int)lightprops(n1,1),
  44443. (unsigned int)lightprops(n2,0),(unsigned int)lightprops(n2,1),
  44444. opacity);
  44445. #ifdef cimg_use_board
  44446. if (pboard) {
  44447. const float
  44448. l0 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n0,0))),
  44449. (int)(light_texture.height()/2*(1 + lightprops(n0,1)))),
  44450. l1 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n1,0))),
  44451. (int)(light_texture.height()/2*(1 + lightprops(n1,1)))),
  44452. l2 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n2,0))),
  44453. (int)(light_texture.height()/2*(1 + lightprops(n2,1))));
  44454. board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
  44455. board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
  44456. (float)x1,height() - (float)y1,l1,
  44457. (float)x2,height() - (float)y2,l2);
  44458. }
  44459. #endif
  44460. break;
  44461. }
  44462. } break;
  44463. case 12 : { // Textured quadrangle
  44464. if (!__color) {
  44465. if (render_type==5) cimg::mutex(10,0);
  44466. throw CImgArgumentException(_cimg_instance
  44467. "draw_object3d(): Undefined texture for quadrangle primitive [%u].",
  44468. cimg_instance,n_primitive);
  44469. }
  44470. const unsigned int
  44471. n0 = (unsigned int)primitive[0],
  44472. n1 = (unsigned int)primitive[1],
  44473. n2 = (unsigned int)primitive[2],
  44474. n3 = (unsigned int)primitive[3];
  44475. const int
  44476. tx0 = (int)primitive[4], ty0 = (int)primitive[5],
  44477. tx1 = (int)primitive[6], ty1 = (int)primitive[7],
  44478. tx2 = (int)primitive[8], ty2 = (int)primitive[9],
  44479. tx3 = (int)primitive[10], ty3 = (int)primitive[11],
  44480. x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
  44481. x1 = (int)projections(n1,0), y1 = (int)projections(n1,1),
  44482. x2 = (int)projections(n2,0), y2 = (int)projections(n2,1),
  44483. x3 = (int)projections(n3,0), y3 = (int)projections(n3,1);
  44484. const float
  44485. z0 = vertices(n0,2) + Z + _focale,
  44486. z1 = vertices(n1,2) + Z + _focale,
  44487. z2 = vertices(n2,2) + Z + _focale,
  44488. z3 = vertices(n3,2) + Z + _focale;
  44489. switch (render_type) {
  44490. case 0 :
  44491. draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0,
  44492. ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity).
  44493. draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1,
  44494. ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity).
  44495. draw_point(x2,y2,color.get_vector_at(tx2<=0?0:tx2>=color.width()?color.width() - 1:tx2,
  44496. ty2<=0?0:ty2>=color.height()?color.height() - 1:ty2)._data,opacity).
  44497. draw_point(x3,y3,color.get_vector_at(tx3<=0?0:tx3>=color.width()?color.width() - 1:tx3,
  44498. ty3<=0?0:ty3>=color.height()?color.height() - 1:ty3)._data,opacity);
  44499. #ifdef cimg_use_board
  44500. if (pboard) {
  44501. board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
  44502. board.drawDot((float)x0,height() - (float)y0);
  44503. board.drawDot((float)x1,height() - (float)y1);
  44504. board.drawDot((float)x2,height() - (float)y2);
  44505. board.drawDot((float)x3,height() - (float)y3);
  44506. }
  44507. #endif
  44508. break;
  44509. case 1 :
  44510. if (zbuffer)
  44511. draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
  44512. draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity).
  44513. draw_line(zbuffer,x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opacity).
  44514. draw_line(zbuffer,x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opacity);
  44515. else
  44516. draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
  44517. draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity).
  44518. draw_line(x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opacity).
  44519. draw_line(x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opacity);
  44520. #ifdef cimg_use_board
  44521. if (pboard) {
  44522. board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
  44523. board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
  44524. board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2);
  44525. board.drawLine((float)x2,height() - (float)y2,(float)x3,height() - (float)y3);
  44526. board.drawLine((float)x3,height() - (float)y3,(float)x0,height() - (float)y0);
  44527. }
  44528. #endif
  44529. break;
  44530. case 2 :
  44531. if (zbuffer)
  44532. draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity).
  44533. draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity);
  44534. else
  44535. draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity).
  44536. draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity);
  44537. #ifdef cimg_use_board
  44538. if (pboard) {
  44539. board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
  44540. board.fillTriangle((float)x0,height() - (float)y0,
  44541. (float)x1,height() - (float)y1,
  44542. (float)x2,height() - (float)y2);
  44543. board.fillTriangle((float)x0,height() - (float)y0,
  44544. (float)x2,height() - (float)y2,
  44545. (float)x3,height() - (float)y3);
  44546. }
  44547. #endif
  44548. break;
  44549. case 3 :
  44550. if (zbuffer)
  44551. draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)).
  44552. draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity,lightprops(l));
  44553. else
  44554. draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)).
  44555. draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity,lightprops(l));
  44556. #ifdef cimg_use_board
  44557. if (pboard) {
  44558. const float lp = std::min(lightprops(l),1);
  44559. board.setPenColorRGBi((unsigned char)(128*lp),
  44560. (unsigned char)(128*lp),
  44561. (unsigned char)(128*lp),
  44562. (unsigned char)(opacity*255));
  44563. board.fillTriangle((float)x0,height() - (float)y0,
  44564. (float)x1,height() - (float)y1,
  44565. (float)x2,height() - (float)y2);
  44566. board.fillTriangle((float)x0,height() - (float)y0,
  44567. (float)x2,height() - (float)y2,
  44568. (float)x3,height() - (float)y3);
  44569. }
  44570. #endif
  44571. break;
  44572. case 4 : {
  44573. const float
  44574. lightprop0 = lightprops(n0), lightprop1 = lightprops(n1),
  44575. lightprop2 = lightprops(n2), lightprop3 = lightprops(n3);
  44576. if (zbuffer)
  44577. draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
  44578. lightprop0,lightprop1,lightprop2,opacity).
  44579. draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,
  44580. lightprop0,lightprop2,lightprop3,opacity);
  44581. else
  44582. draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
  44583. lightprop0,lightprop1,lightprop2,opacity).
  44584. draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,
  44585. lightprop0,lightprop2,lightprop3,opacity);
  44586. #ifdef cimg_use_board
  44587. if (pboard) {
  44588. board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
  44589. board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0,
  44590. (float)x1,height() - (float)y1,lightprop1,
  44591. (float)x2,height() - (float)y2,lightprop2);
  44592. board.fillGouraudTriangle((float)x0,height() -(float)y0,lightprop0,
  44593. (float)x2,height() - (float)y2,lightprop2,
  44594. (float)x3,height() - (float)y3,lightprop3);
  44595. }
  44596. #endif
  44597. } break;
  44598. case 5 : {
  44599. const unsigned int
  44600. lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1),
  44601. lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1),
  44602. lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1),
  44603. lx3 = (unsigned int)lightprops(n3,0), ly3 = (unsigned int)lightprops(n3,1);
  44604. if (zbuffer)
  44605. draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
  44606. light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity).
  44607. draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,
  44608. light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opacity);
  44609. else
  44610. draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
  44611. light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity).
  44612. draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,
  44613. light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opacity);
  44614. #ifdef cimg_use_board
  44615. if (pboard) {
  44616. const float
  44617. l0 = light_texture((int)(light_texture.width()/2*(1 + lx0)), (int)(light_texture.height()/2*(1 + ly0))),
  44618. l1 = light_texture((int)(light_texture.width()/2*(1 + lx1)), (int)(light_texture.height()/2*(1 + ly1))),
  44619. l2 = light_texture((int)(light_texture.width()/2*(1 + lx2)), (int)(light_texture.height()/2*(1 + ly2))),
  44620. l3 = light_texture((int)(light_texture.width()/2*(1 + lx3)), (int)(light_texture.height()/2*(1 + ly3)));
  44621. board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
  44622. board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
  44623. (float)x1,height() - (float)y1,l1,
  44624. (float)x2,height() - (float)y2,l2);
  44625. board.fillGouraudTriangle((float)x0,height() -(float)y0,l0,
  44626. (float)x2,height() - (float)y2,l2,
  44627. (float)x3,height() - (float)y3,l3);
  44628. }
  44629. #endif
  44630. } break;
  44631. }
  44632. } break;
  44633. }
  44634. }
  44635. if (render_type==5) cimg::mutex(10,0);
  44636. return *this;
  44637. }
  44638. //@}
  44639. //---------------------------
  44640. //
  44641. //! \name Data Input
  44642. //@{
  44643. //---------------------------
  44644. //! Launch simple interface to select a shape from an image.
  44645. /**
  44646. \param disp Display window to use.
  44647. \param feature_type Type of feature to select. Can be <tt>{ 0=point | 1=line | 2=rectangle | 3=ellipse }</tt>.
  44648. \param XYZ Pointer to 3 values X,Y,Z which tells about the projection point coordinates, for volumetric images.
  44649. \param exit_on_anykey Exit function when any key is pressed.
  44650. **/
  44651. CImg<T>& select(CImgDisplay &disp,
  44652. const unsigned int feature_type=2, unsigned int *const XYZ=0,
  44653. const bool exit_on_anykey=false,
  44654. const bool is_deep_selection_default=false) {
  44655. return get_select(disp,feature_type,XYZ,exit_on_anykey,is_deep_selection_default).move_to(*this);
  44656. }
  44657. //! Simple interface to select a shape from an image \overloading.
  44658. CImg<T>& select(const char *const title,
  44659. const unsigned int feature_type=2, unsigned int *const XYZ=0,
  44660. const bool exit_on_anykey=false,
  44661. const bool is_deep_selection_default=false) {
  44662. return get_select(title,feature_type,XYZ,exit_on_anykey,is_deep_selection_default).move_to(*this);
  44663. }
  44664. //! Simple interface to select a shape from an image \newinstance.
  44665. CImg<intT> get_select(CImgDisplay &disp,
  44666. const unsigned int feature_type=2, unsigned int *const XYZ=0,
  44667. const bool exit_on_anykey=false,
  44668. const bool is_deep_selection_default=false) const {
  44669. return _select(disp,0,feature_type,XYZ,0,0,0,exit_on_anykey,true,false,is_deep_selection_default);
  44670. }
  44671. //! Simple interface to select a shape from an image \newinstance.
  44672. CImg<intT> get_select(const char *const title,
  44673. const unsigned int feature_type=2, unsigned int *const XYZ=0,
  44674. const bool exit_on_anykey=false,
  44675. const bool is_deep_selection_default=false) const {
  44676. CImgDisplay disp;
  44677. return _select(disp,title,feature_type,XYZ,0,0,0,exit_on_anykey,true,false,is_deep_selection_default);
  44678. }
  44679. CImg<intT> _select(CImgDisplay &disp, const char *const title,
  44680. const unsigned int feature_type, unsigned int *const XYZ,
  44681. const int origX, const int origY, const int origZ,
  44682. const bool exit_on_anykey,
  44683. const bool reset_view3d,
  44684. const bool force_display_z_coord,
  44685. const bool is_deep_selection_default) const {
  44686. if (is_empty()) return CImg<intT>(1,feature_type==0?3:6,1,1,-1);
  44687. if (!disp) {
  44688. disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1);
  44689. if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum);
  44690. } else if (title) disp.set_title("%s",title);
  44691. CImg<T> thumb;
  44692. if (width()>disp.screen_width() || height()>disp.screen_height())
  44693. get_resize(cimg_fitscreen(width(),height(),depth()),depth(),-100).move_to(thumb);
  44694. const unsigned int old_normalization = disp.normalization();
  44695. bool old_is_resized = disp.is_resized();
  44696. disp._normalization = 0;
  44697. disp.show().set_key(0).set_wheel().show_mouse();
  44698. static const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 };
  44699. int area = 0, area_started = 0, area_clicked = 0, phase = 0,
  44700. X0 = (int)((XYZ?XYZ[0]:(_width - 1)/2)%_width),
  44701. Y0 = (int)((XYZ?XYZ[1]:(_height - 1)/2)%_height),
  44702. Z0 = (int)((XYZ?XYZ[2]:(_depth - 1)/2)%_depth),
  44703. X1 =-1, Y1 = -1, Z1 = -1,
  44704. X3d = -1, Y3d = -1,
  44705. oX3d = X3d, oY3d = -1,
  44706. omx = -1, omy = -1;
  44707. float X = -1, Y = -1, Z = -1;
  44708. unsigned int key = 0;
  44709. bool is_deep_selection = is_deep_selection_default,
  44710. shape_selected = false, text_down = false, visible_cursor = true;
  44711. static CImg<floatT> pose3d;
  44712. static bool is_view3d = false, is_axes = true;
  44713. if (reset_view3d) { pose3d.assign(); is_view3d = false; }
  44714. CImg<floatT> points3d, opacities3d, sel_opacities3d;
  44715. CImgList<uintT> primitives3d, sel_primitives3d;
  44716. CImgList<ucharT> colors3d, sel_colors3d;
  44717. CImg<ucharT> visu, visu0, view3d;
  44718. CImg<charT> text(1024); *text = 0;
  44719. while (!key && !disp.is_closed() && !shape_selected) {
  44720. // Handle mouse motion and selection
  44721. int
  44722. mx = disp.mouse_x(),
  44723. my = disp.mouse_y();
  44724. const float
  44725. mX = mx<0?-1.0f:(float)mx*(width() + (depth()>1?depth():0))/disp.width(),
  44726. mY = my<0?-1.0f:(float)my*(height() + (depth()>1?depth():0))/disp.height();
  44727. area = 0;
  44728. if (mX>=0 && mY>=0 && mX<width() && mY<height()) { area = 1; X = mX; Y = mY; Z = (float)(phase?Z1:Z0); }
  44729. if (mX>=0 && mX<width() && mY>=height()) { area = 2; X = mX; Z = mY - _height; Y = (float)(phase?Y1:Y0); }
  44730. if (mY>=0 && mX>=width() && mY<height()) { area = 3; Y = mY; Z = mX - _width; X = (float)(phase?X1:X0); }
  44731. if (mX>=width() && mY>=height()) area = 4;
  44732. if (disp.button()) { if (!area_clicked) area_clicked = area; } else area_clicked = 0;
  44733. CImg<charT> filename(32);
  44734. switch (key = disp.key()) {
  44735. #if cimg_OS!=2
  44736. case cimg::keyCTRLRIGHT :
  44737. #endif
  44738. case 0 : case cimg::keyCTRLLEFT : key = 0; break;
  44739. case cimg::keyPAGEUP :
  44740. if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(1); key = 0; } break;
  44741. case cimg::keyPAGEDOWN :
  44742. if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(-1); key = 0; } break;
  44743. case cimg::keyA : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  44744. is_axes = !is_axes; disp.set_key(key,false); key = 0; visu0.assign();
  44745. } break;
  44746. case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  44747. disp.set_fullscreen(false).
  44748. resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
  44749. CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
  44750. _is_resized = true;
  44751. disp.set_key(key,false); key = 0; visu0.assign();
  44752. } break;
  44753. case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  44754. disp.set_fullscreen(false).
  44755. resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
  44756. disp.set_key(key,false); key = 0; visu0.assign();
  44757. } break;
  44758. case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  44759. disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true;
  44760. disp.set_key(key,false); key = 0; visu0.assign();
  44761. } break;
  44762. case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  44763. disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
  44764. disp.set_key(key,false); key = 0; visu0.assign();
  44765. } break;
  44766. case cimg::keyV : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  44767. is_view3d = !is_view3d; disp.set_key(key,false); key = 0; visu0.assign();
  44768. } break;
  44769. case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  44770. static unsigned int snap_number = 0;
  44771. std::FILE *file;
  44772. do {
  44773. cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++);
  44774. if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
  44775. } while (file);
  44776. if (visu0) {
  44777. (+visu0).draw_text(0,0," Saving snapshot... ",foreground_color,background_color,0.7f,13).display(disp);
  44778. visu0.save(filename);
  44779. (+visu0).draw_text(0,0," Snapshot '%s' saved. ",foreground_color,background_color,0.7f,13,filename._data).
  44780. display(disp);
  44781. }
  44782. disp.set_key(key,false); key = 0;
  44783. } break;
  44784. case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  44785. static unsigned int snap_number = 0;
  44786. std::FILE *file;
  44787. do {
  44788. #ifdef cimg_use_zlib
  44789. cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++);
  44790. #else
  44791. cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++);
  44792. #endif
  44793. if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
  44794. } while (file);
  44795. (+visu0).draw_text(0,0," Saving instance... ",foreground_color,background_color,0.7f,13).display(disp);
  44796. save(filename);
  44797. (+visu0).draw_text(0,0," Instance '%s' saved. ",foreground_color,background_color,0.7f,13,filename._data).
  44798. display(disp);
  44799. disp.set_key(key,false); key = 0;
  44800. } break;
  44801. }
  44802. switch (area) {
  44803. case 0 : // When mouse is out of image range.
  44804. mx = my = -1; X = Y = Z = -1;
  44805. break;
  44806. case 1 : case 2 : case 3 : { // When mouse is over the XY,XZ or YZ projections.
  44807. const unsigned int but = disp.button();
  44808. const bool b1 = (bool)(but&1), b2 = (bool)(but&2), b3 = (bool)(but&4);
  44809. if (b1 && phase==1 && area_clicked==area) { // When selection has been started (1st step).
  44810. if (_depth>1 && (X1!=(int)X || Y1!=(int)Y || Z1!=(int)Z)) visu0.assign();
  44811. X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z;
  44812. }
  44813. if (!b1 && phase==2 && area_clicked!=area) { // When selection is at 2nd step (for volumes).
  44814. switch (area_started) {
  44815. case 1 : if (Z1!=(int)Z) visu0.assign(); Z1 = (int)Z; break;
  44816. case 2 : if (Y1!=(int)Y) visu0.assign(); Y1 = (int)Y; break;
  44817. case 3 : if (X1!=(int)X) visu0.assign(); X1 = (int)X; break;
  44818. }
  44819. }
  44820. if (b2 && area_clicked==area) { // When moving through the image/volume.
  44821. if (phase) {
  44822. if (_depth>1 && (X1!=(int)X || Y1!=(int)Y || Z1!=(int)Z)) visu0.assign();
  44823. X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z;
  44824. } else {
  44825. if (_depth>1 && (X0!=(int)X || Y0!=(int)Y || Z0!=(int)Z)) visu0.assign();
  44826. X0 = (int)X; Y0 = (int)Y; Z0 = (int)Z;
  44827. }
  44828. }
  44829. if (b3) { // Reset selection
  44830. X = (float)X0; Y = (float)Y0; Z = (float)Z0; phase = area = area_clicked = area_started = 0;
  44831. visu0.assign();
  44832. }
  44833. if (disp.wheel()) { // When moving through the slices of the volume (with mouse wheel).
  44834. if (_depth>1 && !disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT() &&
  44835. !disp.is_keySHIFTLEFT() && !disp.is_keySHIFTRIGHT()) {
  44836. switch (area) {
  44837. case 1 :
  44838. if (phase) Z = (float)(Z1+=disp.wheel()); else Z = (float)(Z0+=disp.wheel());
  44839. visu0.assign(); break;
  44840. case 2 :
  44841. if (phase) Y = (float)(Y1+=disp.wheel()); else Y = (float)(Y0+=disp.wheel());
  44842. visu0.assign(); break;
  44843. case 3 :
  44844. if (phase) X = (float)(X1+=disp.wheel()); else X = (float)(X0+=disp.wheel());
  44845. visu0.assign(); break;
  44846. }
  44847. disp.set_wheel();
  44848. } else key = ~0U;
  44849. }
  44850. if ((phase==0 && b1) ||
  44851. (phase==1 && !b1) ||
  44852. (phase==2 && b1)) switch (phase) { // Detect change of phase
  44853. case 0 :
  44854. if (area==area_clicked) {
  44855. X0 = X1 = (int)X; Y0 = Y1 = (int)Y; Z0 = Z1 = (int)Z; area_started = area; ++phase;
  44856. } break;
  44857. case 1 :
  44858. if (area==area_started) {
  44859. X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z; ++phase;
  44860. if (_depth>1) {
  44861. if (disp.is_keyCTRLLEFT()) is_deep_selection = !is_deep_selection_default;
  44862. if (is_deep_selection) ++phase;
  44863. }
  44864. } else if (!b1) { X = (float)X0; Y = (float)Y0; Z = (float)Z0; phase = 0; visu0.assign(); }
  44865. break;
  44866. case 2 : ++phase; break;
  44867. }
  44868. } break;
  44869. case 4 : // When mouse is over the 3d view.
  44870. if (is_view3d && points3d) {
  44871. X3d = mx - width()*disp.width()/(width() + (depth()>1?depth():0));
  44872. Y3d = my - height()*disp.height()/(height() + (depth()>1?depth():0));
  44873. if (oX3d<0) { oX3d = X3d; oY3d = Y3d; }
  44874. // Left + right buttons: reset.
  44875. if ((disp.button()&3)==3) { pose3d.assign(); view3d.assign(); oX3d = oY3d = X3d = Y3d = -1; }
  44876. else if (disp.button()&1 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Left button: rotate.
  44877. const float
  44878. R = 0.45f*std::min(view3d._width,view3d._height),
  44879. R2 = R*R,
  44880. u0 = (float)(oX3d - view3d.width()/2),
  44881. v0 = (float)(oY3d - view3d.height()/2),
  44882. u1 = (float)(X3d - view3d.width()/2),
  44883. v1 = (float)(Y3d - view3d.height()/2),
  44884. n0 = cimg::hypot(u0,v0),
  44885. n1 = cimg::hypot(u1,v1),
  44886. nu0 = n0>R?(u0*R/n0):u0,
  44887. nv0 = n0>R?(v0*R/n0):v0,
  44888. nw0 = (float)std::sqrt(std::max(0.0f,R2 - nu0*nu0 - nv0*nv0)),
  44889. nu1 = n1>R?(u1*R/n1):u1,
  44890. nv1 = n1>R?(v1*R/n1):v1,
  44891. nw1 = (float)std::sqrt(std::max(0.0f,R2 - nu1*nu1 - nv1*nv1)),
  44892. u = nv0*nw1 - nw0*nv1,
  44893. v = nw0*nu1 - nu0*nw1,
  44894. w = nv0*nu1 - nu0*nv1,
  44895. n = cimg::hypot(u,v,w),
  44896. alpha = (float)std::asin(n/R2)*180/cimg::PI;
  44897. pose3d.draw_image(CImg<floatT>::rotation_matrix(u,v,w,-alpha)*pose3d.get_crop(0,0,2,2));
  44898. view3d.assign();
  44899. } else if (disp.button()&2 && pose3d && oY3d!=Y3d) { // Right button: zoom.
  44900. pose3d(3,2)+=(oY3d - Y3d)*1.5f; view3d.assign();
  44901. }
  44902. if (disp.wheel()) { // Wheel: zoom
  44903. pose3d(3,2)-=disp.wheel()*15; view3d.assign(); disp.set_wheel();
  44904. }
  44905. if (disp.button()&4 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Middle button: shift.
  44906. pose3d(3,0)-=oX3d - X3d; pose3d(3,1)-=oY3d - Y3d; view3d.assign();
  44907. }
  44908. oX3d = X3d; oY3d = Y3d;
  44909. }
  44910. mx = my = -1; X = Y = Z = -1;
  44911. break;
  44912. }
  44913. if (phase) {
  44914. if (!feature_type) shape_selected = phase?true:false;
  44915. else {
  44916. if (_depth>1) shape_selected = (phase==3)?true:false;
  44917. else shape_selected = (phase==2)?true:false;
  44918. }
  44919. }
  44920. if (X0<0) X0 = 0;
  44921. if (X0>=width()) X0 = width() - 1;
  44922. if (Y0<0) Y0 = 0;
  44923. if (Y0>=height()) Y0 = height() - 1;
  44924. if (Z0<0) Z0 = 0;
  44925. if (Z0>=depth()) Z0 = depth() - 1;
  44926. if (X1<1) X1 = 0;
  44927. if (X1>=width()) X1 = width() - 1;
  44928. if (Y1<0) Y1 = 0;
  44929. if (Y1>=height()) Y1 = height() - 1;
  44930. if (Z1<0) Z1 = 0;
  44931. if (Z1>=depth()) Z1 = depth() - 1;
  44932. // Draw visualization image on the display
  44933. if (mx!=omx || my!=omy || !visu0 || (_depth>1 && !view3d)) {
  44934. if (!visu0) { // Create image of projected planes.
  44935. if (thumb) thumb.__get_select(disp,old_normalization,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0).move_to(visu0);
  44936. else __get_select(disp,old_normalization,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0).move_to(visu0);
  44937. visu0.resize(disp);
  44938. view3d.assign();
  44939. points3d.assign();
  44940. }
  44941. if (is_view3d && _depth>1 && !view3d) { // Create 3d view for volumetric images.
  44942. const unsigned int
  44943. _x3d = (unsigned int)cimg::round((float)_width*visu0._width/(_width + _depth),1,1),
  44944. _y3d = (unsigned int)cimg::round((float)_height*visu0._height/(_height + _depth),1,1),
  44945. x3d = _x3d>=visu0._width?visu0._width - 1:_x3d,
  44946. y3d = _y3d>=visu0._height?visu0._height - 1:_y3d;
  44947. CImg<ucharT>(1,2,1,1,64,128).resize(visu0._width - x3d,visu0._height - y3d,1,visu0._spectrum,3).
  44948. move_to(view3d);
  44949. if (!points3d) {
  44950. get_projections3d(primitives3d,colors3d,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0,true).move_to(points3d);
  44951. points3d.append(CImg<floatT>(8,3,1,1,
  44952. 0,_width - 1,_width - 1,0,0,_width - 1,_width - 1,0,
  44953. 0,0,_height - 1,_height - 1,0,0,_height - 1,_height - 1,
  44954. 0,0,0,0,_depth - 1,_depth - 1,_depth - 1,_depth - 1),'x');
  44955. CImg<uintT>::vector(12,13).move_to(primitives3d); CImg<uintT>::vector(13,14).move_to(primitives3d);
  44956. CImg<uintT>::vector(14,15).move_to(primitives3d); CImg<uintT>::vector(15,12).move_to(primitives3d);
  44957. CImg<uintT>::vector(16,17).move_to(primitives3d); CImg<uintT>::vector(17,18).move_to(primitives3d);
  44958. CImg<uintT>::vector(18,19).move_to(primitives3d); CImg<uintT>::vector(19,16).move_to(primitives3d);
  44959. CImg<uintT>::vector(12,16).move_to(primitives3d); CImg<uintT>::vector(13,17).move_to(primitives3d);
  44960. CImg<uintT>::vector(14,18).move_to(primitives3d); CImg<uintT>::vector(15,19).move_to(primitives3d);
  44961. colors3d.insert(12,CImg<ucharT>::vector(255,255,255));
  44962. opacities3d.assign(primitives3d.width(),1,1,1,0.5f);
  44963. if (!phase) {
  44964. opacities3d[0] = opacities3d[1] = opacities3d[2] = 0.8f;
  44965. sel_primitives3d.assign();
  44966. sel_colors3d.assign();
  44967. sel_opacities3d.assign();
  44968. } else {
  44969. if (feature_type==2) {
  44970. points3d.append(CImg<floatT>(8,3,1,1,
  44971. X0,X1,X1,X0,X0,X1,X1,X0,
  44972. Y0,Y0,Y1,Y1,Y0,Y0,Y1,Y1,
  44973. Z0,Z0,Z0,Z0,Z1,Z1,Z1,Z1),'x');
  44974. sel_primitives3d.assign();
  44975. CImg<uintT>::vector(20,21).move_to(sel_primitives3d);
  44976. CImg<uintT>::vector(21,22).move_to(sel_primitives3d);
  44977. CImg<uintT>::vector(22,23).move_to(sel_primitives3d);
  44978. CImg<uintT>::vector(23,20).move_to(sel_primitives3d);
  44979. CImg<uintT>::vector(24,25).move_to(sel_primitives3d);
  44980. CImg<uintT>::vector(25,26).move_to(sel_primitives3d);
  44981. CImg<uintT>::vector(26,27).move_to(sel_primitives3d);
  44982. CImg<uintT>::vector(27,24).move_to(sel_primitives3d);
  44983. CImg<uintT>::vector(20,24).move_to(sel_primitives3d);
  44984. CImg<uintT>::vector(21,25).move_to(sel_primitives3d);
  44985. CImg<uintT>::vector(22,26).move_to(sel_primitives3d);
  44986. CImg<uintT>::vector(23,27).move_to(sel_primitives3d);
  44987. } else {
  44988. points3d.append(CImg<floatT>(2,3,1,1,
  44989. X0,X1,
  44990. Y0,Y1,
  44991. Z0,Z1),'x');
  44992. sel_primitives3d.assign(CImg<uintT>::vector(20,21));
  44993. }
  44994. sel_colors3d.assign(sel_primitives3d._width,CImg<ucharT>::vector(255,255,255));
  44995. sel_opacities3d.assign(sel_primitives3d._width,1,1,1,0.8f);
  44996. }
  44997. points3d.shift_object3d(-0.5f*(_width - 1),-0.5f*(_height - 1),-0.5f*(_depth - 1)).resize_object3d();
  44998. points3d*=0.75f*std::min(view3d._width,view3d._height);
  44999. }
  45000. if (!pose3d) CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose3d);
  45001. CImg<floatT> zbuffer3d(view3d._width,view3d._height,1,1,0);
  45002. const CImg<floatT> rotated_points3d = pose3d.get_crop(0,0,2,2)*points3d;
  45003. if (sel_primitives3d)
  45004. view3d.draw_object3d(pose3d(3,0) + 0.5f*view3d._width,
  45005. pose3d(3,1) + 0.5f*view3d._height,
  45006. pose3d(3,2),
  45007. rotated_points3d,sel_primitives3d,sel_colors3d,sel_opacities3d,
  45008. 2,true,500,0,0,0,0,0,zbuffer3d);
  45009. view3d.draw_object3d(pose3d(3,0) + 0.5f*view3d._width,
  45010. pose3d(3,1) + 0.5f*view3d._height,
  45011. pose3d(3,2),
  45012. rotated_points3d,primitives3d,colors3d,opacities3d,
  45013. 2,true,500,0,0,0,0,0,zbuffer3d);
  45014. visu0.draw_image(x3d,y3d,view3d);
  45015. }
  45016. visu = visu0;
  45017. if (X<0 || Y<0 || Z<0) { if (!visible_cursor) { disp.show_mouse(); visible_cursor = true; }}
  45018. else {
  45019. if (is_axes) { if (visible_cursor) { disp.hide_mouse(); visible_cursor = false; }}
  45020. else { if (!visible_cursor) { disp.show_mouse(); visible_cursor = true; }}
  45021. const int d = (depth()>1)?depth():0;
  45022. int _vX = (int)X, _vY = (int)Y, _vZ = (int)Z;
  45023. if (phase>=2) { _vX = X1; _vY = Y1; _vZ = Z1; }
  45024. int
  45025. w = disp.width(), W = width() + d,
  45026. h = disp.height(), H = height() + d,
  45027. _xp = (int)(_vX*(float)w/W), xp = _xp + ((int)(_xp*(float)W/w)!=_vX),
  45028. _yp = (int)(_vY*(float)h/H), yp = _yp + ((int)(_yp*(float)H/h)!=_vY),
  45029. _xn = (int)((_vX + 1.0f)*w/W - 1), xn = _xn + ((int)((_xn + 1.0f)*W/w)!=_vX + 1),
  45030. _yn = (int)((_vY + 1.0f)*h/H - 1), yn = _yn + ((int)((_yn + 1.0f)*H/h)!=_vY + 1),
  45031. _zxp = (int)((_vZ + width())*(float)w/W), zxp = _zxp + ((int)(_zxp*(float)W/w)!=_vZ + width()),
  45032. _zyp = (int)((_vZ + height())*(float)h/H), zyp = _zyp + ((int)(_zyp*(float)H/h)!=_vZ + height()),
  45033. _zxn = (int)((_vZ + width() + 1.0f)*w/W - 1),
  45034. zxn = _zxn + ((int)((_zxn + 1.0f)*W/w)!=_vZ + width() + 1),
  45035. _zyn = (int)((_vZ + height() + 1.0f)*h/H - 1),
  45036. zyn = _zyn + ((int)((_zyn + 1.0f)*H/h)!=_vZ + height() + 1),
  45037. _xM = (int)(width()*(float)w/W - 1), xM = _xM + ((int)((_xM + 1.0f)*W/w)!=width()),
  45038. _yM = (int)(height()*(float)h/H - 1), yM = _yM + ((int)((_yM + 1.0f)*H/h)!=height()),
  45039. xc = (xp + xn)/2,
  45040. yc = (yp + yn)/2,
  45041. zxc = (zxp + zxn)/2,
  45042. zyc = (zyp + zyn)/2,
  45043. xf = (int)(X*w/W),
  45044. yf = (int)(Y*h/H),
  45045. zxf = (int)((Z + width())*w/W),
  45046. zyf = (int)((Z + height())*h/H);
  45047. if (is_axes) { // Draw axes.
  45048. visu.draw_line(0,yf,visu.width() - 1,yf,foreground_color,0.7f,0xFF00FF00).
  45049. draw_line(0,yf,visu.width() - 1,yf,background_color,0.7f,0x00FF00FF).
  45050. draw_line(xf,0,xf,visu.height() - 1,foreground_color,0.7f,0xFF00FF00).
  45051. draw_line(xf,0,xf,visu.height() - 1,background_color,0.7f,0x00FF00FF);
  45052. if (_depth>1)
  45053. visu.draw_line(zxf,0,zxf,yM,foreground_color,0.7f,0xFF00FF00).
  45054. draw_line(zxf,0,zxf,yM,background_color,0.7f,0x00FF00FF).
  45055. draw_line(0,zyf,xM,zyf,foreground_color,0.7f,0xFF00FF00).
  45056. draw_line(0,zyf,xM,zyf,background_color,0.7f,0x00FF00FF);
  45057. }
  45058. // Draw box cursor.
  45059. if (xn - xp>=4 && yn - yp>=4)
  45060. visu.draw_rectangle(xp,yp,xn,yn,foreground_color,0.2f).
  45061. draw_rectangle(xp,yp,xn,yn,foreground_color,1,0xAAAAAAAA).
  45062. draw_rectangle(xp,yp,xn,yn,background_color,1,0x55555555);
  45063. if (_depth>1) {
  45064. if (yn - yp>=4 && zxn - zxp>=4)
  45065. visu.draw_rectangle(zxp,yp,zxn,yn,background_color,0.2f).
  45066. draw_rectangle(zxp,yp,zxn,yn,foreground_color,1,0xAAAAAAAA).
  45067. draw_rectangle(zxp,yp,zxn,yn,background_color,1,0x55555555);
  45068. if (xn - xp>=4 && zyn - zyp>=4)
  45069. visu.draw_rectangle(xp,zyp,xn,zyn,background_color,0.2f).
  45070. draw_rectangle(xp,zyp,xn,zyn,foreground_color,1,0xAAAAAAAA).
  45071. draw_rectangle(xp,zyp,xn,zyn,background_color,1,0x55555555);
  45072. }
  45073. // Draw selection.
  45074. if (phase && (phase!=1 || area_started==area)) {
  45075. const int
  45076. _xp0 = (int)(X0*(float)w/W), xp0 = _xp0 + ((int)(_xp0*(float)W/w)!=X0),
  45077. _yp0 = (int)(Y0*(float)h/H), yp0 = _yp0 + ((int)(_yp0*(float)H/h)!=Y0),
  45078. _xn0 = (int)((X0 + 1.0f)*w/W - 1), xn0 = _xn0 + ((int)((_xn0 + 1.0f)*W/w)!=X0 + 1),
  45079. _yn0 = (int)((Y0 + 1.0f)*h/H - 1), yn0 = _yn0 + ((int)((_yn0 + 1.0f)*H/h)!=Y0 + 1),
  45080. _zxp0 = (int)((Z0 + width())*(float)w/W), zxp0 = _zxp0 + ((int)(_zxp0*(float)W/w)!=Z0 + width()),
  45081. _zyp0 = (int)((Z0 + height())*(float)h/H), zyp0 = _zyp0 + ((int)(_zyp0*(float)H/h)!=Z0 + height()),
  45082. _zxn0 = (int)((Z0 + width() + 1.0f)*w/W - 1),
  45083. zxn0 = _zxn0 + ((int)((_zxn0 + 1.0f)*W/w)!=Z0 + width() + 1),
  45084. _zyn0 = (int)((Z0 + height() + 1.0f)*h/H - 1),
  45085. zyn0 = _zyn0 + ((int)((_zyn0 + 1.0f)*H/h)!=Z0 + height() + 1),
  45086. xc0 = (xp0 + xn0)/2,
  45087. yc0 = (yp0 + yn0)/2,
  45088. zxc0 = (zxp0 + zxn0)/2,
  45089. zyc0 = (zyp0 + zyn0)/2;
  45090. switch (feature_type) {
  45091. case 1 : {
  45092. visu.draw_arrow(xc0,yc0,xc,yc,background_color,0.9f,30,5,0x55555555).
  45093. draw_arrow(xc0,yc0,xc,yc,foreground_color,0.9f,30,5,0xAAAAAAAA);
  45094. if (d) {
  45095. visu.draw_arrow(zxc0,yc0,zxc,yc,background_color,0.9f,30,5,0x55555555).
  45096. draw_arrow(zxc0,yc0,zxc,yc,foreground_color,0.9f,30,5,0xAAAAAAAA).
  45097. draw_arrow(xc0,zyc0,xc,zyc,background_color,0.9f,30,5,0x55555555).
  45098. draw_arrow(xc0,zyc0,xc,zyc,foreground_color,0.9f,30,5,0xAAAAAAAA);
  45099. }
  45100. } break;
  45101. case 2 : {
  45102. visu.draw_rectangle(X0<X1?xp0:xp,Y0<Y1?yp0:yp,X0<X1?xn:xn0,Y0<Y1?yn:yn0,background_color,0.2f).
  45103. draw_rectangle(X0<X1?xp0:xp,Y0<Y1?yp0:yp,X0<X1?xn:xn0,Y0<Y1?yn:yn0,background_color,0.9f,0x55555555).
  45104. draw_rectangle(X0<X1?xp0:xp,Y0<Y1?yp0:yp,X0<X1?xn:xn0,Y0<Y1?yn:yn0,foreground_color,0.9f,0xAAAAAAAA).
  45105. draw_arrow(xc0,yc0,xc,yc,background_color,0.5f,30,5,0x55555555).
  45106. draw_arrow(xc0,yc0,xc,yc,foreground_color,0.5f,30,5,0xAAAAAAAA);
  45107. if (d) {
  45108. visu.draw_rectangle(Z0<Z1?zxp0:zxp,Y0<Y1?yp0:yp,Z0<Z1?zxn:zxn0,Y0<Y1?yn:yn0,background_color,0.2f).
  45109. draw_rectangle(Z0<Z1?zxp0:zxp,Y0<Y1?yp0:yp,Z0<Z1?zxn:zxn0,Y0<Y1?yn:yn0,
  45110. background_color,0.9f,0x55555555).
  45111. draw_rectangle(Z0<Z1?zxp0:zxp,Y0<Y1?yp0:yp,Z0<Z1?zxn:zxn0,Y0<Y1?yn:yn0,
  45112. foreground_color,0.9f,0xAAAAAAAA).
  45113. draw_arrow(zxc0,yc0,zxc,yc,background_color,0.5f,30,5,0x55555555).
  45114. draw_arrow(zxc0,yc0,zxc,yc,foreground_color,0.5f,30,5,0xAAAAAAAA).
  45115. draw_rectangle(X0<X1?xp0:xp,Z0<Z1?zyp0:zyp,X0<X1?xn:xn0,Z0<Z1?zyn:zyn0,
  45116. background_color,0.2f).
  45117. draw_rectangle(X0<X1?xp0:xp,Z0<Z1?zyp0:zyp,X0<X1?xn:xn0,Z0<Z1?zyn:zyn0,
  45118. background_color,0.9f,0x55555555).
  45119. draw_rectangle(X0<X1?xp0:xp,Z0<Z1?zyp0:zyp,X0<X1?xn:xn0,Z0<Z1?zyn:zyn0,
  45120. foreground_color,0.9f,0xAAAAAAAA).
  45121. draw_arrow(xp0,zyp0,xn,zyn,background_color,0.5f,30,5,0x55555555).
  45122. draw_arrow(xp0,zyp0,xn,zyn,foreground_color,0.5f,30,5,0xAAAAAAAA);
  45123. }
  45124. } break;
  45125. case 3 : {
  45126. visu.draw_ellipse(xc0,yc0,
  45127. (float)cimg::abs(xc - xc0),
  45128. (float)cimg::abs(yc - yc0),0,background_color,0.2f).
  45129. draw_ellipse(xc0,yc0,
  45130. (float)cimg::abs(xc - xc0),
  45131. (float)cimg::abs(yc - yc0),0,foreground_color,0.9f,~0U).
  45132. draw_point(xc0,yc0,foreground_color,0.9f);
  45133. if (d) {
  45134. visu.draw_ellipse(zxc0,yc0,(float)cimg::abs(zxc - zxc0),(float)cimg::abs(yc - yc0),0,
  45135. background_color,0.2f).
  45136. draw_ellipse(zxc0,yc0,(float)cimg::abs(zxc - zxc0),(float)cimg::abs(yc - yc0),0,
  45137. foreground_color,0.9f,~0U).
  45138. draw_point(zxc0,yc0,foreground_color,0.9f).
  45139. draw_ellipse(xc0,zyc0,(float)cimg::abs(xc - xc0),(float)cimg::abs(zyc - zyc0),0,
  45140. background_color,0.2f).
  45141. draw_ellipse(xc0,zyc0,(float)cimg::abs(xc - xc0),(float)cimg::abs(zyc - zyc0),0,
  45142. foreground_color,0.9f,~0U).
  45143. draw_point(xc0,zyc0,foreground_color,0.9f);
  45144. }
  45145. } break;
  45146. }
  45147. }
  45148. // Draw text info.
  45149. if (my>=0 && my<13) text_down = true; else if (my>=visu.height() - 13) text_down = false;
  45150. if (!feature_type || !phase) {
  45151. if (X>=0 && Y>=0 && Z>=0 && X<width() && Y<height() && Z<depth()) {
  45152. if (_depth>1 || force_display_z_coord)
  45153. cimg_snprintf(text,text._width," Point (%d,%d,%d) = [ ",origX + (int)X,origY + (int)Y,origZ + (int)Z);
  45154. else cimg_snprintf(text,text._width," Point (%d,%d) = [ ",origX + (int)X,origY + (int)Y);
  45155. CImg<T> values = get_vector_at((int)X,(int)Y,(int)Z);
  45156. const bool is_large_spectrum = values._height>16;
  45157. if (is_large_spectrum)
  45158. values.draw_image(0,8,values.get_rows(values._height - 8,values._height - 1)).resize(1,16,1,1,0);
  45159. char *ctext = text._data + std::strlen(text), *const ltext = text._data + 512;
  45160. for (unsigned int c = 0; c<values._height && ctext<ltext; ++c) {
  45161. cimg_snprintf(ctext,24,cimg::type<T>::format_s(),
  45162. cimg::type<T>::format(values[c]));
  45163. ctext += std::strlen(ctext);
  45164. if (c==7 && is_large_spectrum) {
  45165. cimg_snprintf(ctext,24," (...)");
  45166. ctext += std::strlen(ctext);
  45167. }
  45168. *(ctext++) = ' '; *ctext = 0;
  45169. }
  45170. std::strcpy(text._data + std::strlen(text),"] ");
  45171. }
  45172. } else switch (feature_type) {
  45173. case 1 : {
  45174. const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1),
  45175. length = cimg::round(cimg::hypot(dX,dY,dZ),0.1);
  45176. if (_depth>1 || force_display_z_coord)
  45177. cimg_snprintf(text,text._width," Vect (%d,%d,%d)-(%d,%d,%d), Length = %g ",
  45178. origX + X0,origY + Y0,origZ + Z0,origX + X1,origY + Y1,origZ + Z1,length);
  45179. else cimg_snprintf(text,text._width," Vect (%d,%d)-(%d,%d), Length = %g, Angle = %g\260 ",
  45180. origX + X0,origY + Y0,origX + X1,origY + Y1,length,
  45181. cimg::round(cimg::mod(180*std::atan2(-dY,-dX)/cimg::PI,360.),0.1));
  45182. } break;
  45183. case 2 : {
  45184. const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1),
  45185. length = cimg::round(cimg::hypot(dX,dY,dZ),0.1);
  45186. if (_depth>1 || force_display_z_coord)
  45187. cimg_snprintf(text,text._width,
  45188. " Box (%d,%d,%d)-(%d,%d,%d), Size = (%d,%d,%d), Length = %g ",
  45189. origX + (X0<X1?X0:X1),origY + (Y0<Y1?Y0:Y1),origZ + (Z0<Z1?Z0:Z1),
  45190. origX + (X0<X1?X1:X0),origY + (Y0<Y1?Y1:Y0),origZ + (Z0<Z1?Z1:Z0),
  45191. 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),1 + cimg::abs(Z0 - Z1),length);
  45192. else cimg_snprintf(text,text._width,
  45193. " Box (%d,%d)-(%d,%d), Size = (%d,%d), Length = %g, Angle = %g\260 ",
  45194. origX + (X0<X1?X0:X1),origY + (Y0<Y1?Y0:Y1),
  45195. origX + (X0<X1?X1:X0),origY + (Y0<Y1?Y1:Y0),
  45196. 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),length,
  45197. cimg::round(cimg::mod(180*std::atan2(-dY,-dX)/cimg::PI,360.),0.1));
  45198. } break;
  45199. default :
  45200. if (_depth>1 || force_display_z_coord)
  45201. cimg_snprintf(text,text._width," Ellipse (%d,%d,%d)-(%d,%d,%d), Radii = (%d,%d,%d) ",
  45202. origX + X0,origY + Y0,origZ + Z0,origX + X1,origY + Y1,origZ + Z1,
  45203. 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),1 + cimg::abs(Z0 - Z1));
  45204. else cimg_snprintf(text,text._width," Ellipse (%d,%d)-(%d,%d), Radii = (%d,%d) ",
  45205. origX + X0,origY + Y0,origX + X1,origY + Y1,
  45206. 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1));
  45207. }
  45208. if (phase || (mx>=0 && my>=0))
  45209. visu.draw_text(0,text_down?visu.height() - 13:0,text,foreground_color,background_color,0.7f,13);
  45210. }
  45211. disp.display(visu);
  45212. }
  45213. if (!shape_selected) disp.wait();
  45214. if (disp.is_resized()) { disp.resize(false)._is_resized = false; old_is_resized = true; visu0.assign(); }
  45215. omx = mx; omy = my;
  45216. if (!exit_on_anykey && key && key!=cimg::keyESC &&
  45217. (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
  45218. key = 0;
  45219. }
  45220. }
  45221. // Return result.
  45222. CImg<intT> res(1,feature_type==0?3:6,1,1,-1);
  45223. if (XYZ) { XYZ[0] = (unsigned int)X0; XYZ[1] = (unsigned int)Y0; XYZ[2] = (unsigned int)Z0; }
  45224. if (shape_selected) {
  45225. if (feature_type==2) {
  45226. if (is_deep_selection) switch (area_started) {
  45227. case 1 : Z0 = 0; Z1 = _depth - 1; break;
  45228. case 2 : Y0 = 0; Y1 = _height - 1; break;
  45229. case 3 : X0 = 0; X1 = _width - 1; break;
  45230. }
  45231. if (X0>X1) cimg::swap(X0,X1);
  45232. if (Y0>Y1) cimg::swap(Y0,Y1);
  45233. if (Z0>Z1) cimg::swap(Z0,Z1);
  45234. }
  45235. if (X1<0 || Y1<0 || Z1<0) X0 = Y0 = Z0 = X1 = Y1 = Z1 = -1;
  45236. switch (feature_type) {
  45237. case 1 : case 2 : res[0] = X0; res[1] = Y0; res[2] = Z0; res[3] = X1; res[4] = Y1; res[5] = Z1; break;
  45238. case 3 :
  45239. res[3] = cimg::abs(X1 - X0); res[4] = cimg::abs(Y1 - Y0); res[5] = cimg::abs(Z1 - Z0);
  45240. res[0] = X0; res[1] = Y0; res[2] = Z0;
  45241. break;
  45242. default : res[0] = X0; res[1] = Y0; res[2] = Z0;
  45243. }
  45244. }
  45245. if (!exit_on_anykey || !(disp.button()&4)) disp.set_button();
  45246. if (!visible_cursor) disp.show_mouse();
  45247. disp._normalization = old_normalization;
  45248. disp._is_resized = old_is_resized;
  45249. if (key!=~0U) disp.set_key(key);
  45250. return res;
  45251. }
  45252. // Return a visualizable uchar8 image for display routines.
  45253. CImg<ucharT> __get_select(const CImgDisplay& disp, const int normalization,
  45254. const int x, const int y, const int z) const {
  45255. if (is_empty()) return CImg<ucharT>(1,1,1,1,0);
  45256. const CImg<T> crop = get_shared_channels(0,std::min(2,spectrum() - 1));
  45257. CImg<Tuchar> img2d;
  45258. if (_depth>1) {
  45259. const int mdisp = std::min(disp.screen_width(),disp.screen_height());
  45260. if (depth()>mdisp) {
  45261. crop.get_resize(-100,-100,mdisp,-100,0).move_to(img2d);
  45262. img2d.projections2d(x,y,z*img2d._depth/_depth);
  45263. } else crop.get_projections2d(x,y,z).move_to(img2d);
  45264. } else CImg<Tuchar>(crop,false).move_to(img2d);
  45265. // Check for inf and NaN values.
  45266. if (cimg::type<T>::is_float() && normalization) {
  45267. bool is_inf = false, is_nan = false;
  45268. cimg_for(img2d,ptr,Tuchar)
  45269. if (cimg::type<T>::is_inf(*ptr)) { is_inf = true; break; }
  45270. else if (cimg::type<T>::is_nan(*ptr)) { is_nan = true; break; }
  45271. if (is_inf || is_nan) {
  45272. Tint m0 = (Tint)cimg::type<T>::max(), M0 = (Tint)cimg::type<T>::min();
  45273. if (!normalization) { m0 = 0; M0 = 255; }
  45274. else if (normalization==2) { m0 = (Tint)disp._min; M0 = (Tint)disp._max; }
  45275. else
  45276. cimg_for(img2d,ptr,Tuchar)
  45277. if (!cimg::type<T>::is_inf(*ptr) && !cimg::type<T>::is_nan(*ptr)) {
  45278. if (*ptr<(Tuchar)m0) m0 = *ptr;
  45279. if (*ptr>(Tuchar)M0) M0 = *ptr;
  45280. }
  45281. const T
  45282. val_minf = (T)(normalization==1 || normalization==3?m0 - (M0 - m0)*20 - 1:m0),
  45283. val_pinf = (T)(normalization==1 || normalization==3?M0 + (M0 - m0)*20 + 1:M0);
  45284. if (is_nan)
  45285. cimg_for(img2d,ptr,Tuchar)
  45286. if (cimg::type<T>::is_nan(*ptr)) *ptr = val_minf; // Replace NaN values.
  45287. if (is_inf)
  45288. cimg_for(img2d,ptr,Tuchar)
  45289. if (cimg::type<T>::is_inf(*ptr)) *ptr = (float)*ptr<0?val_minf:val_pinf; // Replace +-inf values.
  45290. }
  45291. }
  45292. switch (normalization) {
  45293. case 1 : img2d.normalize((ucharT)0,(ucharT)255); break;
  45294. case 2 : {
  45295. const float m = disp._min, M = disp._max;
  45296. (img2d-=m)*=255.0f/(M - m>0?M - m:1);
  45297. } break;
  45298. case 3 :
  45299. if (cimg::type<T>::is_float()) img2d.normalize((ucharT)0,(ucharT)255);
  45300. else {
  45301. const float m = (float)cimg::type<T>::min(), M = (float)cimg::type<T>::max();
  45302. (img2d-=m)*=255.0f/(M - m>0?M - m:1);
  45303. } break;
  45304. }
  45305. if (img2d.spectrum()==2) img2d.channels(0,2);
  45306. return img2d;
  45307. }
  45308. //! Select sub-graph in a graph.
  45309. CImg<intT> get_select_graph(CImgDisplay &disp,
  45310. const unsigned int plot_type=1, const unsigned int vertex_type=1,
  45311. const char *const labelx=0, const double xmin=0, const double xmax=0,
  45312. const char *const labely=0, const double ymin=0, const double ymax=0,
  45313. const bool exit_on_anykey=false) const {
  45314. if (is_empty())
  45315. throw CImgInstanceException(_cimg_instance
  45316. "select_graph(): Empty instance.",
  45317. cimg_instance);
  45318. if (!disp) disp.assign(cimg_fitscreen(CImgDisplay::screen_width()/2,CImgDisplay::screen_height()/2,1),0,0).
  45319. set_title("CImg<%s>",pixel_type());
  45320. const ulongT siz = (ulongT)_width*_height*_depth;
  45321. const unsigned int old_normalization = disp.normalization();
  45322. disp.show().set_button().set_wheel()._normalization = 0;
  45323. double nymin = ymin, nymax = ymax, nxmin = xmin, nxmax = xmax;
  45324. if (nymin==nymax) { nymin = (Tfloat)min_max(nymax); const double dy = nymax - nymin; nymin-=dy/20; nymax+=dy/20; }
  45325. if (nymin==nymax) { --nymin; ++nymax; }
  45326. if (nxmin==nxmax && nxmin==0) { nxmin = 0; nxmax = siz - 1.0; }
  45327. static const unsigned char black[] = { 0, 0, 0 }, white[] = { 255, 255, 255 }, gray[] = { 220, 220, 220 };
  45328. static const unsigned char gray2[] = { 110, 110, 110 }, ngray[] = { 35, 35, 35 };
  45329. static unsigned int odimv = 0;
  45330. static CImg<ucharT> colormap;
  45331. if (odimv!=_spectrum) {
  45332. odimv = _spectrum;
  45333. colormap = CImg<ucharT>(3,_spectrum,1,1,120).noise(70,1);
  45334. if (_spectrum==1) { colormap[0] = colormap[1] = 120; colormap[2] = 200; }
  45335. else {
  45336. colormap(0,0) = 220; colormap(1,0) = 10; colormap(2,0) = 10;
  45337. if (_spectrum>1) { colormap(0,1) = 10; colormap(1,1) = 220; colormap(2,1) = 10; }
  45338. if (_spectrum>2) { colormap(0,2) = 10; colormap(1,2) = 10; colormap(2,2) = 220; }
  45339. }
  45340. }
  45341. CImg<ucharT> visu0, visu, graph, text, axes;
  45342. int x0 = -1, x1 = -1, y0 = -1, y1 = -1, omouse_x = -2, omouse_y = -2;
  45343. const unsigned int one = plot_type==3?0U:1U;
  45344. unsigned int okey = 0, obutton = 0;
  45345. CImg<charT> message(1024);
  45346. CImg_3x3(I,unsigned char);
  45347. for (bool selected = false; !selected && !disp.is_closed() && !okey && !disp.wheel(); ) {
  45348. const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y();
  45349. const unsigned int key = disp.key(), button = disp.button();
  45350. // Generate graph representation.
  45351. if (!visu0) {
  45352. visu0.assign(disp.width(),disp.height(),1,3,220);
  45353. const int gdimx = disp.width() - 32, gdimy = disp.height() - 32;
  45354. if (gdimx>0 && gdimy>0) {
  45355. graph.assign(gdimx,gdimy,1,3,255);
  45356. if (siz<32) {
  45357. if (siz>1) graph.draw_grid(gdimx/(float)(siz - one),gdimy/(float)(siz - one),0,0,
  45358. false,true,black,0.2f,0x33333333,0x33333333);
  45359. } else graph.draw_grid(-10,-10,0,0,false,true,black,0.2f,0x33333333,0x33333333);
  45360. cimg_forC(*this,c)
  45361. graph.draw_graph(get_shared_channel(c),&colormap(0,c),(plot_type!=3 || _spectrum==1)?1:0.6f,
  45362. plot_type,vertex_type,nymax,nymin);
  45363. axes.assign(gdimx,gdimy,1,1,0);
  45364. const float
  45365. dx = (float)cimg::abs(nxmax - nxmin), dy = (float)cimg::abs(nymax - nymin),
  45366. px = (float)std::pow(10.0,(int)std::log10(dx?dx:1) - 2.0),
  45367. py = (float)std::pow(10.0,(int)std::log10(dy?dy:1) - 2.0);
  45368. const CImg<Tdouble>
  45369. seqx = dx<=0?CImg<Tdouble>::vector(nxmin):
  45370. CImg<Tdouble>::sequence(1 + gdimx/60,nxmin,one?nxmax:nxmin + (nxmax - nxmin)*(siz + 1)/siz).round(px),
  45371. seqy = CImg<Tdouble>::sequence(1 + gdimy/60,nymax,nymin).round(py);
  45372. const bool allow_zero = (nxmin*nxmax>0) || (nymin*nymax>0);
  45373. axes.draw_axes(seqx,seqy,white,1,~0U,~0U,13,allow_zero);
  45374. if (nymin>0) axes.draw_axis(seqx,gdimy - 1,gray,1,~0U,13,allow_zero);
  45375. if (nymax<0) axes.draw_axis(seqx,0,gray,1,~0U,13,allow_zero);
  45376. if (nxmin>0) axes.draw_axis(0,seqy,gray,1,~0U,13,allow_zero);
  45377. if (nxmax<0) axes.draw_axis(gdimx - 1,seqy,gray,1,~0U,13,allow_zero);
  45378. cimg_for3x3(axes,x,y,0,0,I,unsigned char)
  45379. if (Icc) {
  45380. if (Icc==255) cimg_forC(graph,c) graph(x,y,c) = 0;
  45381. else cimg_forC(graph,c) graph(x,y,c) = (unsigned char)(2*graph(x,y,c)/3);
  45382. }
  45383. else if (Ipc || Inc || Icp || Icn || Ipp || Inn || Ipn || Inp)
  45384. cimg_forC(graph,c) graph(x,y,c) = (unsigned char)((graph(x,y,c) + 511)/3);
  45385. visu0.draw_image(16,16,graph);
  45386. visu0.draw_line(15,15,16 + gdimx,15,gray2).draw_line(16 + gdimx,15,16 + gdimx,16 + gdimy,gray2).
  45387. draw_line(16 + gdimx,16 + gdimy,15,16 + gdimy,white).draw_line(15,16 + gdimy,15,15,white);
  45388. } else graph.assign();
  45389. text.assign().draw_text(0,0,labelx?labelx:"X-axis",white,ngray,1,13).resize(-100,-100,1,3);
  45390. visu0.draw_image((visu0.width() - text.width())/2,visu0.height() - 14,~text);
  45391. text.assign().draw_text(0,0,labely?labely:"Y-axis",white,ngray,1,13).rotate(-90).resize(-100,-100,1,3);
  45392. visu0.draw_image(1,(visu0.height() - text.height())/2,~text);
  45393. visu.assign();
  45394. }
  45395. // Generate and display current view.
  45396. if (!visu) {
  45397. visu.assign(visu0);
  45398. if (graph && x0>=0 && x1>=0) {
  45399. const int
  45400. nx0 = x0<=x1?x0:x1,
  45401. nx1 = x0<=x1?x1:x0,
  45402. ny0 = y0<=y1?y0:y1,
  45403. ny1 = y0<=y1?y1:y0,
  45404. sx0 = (int)(16 + nx0*(visu.width() - 32)/std::max((ulongT)1,siz - one)),
  45405. sx1 = (int)(15 + (nx1 + 1)*(visu.width() - 32)/std::max((ulongT)1,siz - one)),
  45406. sy0 = 16 + ny0,
  45407. sy1 = 16 + ny1;
  45408. if (y0>=0 && y1>=0)
  45409. visu.draw_rectangle(sx0,sy0,sx1,sy1,gray,0.5f).draw_rectangle(sx0,sy0,sx1,sy1,black,0.5f,0xCCCCCCCCU);
  45410. else visu.draw_rectangle(sx0,0,sx1,visu.height() - 17,gray,0.5f).
  45411. draw_line(sx0,16,sx0,visu.height() - 17,black,0.5f,0xCCCCCCCCU).
  45412. draw_line(sx1,16,sx1,visu.height() - 17,black,0.5f,0xCCCCCCCCU);
  45413. }
  45414. if (mouse_x>=16 && mouse_y>=16 && mouse_x<visu.width() - 16 && mouse_y<visu.height() - 16) {
  45415. if (graph) visu.draw_line(mouse_x,16,mouse_x,visu.height() - 17,black,0.5f,0x55555555U);
  45416. const unsigned int
  45417. x = (unsigned int)cimg::round((mouse_x - 16.0f)*(siz - one)/(disp.width() - 32),1,one?0:-1);
  45418. const double cx = nxmin + x*(nxmax - nxmin)/std::max((ulongT)1,siz - 1);
  45419. if (_spectrum>=7)
  45420. cimg_snprintf(message,message._width,"Value[%u:%g] = ( %g %g %g ... %g %g %g )",x,cx,
  45421. (double)(*this)(x,0,0,0),(double)(*this)(x,0,0,1),(double)(*this)(x,0,0,2),
  45422. (double)(*this)(x,0,0,_spectrum - 4),(double)(*this)(x,0,0,_spectrum - 3),
  45423. (double)(*this)(x,0,0,_spectrum - 1));
  45424. else {
  45425. cimg_snprintf(message,message._width,"Value[%u:%g] = ( ",x,cx);
  45426. cimg_forC(*this,c) cimg_sprintf(message._data + std::strlen(message),"%g ",(double)(*this)(x,0,0,c));
  45427. cimg_sprintf(message._data + std::strlen(message),")");
  45428. }
  45429. if (x0>=0 && x1>=0) {
  45430. const unsigned int
  45431. nx0 = (unsigned int)(x0<=x1?x0:x1),
  45432. nx1 = (unsigned int)(x0<=x1?x1:x0),
  45433. ny0 = (unsigned int)(y0<=y1?y0:y1),
  45434. ny1 = (unsigned int)(y0<=y1?y1:y0);
  45435. const double
  45436. cx0 = nxmin + nx0*(nxmax - nxmin)/std::max((ulongT)1,siz - 1),
  45437. cx1 = nxmin + (nx1 + one)*(nxmax - nxmin)/std::max((ulongT)1,siz - 1),
  45438. cy0 = nymax - ny0*(nymax - nymin)/(visu._height - 32),
  45439. cy1 = nymax - ny1*(nymax - nymin)/(visu._height - 32);
  45440. if (y0>=0 && y1>=0)
  45441. cimg_sprintf(message._data + std::strlen(message)," - Range ( %u:%g, %g ) - ( %u:%g, %g )",
  45442. x0,cx0,cy0,x1 + one,cx1,cy1);
  45443. else
  45444. cimg_sprintf(message._data + std::strlen(message)," - Range [ %u:%g - %u:%g ]",
  45445. x0,cx0,x1 + one,cx1);
  45446. }
  45447. text.assign().draw_text(0,0,message,white,ngray,1,13).resize(-100,-100,1,3);
  45448. visu.draw_image((visu.width() - text.width())/2,1,~text);
  45449. }
  45450. visu.display(disp);
  45451. }
  45452. // Test keys.
  45453. CImg<charT> filename(32);
  45454. switch (okey = key) {
  45455. #if cimg_OS!=2
  45456. case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT :
  45457. #endif
  45458. case cimg::keyCTRLLEFT : case cimg::keySHIFTLEFT : okey = 0; break;
  45459. case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  45460. disp.set_fullscreen(false).
  45461. resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
  45462. CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
  45463. _is_resized = true;
  45464. disp.set_key(key,false); okey = 0;
  45465. } break;
  45466. case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  45467. disp.set_fullscreen(false).
  45468. resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
  45469. disp.set_key(key,false); okey = 0;
  45470. } break;
  45471. case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  45472. disp.set_fullscreen(false).
  45473. resize(cimg_fitscreen(CImgDisplay::screen_width()/2,
  45474. CImgDisplay::screen_height()/2,1),false)._is_resized = true;
  45475. disp.set_key(key,false); okey = 0;
  45476. } break;
  45477. case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  45478. disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
  45479. disp.set_key(key,false); okey = 0;
  45480. } break;
  45481. case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  45482. static unsigned int snap_number = 0;
  45483. if (visu || visu0) {
  45484. CImg<ucharT> &screen = visu?visu:visu0;
  45485. std::FILE *file;
  45486. do {
  45487. cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++);
  45488. if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
  45489. } while (file);
  45490. (+screen).draw_text(0,0," Saving snapshot... ",black,gray,1,13).display(disp);
  45491. screen.save(filename);
  45492. (+screen).draw_text(0,0," Snapshot '%s' saved. ",black,gray,1,13,filename._data).display(disp);
  45493. }
  45494. disp.set_key(key,false); okey = 0;
  45495. } break;
  45496. case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  45497. static unsigned int snap_number = 0;
  45498. if (visu || visu0) {
  45499. CImg<ucharT> &screen = visu?visu:visu0;
  45500. std::FILE *file;
  45501. do {
  45502. #ifdef cimg_use_zlib
  45503. cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++);
  45504. #else
  45505. cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++);
  45506. #endif
  45507. if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
  45508. } while (file);
  45509. (+screen).draw_text(0,0," Saving instance... ",black,gray,1,13).display(disp);
  45510. save(filename);
  45511. (+screen).draw_text(0,0," Instance '%s' saved. ",black,gray,1,13,filename._data).display(disp);
  45512. }
  45513. disp.set_key(key,false); okey = 0;
  45514. } break;
  45515. }
  45516. // Handle mouse motion and mouse buttons
  45517. if (obutton!=button || omouse_x!=mouse_x || omouse_y!=mouse_y) {
  45518. visu.assign();
  45519. if (disp.mouse_x()>=0 && disp.mouse_y()>=0) {
  45520. const int
  45521. mx = (mouse_x - 16)*(int)(siz - one)/(disp.width() - 32),
  45522. cx = cimg::cut(mx,0,(int)(siz - 1 - one)),
  45523. my = mouse_y - 16,
  45524. cy = cimg::cut(my,0,disp.height() - 32);
  45525. if (button&1) {
  45526. if (!obutton) { x0 = cx; y0 = -1; } else { x1 = cx; y1 = -1; }
  45527. }
  45528. else if (button&2) {
  45529. if (!obutton) { x0 = cx; y0 = cy; } else { x1 = cx; y1 = cy; }
  45530. }
  45531. else if (obutton) { x1 = x1>=0?cx:-1; y1 = y1>=0?cy:-1; selected = true; }
  45532. } else if (!button && obutton) selected = true;
  45533. obutton = button; omouse_x = mouse_x; omouse_y = mouse_y;
  45534. }
  45535. if (disp.is_resized()) { disp.resize(false); visu0.assign(); }
  45536. if (visu && visu0) disp.wait();
  45537. if (!exit_on_anykey && okey && okey!=cimg::keyESC &&
  45538. (okey!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
  45539. disp.set_key(key,false);
  45540. okey = 0;
  45541. }
  45542. }
  45543. disp._normalization = old_normalization;
  45544. if (x1>=0 && x1<x0) cimg::swap(x0,x1);
  45545. if (y1<y0) cimg::swap(y0,y1);
  45546. disp.set_key(okey);
  45547. return CImg<intT>(4,1,1,1,x0,y0,x1>=0?x1 + (int)one:-1,y1);
  45548. }
  45549. //! Load image from a file.
  45550. /**
  45551. \param filename Filename, as a C-string.
  45552. \note The extension of \c filename defines the file format. If no filename
  45553. extension is provided, CImg<T>::get_load() will try to load the file as a .cimg or .cimgz file.
  45554. **/
  45555. CImg<T>& load(const char *const filename) {
  45556. if (!filename)
  45557. throw CImgArgumentException(_cimg_instance
  45558. "load(): Specified filename is (null).",
  45559. cimg_instance);
  45560. if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) {
  45561. CImg<charT> filename_local(256);
  45562. load(cimg::load_network(filename,filename_local));
  45563. std::remove(filename_local);
  45564. return *this;
  45565. }
  45566. const char *const ext = cimg::split_filename(filename);
  45567. const unsigned int omode = cimg::exception_mode();
  45568. cimg::exception_mode(0);
  45569. bool is_loaded = true;
  45570. try {
  45571. #ifdef cimg_load_plugin
  45572. cimg_load_plugin(filename);
  45573. #endif
  45574. #ifdef cimg_load_plugin1
  45575. cimg_load_plugin1(filename);
  45576. #endif
  45577. #ifdef cimg_load_plugin2
  45578. cimg_load_plugin2(filename);
  45579. #endif
  45580. #ifdef cimg_load_plugin3
  45581. cimg_load_plugin3(filename);
  45582. #endif
  45583. #ifdef cimg_load_plugin4
  45584. cimg_load_plugin4(filename);
  45585. #endif
  45586. #ifdef cimg_load_plugin5
  45587. cimg_load_plugin5(filename);
  45588. #endif
  45589. #ifdef cimg_load_plugin6
  45590. cimg_load_plugin6(filename);
  45591. #endif
  45592. #ifdef cimg_load_plugin7
  45593. cimg_load_plugin7(filename);
  45594. #endif
  45595. #ifdef cimg_load_plugin8
  45596. cimg_load_plugin8(filename);
  45597. #endif
  45598. // Ascii formats
  45599. if (!cimg::strcasecmp(ext,"asc")) load_ascii(filename);
  45600. else if (!cimg::strcasecmp(ext,"dlm") ||
  45601. !cimg::strcasecmp(ext,"txt")) load_dlm(filename);
  45602. // 2d binary formats
  45603. else if (!cimg::strcasecmp(ext,"bmp")) load_bmp(filename);
  45604. else if (!cimg::strcasecmp(ext,"jpg") ||
  45605. !cimg::strcasecmp(ext,"jpeg") ||
  45606. !cimg::strcasecmp(ext,"jpe") ||
  45607. !cimg::strcasecmp(ext,"jfif") ||
  45608. !cimg::strcasecmp(ext,"jif")) load_jpeg(filename);
  45609. else if (!cimg::strcasecmp(ext,"png")) load_png(filename);
  45610. else if (!cimg::strcasecmp(ext,"ppm") ||
  45611. !cimg::strcasecmp(ext,"pgm") ||
  45612. !cimg::strcasecmp(ext,"pnm") ||
  45613. !cimg::strcasecmp(ext,"pbm") ||
  45614. !cimg::strcasecmp(ext,"pnk")) load_pnm(filename);
  45615. else if (!cimg::strcasecmp(ext,"pfm")) load_pfm(filename);
  45616. else if (!cimg::strcasecmp(ext,"tif") ||
  45617. !cimg::strcasecmp(ext,"tiff")) load_tiff(filename);
  45618. else if (!cimg::strcasecmp(ext,"exr")) load_exr(filename);
  45619. else if (!cimg::strcasecmp(ext,"cr2") ||
  45620. !cimg::strcasecmp(ext,"crw") ||
  45621. !cimg::strcasecmp(ext,"dcr") ||
  45622. !cimg::strcasecmp(ext,"mrw") ||
  45623. !cimg::strcasecmp(ext,"nef") ||
  45624. !cimg::strcasecmp(ext,"orf") ||
  45625. !cimg::strcasecmp(ext,"pix") ||
  45626. !cimg::strcasecmp(ext,"ptx") ||
  45627. !cimg::strcasecmp(ext,"raf") ||
  45628. !cimg::strcasecmp(ext,"srf")) load_dcraw_external(filename);
  45629. else if (!cimg::strcasecmp(ext,"gif")) load_gif_external(filename);
  45630. // 3d binary formats
  45631. else if (!cimg::strcasecmp(ext,"dcm") ||
  45632. !cimg::strcasecmp(ext,"dicom")) load_medcon_external(filename);
  45633. else if (!cimg::strcasecmp(ext,"hdr") ||
  45634. !cimg::strcasecmp(ext,"nii")) load_analyze(filename);
  45635. else if (!cimg::strcasecmp(ext,"par") ||
  45636. !cimg::strcasecmp(ext,"rec")) load_parrec(filename);
  45637. else if (!cimg::strcasecmp(ext,"mnc")) load_minc2(filename);
  45638. else if (!cimg::strcasecmp(ext,"inr")) load_inr(filename);
  45639. else if (!cimg::strcasecmp(ext,"pan")) load_pandore(filename);
  45640. else if (!cimg::strcasecmp(ext,"cimg") ||
  45641. !cimg::strcasecmp(ext,"cimgz") ||
  45642. !*ext) return load_cimg(filename);
  45643. // Archive files
  45644. else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename);
  45645. // Image sequences
  45646. else if (!cimg::strcasecmp(ext,"avi") ||
  45647. !cimg::strcasecmp(ext,"mov") ||
  45648. !cimg::strcasecmp(ext,"asf") ||
  45649. !cimg::strcasecmp(ext,"divx") ||
  45650. !cimg::strcasecmp(ext,"flv") ||
  45651. !cimg::strcasecmp(ext,"mpg") ||
  45652. !cimg::strcasecmp(ext,"m1v") ||
  45653. !cimg::strcasecmp(ext,"m2v") ||
  45654. !cimg::strcasecmp(ext,"m4v") ||
  45655. !cimg::strcasecmp(ext,"mjp") ||
  45656. !cimg::strcasecmp(ext,"mp4") ||
  45657. !cimg::strcasecmp(ext,"mkv") ||
  45658. !cimg::strcasecmp(ext,"mpe") ||
  45659. !cimg::strcasecmp(ext,"movie") ||
  45660. !cimg::strcasecmp(ext,"ogm") ||
  45661. !cimg::strcasecmp(ext,"ogg") ||
  45662. !cimg::strcasecmp(ext,"ogv") ||
  45663. !cimg::strcasecmp(ext,"qt") ||
  45664. !cimg::strcasecmp(ext,"rm") ||
  45665. !cimg::strcasecmp(ext,"vob") ||
  45666. !cimg::strcasecmp(ext,"wmv") ||
  45667. !cimg::strcasecmp(ext,"xvid") ||
  45668. !cimg::strcasecmp(ext,"mpeg")) load_video(filename);
  45669. else is_loaded = false;
  45670. } catch (CImgIOException&) { is_loaded = false; }
  45671. // If nothing loaded, try to guess file format from magic number in file.
  45672. if (!is_loaded) {
  45673. std::FILE *file = std_fopen(filename,"rb");
  45674. if (!file) {
  45675. cimg::exception_mode(omode);
  45676. throw CImgIOException(_cimg_instance
  45677. "load(): Failed to open file '%s'.",
  45678. cimg_instance,
  45679. filename);
  45680. }
  45681. const char *const f_type = cimg::ftype(file,filename);
  45682. std::fclose(file);
  45683. is_loaded = true;
  45684. try {
  45685. if (!cimg::strcasecmp(f_type,"pnm")) load_pnm(filename);
  45686. else if (!cimg::strcasecmp(f_type,"pfm")) load_pfm(filename);
  45687. else if (!cimg::strcasecmp(f_type,"bmp")) load_bmp(filename);
  45688. else if (!cimg::strcasecmp(f_type,"inr")) load_inr(filename);
  45689. else if (!cimg::strcasecmp(f_type,"jpg")) load_jpeg(filename);
  45690. else if (!cimg::strcasecmp(f_type,"pan")) load_pandore(filename);
  45691. else if (!cimg::strcasecmp(f_type,"png")) load_png(filename);
  45692. else if (!cimg::strcasecmp(f_type,"tif")) load_tiff(filename);
  45693. else if (!cimg::strcasecmp(f_type,"gif")) load_gif_external(filename);
  45694. else if (!cimg::strcasecmp(f_type,"dcm")) load_medcon_external(filename);
  45695. else is_loaded = false;
  45696. } catch (CImgIOException&) { is_loaded = false; }
  45697. }
  45698. // If nothing loaded, try to load file with other means.
  45699. if (!is_loaded) {
  45700. try {
  45701. load_other(filename);
  45702. } catch (CImgIOException&) {
  45703. cimg::exception_mode(omode);
  45704. throw CImgIOException(_cimg_instance
  45705. "load(): Failed to recognize format of file '%s'.",
  45706. cimg_instance,
  45707. filename);
  45708. }
  45709. }
  45710. cimg::exception_mode(omode);
  45711. return *this;
  45712. }
  45713. //! Load image from a file \newinstance.
  45714. static CImg<T> get_load(const char *const filename) {
  45715. return CImg<T>().load(filename);
  45716. }
  45717. //! Load image from an ascii file.
  45718. /**
  45719. \param filename Filename, as a C -string.
  45720. **/
  45721. CImg<T>& load_ascii(const char *const filename) {
  45722. return _load_ascii(0,filename);
  45723. }
  45724. //! Load image from an ascii file \inplace.
  45725. static CImg<T> get_load_ascii(const char *const filename) {
  45726. return CImg<T>().load_ascii(filename);
  45727. }
  45728. //! Load image from an ascii file \overloading.
  45729. CImg<T>& load_ascii(std::FILE *const file) {
  45730. return _load_ascii(file,0);
  45731. }
  45732. //! Loadimage from an ascii file \newinstance.
  45733. static CImg<T> get_load_ascii(std::FILE *const file) {
  45734. return CImg<T>().load_ascii(file);
  45735. }
  45736. CImg<T>& _load_ascii(std::FILE *const file, const char *const filename) {
  45737. if (!file && !filename)
  45738. throw CImgArgumentException(_cimg_instance
  45739. "load_ascii(): Specified filename is (null).",
  45740. cimg_instance);
  45741. std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
  45742. CImg<charT> line(256); *line = 0;
  45743. int err = std::fscanf(nfile,"%255[^\n]",line._data);
  45744. unsigned int dx = 0, dy = 1, dz = 1, dc = 1;
  45745. cimg_sscanf(line,"%u%*c%u%*c%u%*c%u",&dx,&dy,&dz,&dc);
  45746. err = std::fscanf(nfile,"%*[^0-9.eEinfa+-]");
  45747. if (!dx || !dy || !dz || !dc) {
  45748. if (!file) cimg::fclose(nfile);
  45749. throw CImgIOException(_cimg_instance
  45750. "load_ascii(): Invalid ascii header in file '%s', image dimensions are set "
  45751. "to (%u,%u,%u,%u).",
  45752. cimg_instance,
  45753. filename?filename:"(FILE*)",dx,dy,dz,dc);
  45754. }
  45755. assign(dx,dy,dz,dc);
  45756. const ulongT siz = size();
  45757. ulongT off = 0;
  45758. double val;
  45759. T *ptr = _data;
  45760. for (err = 1, off = 0; off<siz && err==1; ++off) {
  45761. err = std::fscanf(nfile,"%lf%*[^0-9.eEinfa+-]",&val);
  45762. *(ptr++) = (T)val;
  45763. }
  45764. if (err!=1)
  45765. cimg::warn(_cimg_instance
  45766. "load_ascii(): Only %lu/%lu values read from file '%s'.",
  45767. cimg_instance,
  45768. off - 1,siz,filename?filename:"(FILE*)");
  45769. if (!file) cimg::fclose(nfile);
  45770. return *this;
  45771. }
  45772. //! Load image from a DLM file.
  45773. /**
  45774. \param filename Filename, as a C-string.
  45775. **/
  45776. CImg<T>& load_dlm(const char *const filename) {
  45777. return _load_dlm(0,filename);
  45778. }
  45779. //! Load image from a DLM file \newinstance.
  45780. static CImg<T> get_load_dlm(const char *const filename) {
  45781. return CImg<T>().load_dlm(filename);
  45782. }
  45783. //! Load image from a DLM file \overloading.
  45784. CImg<T>& load_dlm(std::FILE *const file) {
  45785. return _load_dlm(file,0);
  45786. }
  45787. //! Load image from a DLM file \newinstance.
  45788. static CImg<T> get_load_dlm(std::FILE *const file) {
  45789. return CImg<T>().load_dlm(file);
  45790. }
  45791. CImg<T>& _load_dlm(std::FILE *const file, const char *const filename) {
  45792. if (!file && !filename)
  45793. throw CImgArgumentException(_cimg_instance
  45794. "load_dlm(): Specified filename is (null).",
  45795. cimg_instance);
  45796. std::FILE *const nfile = file?file:cimg::fopen(filename,"r");
  45797. CImg<charT> delimiter(256), tmp(256); *delimiter = *tmp = 0;
  45798. unsigned int cdx = 0, dx = 0, dy = 0;
  45799. int err = 0;
  45800. double val;
  45801. assign(256,256,1,1,(T)0);
  45802. while ((err = std::fscanf(nfile,"%lf%255[^0-9eEinfa.+-]",&val,delimiter._data))>0) {
  45803. if (err>0) (*this)(cdx++,dy) = (T)val;
  45804. if (cdx>=_width) resize(3*_width/2,_height,1,1,0);
  45805. char c = 0;
  45806. if (!cimg_sscanf(delimiter,"%255[^\n]%c",tmp._data,&c) || c=='\n') {
  45807. dx = std::max(cdx,dx);
  45808. if (++dy>=_height) resize(_width,3*_height/2,1,1,0);
  45809. cdx = 0;
  45810. }
  45811. }
  45812. if (cdx && err==1) { dx = cdx; ++dy; }
  45813. if (!dx || !dy) {
  45814. if (!file) cimg::fclose(nfile);
  45815. throw CImgIOException(_cimg_instance
  45816. "load_dlm(): Invalid DLM file '%s'.",
  45817. cimg_instance,
  45818. filename?filename:"(FILE*)");
  45819. }
  45820. resize(dx,dy,1,1,0);
  45821. if (!file) cimg::fclose(nfile);
  45822. return *this;
  45823. }
  45824. //! Load image from a BMP file.
  45825. /**
  45826. \param filename Filename, as a C-string.
  45827. **/
  45828. CImg<T>& load_bmp(const char *const filename) {
  45829. return _load_bmp(0,filename);
  45830. }
  45831. //! Load image from a BMP file \newinstance.
  45832. static CImg<T> get_load_bmp(const char *const filename) {
  45833. return CImg<T>().load_bmp(filename);
  45834. }
  45835. //! Load image from a BMP file \overloading.
  45836. CImg<T>& load_bmp(std::FILE *const file) {
  45837. return _load_bmp(file,0);
  45838. }
  45839. //! Load image from a BMP file \newinstance.
  45840. static CImg<T> get_load_bmp(std::FILE *const file) {
  45841. return CImg<T>().load_bmp(file);
  45842. }
  45843. CImg<T>& _load_bmp(std::FILE *const file, const char *const filename) {
  45844. if (!file && !filename)
  45845. throw CImgArgumentException(_cimg_instance
  45846. "load_bmp(): Specified filename is (null).",
  45847. cimg_instance);
  45848. std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
  45849. CImg<ucharT> header(54);
  45850. cimg::fread(header._data,54,nfile);
  45851. if (*header!='B' || header[1]!='M') {
  45852. if (!file) cimg::fclose(nfile);
  45853. throw CImgIOException(_cimg_instance
  45854. "load_bmp(): Invalid BMP file '%s'.",
  45855. cimg_instance,
  45856. filename?filename:"(FILE*)");
  45857. }
  45858. // Read header and pixel buffer
  45859. int
  45860. file_size = header[0x02] + (header[0x03]<<8) + (header[0x04]<<16) + (header[0x05]<<24),
  45861. offset = header[0x0A] + (header[0x0B]<<8) + (header[0x0C]<<16) + (header[0x0D]<<24),
  45862. header_size = header[0x0E] + (header[0x0F]<<8) + (header[0x10]<<16) + (header[0x11]<<24),
  45863. dx = header[0x12] + (header[0x13]<<8) + (header[0x14]<<16) + (header[0x15]<<24),
  45864. dy = header[0x16] + (header[0x17]<<8) + (header[0x18]<<16) + (header[0x19]<<24),
  45865. compression = header[0x1E] + (header[0x1F]<<8) + (header[0x20]<<16) + (header[0x21]<<24),
  45866. nb_colors = header[0x2E] + (header[0x2F]<<8) + (header[0x30]<<16) + (header[0x31]<<24),
  45867. bpp = header[0x1C] + (header[0x1D]<<8);
  45868. if (!file_size || file_size==offset) {
  45869. cimg::fseek(nfile,0,SEEK_END);
  45870. file_size = (int)cimg::ftell(nfile);
  45871. cimg::fseek(nfile,54,SEEK_SET);
  45872. }
  45873. if (header_size>40) cimg::fseek(nfile,header_size - 40,SEEK_CUR);
  45874. const int
  45875. dx_bytes = (bpp==1)?(dx/8 + (dx%8?1:0)):((bpp==4)?(dx/2 + (dx%2)):(int)((longT)dx*bpp/8)),
  45876. align_bytes = (4 - dx_bytes%4)%4;
  45877. const ulongT
  45878. cimg_iobuffer = (ulongT)24*1024*1024,
  45879. buf_size = std::min((ulongT)cimg::abs(dy)*(dx_bytes + align_bytes),(ulongT)file_size - offset);
  45880. CImg<intT> colormap;
  45881. if (bpp<16) { if (!nb_colors) nb_colors = 1<<bpp; } else nb_colors = 0;
  45882. if (nb_colors) { colormap.assign(nb_colors); cimg::fread(colormap._data,nb_colors,nfile); }
  45883. const int xoffset = offset - 14 - header_size - 4*nb_colors;
  45884. if (xoffset>0) cimg::fseek(nfile,xoffset,SEEK_CUR);
  45885. CImg<ucharT> buffer;
  45886. if (buf_size<cimg_iobuffer) {
  45887. buffer.assign(cimg::abs(dy)*(dx_bytes + align_bytes),1,1,1,0);
  45888. cimg::fread(buffer._data,buf_size,nfile);
  45889. } else buffer.assign(dx_bytes + align_bytes);
  45890. unsigned char *ptrs = buffer;
  45891. // Decompress buffer (if necessary)
  45892. if (compression) {
  45893. if (file)
  45894. throw CImgIOException(_cimg_instance
  45895. "load_bmp(): Unable to load compressed data from '(*FILE)' inputs.",
  45896. cimg_instance);
  45897. else {
  45898. if (!file) cimg::fclose(nfile);
  45899. return load_other(filename);
  45900. }
  45901. }
  45902. // Read pixel data
  45903. assign(dx,cimg::abs(dy),1,3,0);
  45904. switch (bpp) {
  45905. case 1 : { // Monochrome
  45906. if (colormap._width>=2) for (int y = height() - 1; y>=0; --y) {
  45907. if (buf_size>=cimg_iobuffer) {
  45908. if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
  45909. cimg::fseek(nfile,align_bytes,SEEK_CUR);
  45910. }
  45911. unsigned char mask = 0x80, val = 0;
  45912. cimg_forX(*this,x) {
  45913. if (mask==0x80) val = *(ptrs++);
  45914. const unsigned char *col = (unsigned char*)(colormap._data + (val&mask?1:0));
  45915. (*this)(x,y,2) = (T)*(col++);
  45916. (*this)(x,y,1) = (T)*(col++);
  45917. (*this)(x,y,0) = (T)*(col++);
  45918. mask = cimg::ror(mask);
  45919. }
  45920. ptrs+=align_bytes;
  45921. }
  45922. } break;
  45923. case 4 : { // 16 colors
  45924. if (colormap._width>=16) for (int y = height() - 1; y>=0; --y) {
  45925. if (buf_size>=cimg_iobuffer) {
  45926. if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
  45927. cimg::fseek(nfile,align_bytes,SEEK_CUR);
  45928. }
  45929. unsigned char mask = 0xF0, val = 0;
  45930. cimg_forX(*this,x) {
  45931. if (mask==0xF0) val = *(ptrs++);
  45932. const unsigned char color = (unsigned char)((mask<16)?(val&mask):((val&mask)>>4));
  45933. const unsigned char *col = (unsigned char*)(colormap._data + color);
  45934. (*this)(x,y,2) = (T)*(col++);
  45935. (*this)(x,y,1) = (T)*(col++);
  45936. (*this)(x,y,0) = (T)*(col++);
  45937. mask = cimg::ror(mask,4);
  45938. }
  45939. ptrs+=align_bytes;
  45940. }
  45941. } break;
  45942. case 8 : { // 256 colors
  45943. if (colormap._width>=256) for (int y = height() - 1; y>=0; --y) {
  45944. if (buf_size>=cimg_iobuffer) {
  45945. if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
  45946. cimg::fseek(nfile,align_bytes,SEEK_CUR);
  45947. }
  45948. cimg_forX(*this,x) {
  45949. const unsigned char *col = (unsigned char*)(colormap._data + *(ptrs++));
  45950. (*this)(x,y,2) = (T)*(col++);
  45951. (*this)(x,y,1) = (T)*(col++);
  45952. (*this)(x,y,0) = (T)*(col++);
  45953. }
  45954. ptrs+=align_bytes;
  45955. }
  45956. } break;
  45957. case 16 : { // 16 bits colors
  45958. for (int y = height() - 1; y>=0; --y) {
  45959. if (buf_size>=cimg_iobuffer) {
  45960. if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
  45961. cimg::fseek(nfile,align_bytes,SEEK_CUR);
  45962. }
  45963. cimg_forX(*this,x) {
  45964. const unsigned char c1 = *(ptrs++), c2 = *(ptrs++);
  45965. const unsigned short col = (unsigned short)(c1|(c2<<8));
  45966. (*this)(x,y,2) = (T)(col&0x1F);
  45967. (*this)(x,y,1) = (T)((col>>5)&0x1F);
  45968. (*this)(x,y,0) = (T)((col>>10)&0x1F);
  45969. }
  45970. ptrs+=align_bytes;
  45971. }
  45972. } break;
  45973. case 24 : { // 24 bits colors
  45974. for (int y = height() - 1; y>=0; --y) {
  45975. if (buf_size>=cimg_iobuffer) {
  45976. if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
  45977. cimg::fseek(nfile,align_bytes,SEEK_CUR);
  45978. }
  45979. cimg_forX(*this,x) {
  45980. (*this)(x,y,2) = (T)*(ptrs++);
  45981. (*this)(x,y,1) = (T)*(ptrs++);
  45982. (*this)(x,y,0) = (T)*(ptrs++);
  45983. }
  45984. ptrs+=align_bytes;
  45985. }
  45986. } break;
  45987. case 32 : { // 32 bits colors
  45988. for (int y = height() - 1; y>=0; --y) {
  45989. if (buf_size>=cimg_iobuffer) {
  45990. if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
  45991. cimg::fseek(nfile,align_bytes,SEEK_CUR);
  45992. }
  45993. cimg_forX(*this,x) {
  45994. (*this)(x,y,2) = (T)*(ptrs++);
  45995. (*this)(x,y,1) = (T)*(ptrs++);
  45996. (*this)(x,y,0) = (T)*(ptrs++);
  45997. ++ptrs;
  45998. }
  45999. ptrs+=align_bytes;
  46000. }
  46001. } break;
  46002. }
  46003. if (dy<0) mirror('y');
  46004. if (!file) cimg::fclose(nfile);
  46005. return *this;
  46006. }
  46007. //! Load image from a JPEG file.
  46008. /**
  46009. \param filename Filename, as a C-string.
  46010. **/
  46011. CImg<T>& load_jpeg(const char *const filename) {
  46012. return _load_jpeg(0,filename);
  46013. }
  46014. //! Load image from a JPEG file \newinstance.
  46015. static CImg<T> get_load_jpeg(const char *const filename) {
  46016. return CImg<T>().load_jpeg(filename);
  46017. }
  46018. //! Load image from a JPEG file \overloading.
  46019. CImg<T>& load_jpeg(std::FILE *const file) {
  46020. return _load_jpeg(file,0);
  46021. }
  46022. //! Load image from a JPEG file \newinstance.
  46023. static CImg<T> get_load_jpeg(std::FILE *const file) {
  46024. return CImg<T>().load_jpeg(file);
  46025. }
  46026. // Custom error handler for libjpeg.
  46027. #ifdef cimg_use_jpeg
  46028. struct _cimg_error_mgr {
  46029. struct jpeg_error_mgr original;
  46030. jmp_buf setjmp_buffer;
  46031. char message[JMSG_LENGTH_MAX];
  46032. };
  46033. typedef struct _cimg_error_mgr *_cimg_error_ptr;
  46034. METHODDEF(void) _cimg_jpeg_error_exit(j_common_ptr cinfo) {
  46035. _cimg_error_ptr c_err = (_cimg_error_ptr) cinfo->err; // Return control to the setjmp point
  46036. (*cinfo->err->format_message)(cinfo,c_err->message);
  46037. jpeg_destroy(cinfo); // Clean memory and temp files.
  46038. longjmp(c_err->setjmp_buffer,1);
  46039. }
  46040. #endif
  46041. CImg<T>& _load_jpeg(std::FILE *const file, const char *const filename) {
  46042. if (!file && !filename)
  46043. throw CImgArgumentException(_cimg_instance
  46044. "load_jpeg(): Specified filename is (null).",
  46045. cimg_instance);
  46046. #ifndef cimg_use_jpeg
  46047. if (file)
  46048. throw CImgIOException(_cimg_instance
  46049. "load_jpeg(): Unable to load data from '(FILE*)' unless libjpeg is enabled.",
  46050. cimg_instance);
  46051. else return load_other(filename);
  46052. #else
  46053. std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
  46054. struct jpeg_decompress_struct cinfo;
  46055. struct _cimg_error_mgr jerr;
  46056. cinfo.err = jpeg_std_error(&jerr.original);
  46057. jerr.original.error_exit = _cimg_jpeg_error_exit;
  46058. if (setjmp(jerr.setjmp_buffer)) { // JPEG error
  46059. if (!file) cimg::fclose(nfile);
  46060. throw CImgIOException(_cimg_instance
  46061. "load_jpeg(): Error message returned by libjpeg: %s.",
  46062. cimg_instance,jerr.message);
  46063. }
  46064. jpeg_create_decompress(&cinfo);
  46065. jpeg_stdio_src(&cinfo,nfile);
  46066. jpeg_read_header(&cinfo,TRUE);
  46067. jpeg_start_decompress(&cinfo);
  46068. if (cinfo.output_components!=1 && cinfo.output_components!=3 && cinfo.output_components!=4) {
  46069. if (!file) {
  46070. cimg::fclose(nfile);
  46071. return load_other(filename);
  46072. } else
  46073. throw CImgIOException(_cimg_instance
  46074. "load_jpeg(): Failed to load JPEG data from file '%s'.",
  46075. cimg_instance,filename?filename:"(FILE*)");
  46076. }
  46077. CImg<ucharT> buffer(cinfo.output_width*cinfo.output_components);
  46078. JSAMPROW row_pointer[1];
  46079. try { assign(cinfo.output_width,cinfo.output_height,1,cinfo.output_components); }
  46080. catch (...) { if (!file) cimg::fclose(nfile); throw; }
  46081. T *ptr_r = _data, *ptr_g = _data + 1UL*_width*_height, *ptr_b = _data + 2UL*_width*_height,
  46082. *ptr_a = _data + 3UL*_width*_height;
  46083. while (cinfo.output_scanline<cinfo.output_height) {
  46084. *row_pointer = buffer._data;
  46085. if (jpeg_read_scanlines(&cinfo,row_pointer,1)!=1) {
  46086. cimg::warn(_cimg_instance
  46087. "load_jpeg(): Incomplete data in file '%s'.",
  46088. cimg_instance,filename?filename:"(FILE*)");
  46089. break;
  46090. }
  46091. const unsigned char *ptrs = buffer._data;
  46092. switch (_spectrum) {
  46093. case 1 : {
  46094. cimg_forX(*this,x) *(ptr_r++) = (T)*(ptrs++);
  46095. } break;
  46096. case 3 : {
  46097. cimg_forX(*this,x) {
  46098. *(ptr_r++) = (T)*(ptrs++);
  46099. *(ptr_g++) = (T)*(ptrs++);
  46100. *(ptr_b++) = (T)*(ptrs++);
  46101. }
  46102. } break;
  46103. case 4 : {
  46104. cimg_forX(*this,x) {
  46105. *(ptr_r++) = (T)*(ptrs++);
  46106. *(ptr_g++) = (T)*(ptrs++);
  46107. *(ptr_b++) = (T)*(ptrs++);
  46108. *(ptr_a++) = (T)*(ptrs++);
  46109. }
  46110. } break;
  46111. }
  46112. }
  46113. jpeg_finish_decompress(&cinfo);
  46114. jpeg_destroy_decompress(&cinfo);
  46115. if (!file) cimg::fclose(nfile);
  46116. return *this;
  46117. #endif
  46118. }
  46119. //! Load image from a file, using Magick++ library.
  46120. /**
  46121. \param filename Filename, as a C-string.
  46122. **/
  46123. // Added April/may 2006 by Christoph Hormann <chris_hormann@gmx.de>
  46124. // This is experimental code, not much tested, use with care.
  46125. CImg<T>& load_magick(const char *const filename) {
  46126. if (!filename)
  46127. throw CImgArgumentException(_cimg_instance
  46128. "load_magick(): Specified filename is (null).",
  46129. cimg_instance);
  46130. #ifdef cimg_use_magick
  46131. Magick::Image image(filename);
  46132. const unsigned int W = image.size().width(), H = image.size().height();
  46133. switch (image.type()) {
  46134. case Magick::PaletteMatteType :
  46135. case Magick::TrueColorMatteType :
  46136. case Magick::ColorSeparationType : {
  46137. assign(W,H,1,4);
  46138. 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);
  46139. Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
  46140. for (ulongT off = (ulongT)W*H; off; --off) {
  46141. *(ptr_r++) = (T)(pixels->red);
  46142. *(ptr_g++) = (T)(pixels->green);
  46143. *(ptr_b++) = (T)(pixels->blue);
  46144. *(ptr_a++) = (T)(pixels->opacity);
  46145. ++pixels;
  46146. }
  46147. } break;
  46148. case Magick::PaletteType :
  46149. case Magick::TrueColorType : {
  46150. assign(W,H,1,3);
  46151. T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
  46152. Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
  46153. for (ulongT off = (ulongT)W*H; off; --off) {
  46154. *(ptr_r++) = (T)(pixels->red);
  46155. *(ptr_g++) = (T)(pixels->green);
  46156. *(ptr_b++) = (T)(pixels->blue);
  46157. ++pixels;
  46158. }
  46159. } break;
  46160. case Magick::GrayscaleMatteType : {
  46161. assign(W,H,1,2);
  46162. T *ptr_r = data(0,0,0,0), *ptr_a = data(0,0,0,1);
  46163. Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
  46164. for (ulongT off = (ulongT)W*H; off; --off) {
  46165. *(ptr_r++) = (T)(pixels->red);
  46166. *(ptr_a++) = (T)(pixels->opacity);
  46167. ++pixels;
  46168. }
  46169. } break;
  46170. default : {
  46171. assign(W,H,1,1);
  46172. T *ptr_r = data(0,0,0,0);
  46173. Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
  46174. for (ulongT off = (ulongT)W*H; off; --off) {
  46175. *(ptr_r++) = (T)(pixels->red);
  46176. ++pixels;
  46177. }
  46178. }
  46179. }
  46180. return *this;
  46181. #else
  46182. throw CImgIOException(_cimg_instance
  46183. "load_magick(): Unable to load file '%s' unless libMagick++ is enabled.",
  46184. cimg_instance,
  46185. filename);
  46186. #endif
  46187. }
  46188. //! Load image from a file, using Magick++ library \newinstance.
  46189. static CImg<T> get_load_magick(const char *const filename) {
  46190. return CImg<T>().load_magick(filename);
  46191. }
  46192. //! Load image from a PNG file.
  46193. /**
  46194. \param filename Filename, as a C-string.
  46195. \param[out] bits_per_pixel Number of bits per pixels used to store pixel values in the image file.
  46196. **/
  46197. CImg<T>& load_png(const char *const filename, unsigned int *const bits_per_pixel=0) {
  46198. return _load_png(0,filename,bits_per_pixel);
  46199. }
  46200. //! Load image from a PNG file \newinstance.
  46201. static CImg<T> get_load_png(const char *const filename, unsigned int *const bits_per_pixel=0) {
  46202. return CImg<T>().load_png(filename,bits_per_pixel);
  46203. }
  46204. //! Load image from a PNG file \overloading.
  46205. CImg<T>& load_png(std::FILE *const file, unsigned int *const bits_per_pixel=0) {
  46206. return _load_png(file,0,bits_per_pixel);
  46207. }
  46208. //! Load image from a PNG file \newinstance.
  46209. static CImg<T> get_load_png(std::FILE *const file, unsigned int *const bits_per_pixel=0) {
  46210. return CImg<T>().load_png(file,bits_per_pixel);
  46211. }
  46212. // (Note: Most of this function has been written by Eric Fausett)
  46213. CImg<T>& _load_png(std::FILE *const file, const char *const filename, unsigned int *const bits_per_pixel) {
  46214. if (!file && !filename)
  46215. throw CImgArgumentException(_cimg_instance
  46216. "load_png(): Specified filename is (null).",
  46217. cimg_instance);
  46218. #ifndef cimg_use_png
  46219. cimg::unused(bits_per_pixel);
  46220. if (file)
  46221. throw CImgIOException(_cimg_instance
  46222. "load_png(): Unable to load data from '(FILE*)' unless libpng is enabled.",
  46223. cimg_instance);
  46224. else return load_other(filename);
  46225. #else
  46226. // Open file and check for PNG validity
  46227. #if defined __GNUC__
  46228. const char *volatile nfilename = filename; // Use 'volatile' to avoid (wrong) g++ warning.
  46229. std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"rb");
  46230. #else
  46231. const char *nfilename = filename;
  46232. std::FILE *nfile = file?file:cimg::fopen(nfilename,"rb");
  46233. #endif
  46234. unsigned char pngCheck[8] = { 0 };
  46235. cimg::fread(pngCheck,8,(std::FILE*)nfile);
  46236. if (png_sig_cmp(pngCheck,0,8)) {
  46237. if (!file) cimg::fclose(nfile);
  46238. throw CImgIOException(_cimg_instance
  46239. "load_png(): Invalid PNG file '%s'.",
  46240. cimg_instance,
  46241. nfilename?nfilename:"(FILE*)");
  46242. }
  46243. // Setup PNG structures for read
  46244. png_voidp user_error_ptr = 0;
  46245. png_error_ptr user_error_fn = 0, user_warning_fn = 0;
  46246. png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,user_error_ptr,user_error_fn,user_warning_fn);
  46247. if (!png_ptr) {
  46248. if (!file) cimg::fclose(nfile);
  46249. throw CImgIOException(_cimg_instance
  46250. "load_png(): Failed to initialize 'png_ptr' structure for file '%s'.",
  46251. cimg_instance,
  46252. nfilename?nfilename:"(FILE*)");
  46253. }
  46254. png_infop info_ptr = png_create_info_struct(png_ptr);
  46255. if (!info_ptr) {
  46256. if (!file) cimg::fclose(nfile);
  46257. png_destroy_read_struct(&png_ptr,(png_infopp)0,(png_infopp)0);
  46258. throw CImgIOException(_cimg_instance
  46259. "load_png(): Failed to initialize 'info_ptr' structure for file '%s'.",
  46260. cimg_instance,
  46261. nfilename?nfilename:"(FILE*)");
  46262. }
  46263. png_infop end_info = png_create_info_struct(png_ptr);
  46264. if (!end_info) {
  46265. if (!file) cimg::fclose(nfile);
  46266. png_destroy_read_struct(&png_ptr,&info_ptr,(png_infopp)0);
  46267. throw CImgIOException(_cimg_instance
  46268. "load_png(): Failed to initialize 'end_info' structure for file '%s'.",
  46269. cimg_instance,
  46270. nfilename?nfilename:"(FILE*)");
  46271. }
  46272. // Error handling callback for png file reading
  46273. if (setjmp(png_jmpbuf(png_ptr))) {
  46274. if (!file) cimg::fclose((std::FILE*)nfile);
  46275. png_destroy_read_struct(&png_ptr, &end_info, (png_infopp)0);
  46276. throw CImgIOException(_cimg_instance
  46277. "load_png(): Encountered unknown fatal error in libpng for file '%s'.",
  46278. cimg_instance,
  46279. nfilename?nfilename:"(FILE*)");
  46280. }
  46281. png_init_io(png_ptr, nfile);
  46282. png_set_sig_bytes(png_ptr, 8);
  46283. // Get PNG Header Info up to data block
  46284. png_read_info(png_ptr,info_ptr);
  46285. png_uint_32 W, H;
  46286. int bit_depth, color_type, interlace_type;
  46287. bool is_gray = false;
  46288. png_get_IHDR(png_ptr,info_ptr,&W,&H,&bit_depth,&color_type,&interlace_type,(int*)0,(int*)0);
  46289. if (bits_per_pixel) *bits_per_pixel = (unsigned int)bit_depth;
  46290. // Transforms to unify image data
  46291. if (color_type==PNG_COLOR_TYPE_PALETTE) {
  46292. png_set_palette_to_rgb(png_ptr);
  46293. color_type = PNG_COLOR_TYPE_RGB;
  46294. bit_depth = 8;
  46295. }
  46296. if (color_type==PNG_COLOR_TYPE_GRAY && bit_depth<8) {
  46297. png_set_expand_gray_1_2_4_to_8(png_ptr);
  46298. is_gray = true;
  46299. bit_depth = 8;
  46300. }
  46301. if (png_get_valid(png_ptr,info_ptr,PNG_INFO_tRNS)) {
  46302. png_set_tRNS_to_alpha(png_ptr);
  46303. color_type |= PNG_COLOR_MASK_ALPHA;
  46304. }
  46305. if (color_type==PNG_COLOR_TYPE_GRAY || color_type==PNG_COLOR_TYPE_GRAY_ALPHA) {
  46306. png_set_gray_to_rgb(png_ptr);
  46307. color_type |= PNG_COLOR_MASK_COLOR;
  46308. is_gray = true;
  46309. }
  46310. if (color_type==PNG_COLOR_TYPE_RGB)
  46311. png_set_filler(png_ptr,0xffffU,PNG_FILLER_AFTER);
  46312. png_read_update_info(png_ptr,info_ptr);
  46313. if (bit_depth!=8 && bit_depth!=16) {
  46314. if (!file) cimg::fclose(nfile);
  46315. png_destroy_read_struct(&png_ptr,&end_info,(png_infopp)0);
  46316. throw CImgIOException(_cimg_instance
  46317. "load_png(): Invalid bit depth %u in file '%s'.",
  46318. cimg_instance,
  46319. bit_depth,nfilename?nfilename:"(FILE*)");
  46320. }
  46321. const int byte_depth = bit_depth>>3;
  46322. // Allocate Memory for Image Read
  46323. png_bytep *const imgData = new png_bytep[H];
  46324. for (unsigned int row = 0; row<H; ++row) imgData[row] = new png_byte[(size_t)byte_depth*4*W];
  46325. png_read_image(png_ptr,imgData);
  46326. png_read_end(png_ptr,end_info);
  46327. // Read pixel data
  46328. if (color_type!=PNG_COLOR_TYPE_RGB && color_type!=PNG_COLOR_TYPE_RGB_ALPHA) {
  46329. if (!file) cimg::fclose(nfile);
  46330. png_destroy_read_struct(&png_ptr,&end_info,(png_infopp)0);
  46331. throw CImgIOException(_cimg_instance
  46332. "load_png(): Invalid color coding type %u in file '%s'.",
  46333. cimg_instance,
  46334. color_type,nfilename?nfilename:"(FILE*)");
  46335. }
  46336. const bool is_alpha = (color_type==PNG_COLOR_TYPE_RGBA);
  46337. try { assign(W,H,1,(is_gray?1:3) + (is_alpha?1:0)); }
  46338. catch (...) { if (!file) cimg::fclose(nfile); throw; }
  46339. T
  46340. *ptr_r = data(0,0,0,0),
  46341. *ptr_g = is_gray?0:data(0,0,0,1),
  46342. *ptr_b = is_gray?0:data(0,0,0,2),
  46343. *ptr_a = !is_alpha?0:data(0,0,0,is_gray?1:3);
  46344. switch (bit_depth) {
  46345. case 8 : {
  46346. cimg_forY(*this,y) {
  46347. const unsigned char *ptrs = (unsigned char*)imgData[y];
  46348. cimg_forX(*this,x) {
  46349. *(ptr_r++) = (T)*(ptrs++);
  46350. if (ptr_g) *(ptr_g++) = (T)*(ptrs++); else ++ptrs;
  46351. if (ptr_b) *(ptr_b++) = (T)*(ptrs++); else ++ptrs;
  46352. if (ptr_a) *(ptr_a++) = (T)*(ptrs++); else ++ptrs;
  46353. }
  46354. }
  46355. } break;
  46356. case 16 : {
  46357. cimg_forY(*this,y) {
  46358. const unsigned short *ptrs = (unsigned short*)(imgData[y]);
  46359. if (!cimg::endianness()) cimg::invert_endianness(ptrs,4*_width);
  46360. cimg_forX(*this,x) {
  46361. *(ptr_r++) = (T)*(ptrs++);
  46362. if (ptr_g) *(ptr_g++) = (T)*(ptrs++); else ++ptrs;
  46363. if (ptr_b) *(ptr_b++) = (T)*(ptrs++); else ++ptrs;
  46364. if (ptr_a) *(ptr_a++) = (T)*(ptrs++); else ++ptrs;
  46365. }
  46366. }
  46367. } break;
  46368. }
  46369. png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
  46370. // Deallocate Image Read Memory
  46371. cimg_forY(*this,n) delete[] imgData[n];
  46372. delete[] imgData;
  46373. if (!file) cimg::fclose(nfile);
  46374. return *this;
  46375. #endif
  46376. }
  46377. //! Load image from a PNM file.
  46378. /**
  46379. \param filename Filename, as a C-string.
  46380. **/
  46381. CImg<T>& load_pnm(const char *const filename) {
  46382. return _load_pnm(0,filename);
  46383. }
  46384. //! Load image from a PNM file \newinstance.
  46385. static CImg<T> get_load_pnm(const char *const filename) {
  46386. return CImg<T>().load_pnm(filename);
  46387. }
  46388. //! Load image from a PNM file \overloading.
  46389. CImg<T>& load_pnm(std::FILE *const file) {
  46390. return _load_pnm(file,0);
  46391. }
  46392. //! Load image from a PNM file \newinstance.
  46393. static CImg<T> get_load_pnm(std::FILE *const file) {
  46394. return CImg<T>().load_pnm(file);
  46395. }
  46396. CImg<T>& _load_pnm(std::FILE *const file, const char *const filename) {
  46397. if (!file && !filename)
  46398. throw CImgArgumentException(_cimg_instance
  46399. "load_pnm(): Specified filename is (null).",
  46400. cimg_instance);
  46401. std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
  46402. unsigned int ppm_type, W, H, D = 1, colormax = 255;
  46403. CImg<charT> item(16384,1,1,1,0);
  46404. int err, rval, gval, bval;
  46405. const longT cimg_iobuffer = (longT)24*1024*1024;
  46406. while ((err=std::fscanf(nfile,"%16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
  46407. if (cimg_sscanf(item," P%u",&ppm_type)!=1) {
  46408. if (!file) cimg::fclose(nfile);
  46409. throw CImgIOException(_cimg_instance
  46410. "load_pnm(): PNM header not found in file '%s'.",
  46411. cimg_instance,
  46412. filename?filename:"(FILE*)");
  46413. }
  46414. while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
  46415. if ((err=cimg_sscanf(item," %u %u %u %u",&W,&H,&D,&colormax))<2) {
  46416. if (!file) cimg::fclose(nfile);
  46417. throw CImgIOException(_cimg_instance
  46418. "load_pnm(): WIDTH and HEIGHT fields undefined in file '%s'.",
  46419. cimg_instance,
  46420. filename?filename:"(FILE*)");
  46421. }
  46422. if (ppm_type!=1 && ppm_type!=4) {
  46423. if (err==2 || (err==3 && (ppm_type==5 || ppm_type==7 || ppm_type==8 || ppm_type==9))) {
  46424. while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
  46425. if (cimg_sscanf(item,"%u",&colormax)!=1)
  46426. cimg::warn(_cimg_instance
  46427. "load_pnm(): COLORMAX field is undefined in file '%s'.",
  46428. cimg_instance,
  46429. filename?filename:"(FILE*)");
  46430. } else { colormax = D; D = 1; }
  46431. }
  46432. std::fgetc(nfile);
  46433. switch (ppm_type) {
  46434. case 1 : { // 2d b&w ascii.
  46435. assign(W,H,1,1);
  46436. T* ptrd = _data;
  46437. cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptrd++) = (T)(rval?0:255); else break; }
  46438. } break;
  46439. case 2 : { // 2d grey ascii.
  46440. assign(W,H,1,1);
  46441. T* ptrd = _data;
  46442. cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptrd++) = (T)rval; else break; }
  46443. } break;
  46444. case 3 : { // 2d color ascii.
  46445. assign(W,H,1,3);
  46446. T *ptrd = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
  46447. cimg_forXY(*this,x,y) {
  46448. if (std::fscanf(nfile,"%d %d %d",&rval,&gval,&bval)==3) {
  46449. *(ptrd++) = (T)rval; *(ptr_g++) = (T)gval; *(ptr_b++) = (T)bval;
  46450. } else break;
  46451. }
  46452. } break;
  46453. case 4 : { // 2d b&w binary (support 3D PINK extension).
  46454. CImg<ucharT> raw;
  46455. assign(W,H,D,1);
  46456. T *ptrd = data(0,0,0,0);
  46457. unsigned int w = 0, h = 0, d = 0;
  46458. for (longT to_read = (longT)((W/8 + (W%8?1:0))*H*D); to_read>0; ) {
  46459. raw.assign(std::min(to_read,cimg_iobuffer));
  46460. cimg::fread(raw._data,raw._width,nfile);
  46461. to_read-=raw._width;
  46462. const unsigned char *ptrs = raw._data;
  46463. unsigned char mask = 0, val = 0;
  46464. for (ulongT off = (ulongT)raw._width; off || mask; mask>>=1) {
  46465. if (!mask) { if (off--) val = *(ptrs++); mask = 128; }
  46466. *(ptrd++) = (T)((val&mask)?0:255);
  46467. if (++w==W) { w = 0; mask = 0; if (++h==H) { h = 0; if (++d==D) break; }}
  46468. }
  46469. }
  46470. } break;
  46471. case 5 : case 7 : { // 2d/3d grey binary (support 3D PINK extension).
  46472. if (colormax<256) { // 8 bits.
  46473. CImg<ucharT> raw;
  46474. assign(W,H,D,1);
  46475. T *ptrd = data(0,0,0,0);
  46476. for (longT to_read = (longT)size(); to_read>0; ) {
  46477. raw.assign(std::min(to_read,cimg_iobuffer));
  46478. cimg::fread(raw._data,raw._width,nfile);
  46479. to_read-=raw._width;
  46480. const unsigned char *ptrs = raw._data;
  46481. for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
  46482. }
  46483. } else { // 16 bits.
  46484. CImg<ushortT> raw;
  46485. assign(W,H,D,1);
  46486. T *ptrd = data(0,0,0,0);
  46487. for (longT to_read = (longT)size(); to_read>0; ) {
  46488. raw.assign(std::min(to_read,cimg_iobuffer/2));
  46489. cimg::fread(raw._data,raw._width,nfile);
  46490. if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width);
  46491. to_read-=raw._width;
  46492. const unsigned short *ptrs = raw._data;
  46493. for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
  46494. }
  46495. }
  46496. } break;
  46497. case 6 : { // 2d color binary.
  46498. if (colormax<256) { // 8 bits.
  46499. CImg<ucharT> raw;
  46500. assign(W,H,1,3);
  46501. T
  46502. *ptr_r = data(0,0,0,0),
  46503. *ptr_g = data(0,0,0,1),
  46504. *ptr_b = data(0,0,0,2);
  46505. for (longT to_read = (longT)size(); to_read>0; ) {
  46506. raw.assign(std::min(to_read,cimg_iobuffer));
  46507. cimg::fread(raw._data,raw._width,nfile);
  46508. to_read-=raw._width;
  46509. const unsigned char *ptrs = raw._data;
  46510. for (ulongT off = (ulongT)raw._width/3; off; --off) {
  46511. *(ptr_r++) = (T)*(ptrs++);
  46512. *(ptr_g++) = (T)*(ptrs++);
  46513. *(ptr_b++) = (T)*(ptrs++);
  46514. }
  46515. }
  46516. } else { // 16 bits.
  46517. CImg<ushortT> raw;
  46518. assign(W,H,1,3);
  46519. T
  46520. *ptr_r = data(0,0,0,0),
  46521. *ptr_g = data(0,0,0,1),
  46522. *ptr_b = data(0,0,0,2);
  46523. for (longT to_read = (longT)size(); to_read>0; ) {
  46524. raw.assign(std::min(to_read,cimg_iobuffer/2));
  46525. cimg::fread(raw._data,raw._width,nfile);
  46526. if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width);
  46527. to_read-=raw._width;
  46528. const unsigned short *ptrs = raw._data;
  46529. for (ulongT off = (ulongT)raw._width/3; off; --off) {
  46530. *(ptr_r++) = (T)*(ptrs++);
  46531. *(ptr_g++) = (T)*(ptrs++);
  46532. *(ptr_b++) = (T)*(ptrs++);
  46533. }
  46534. }
  46535. }
  46536. } break;
  46537. case 8 : { // 2d/3d grey binary with int32 integers (PINK extension).
  46538. CImg<intT> raw;
  46539. assign(W,H,D,1);
  46540. T *ptrd = data(0,0,0,0);
  46541. for (longT to_read = (longT)size(); to_read>0; ) {
  46542. raw.assign(std::min(to_read,cimg_iobuffer));
  46543. cimg::fread(raw._data,raw._width,nfile);
  46544. to_read-=raw._width;
  46545. const int *ptrs = raw._data;
  46546. for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
  46547. }
  46548. } break;
  46549. case 9 : { // 2d/3d grey binary with float values (PINK extension).
  46550. CImg<floatT> raw;
  46551. assign(W,H,D,1);
  46552. T *ptrd = data(0,0,0,0);
  46553. for (longT to_read = (longT)size(); to_read>0; ) {
  46554. raw.assign(std::min(to_read,cimg_iobuffer));
  46555. cimg::fread(raw._data,raw._width,nfile);
  46556. to_read-=raw._width;
  46557. const float *ptrs = raw._data;
  46558. for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
  46559. }
  46560. } break;
  46561. default :
  46562. assign();
  46563. if (!file) cimg::fclose(nfile);
  46564. throw CImgIOException(_cimg_instance
  46565. "load_pnm(): PNM type 'P%d' found, but type is not supported.",
  46566. cimg_instance,
  46567. filename?filename:"(FILE*)",ppm_type);
  46568. }
  46569. if (!file) cimg::fclose(nfile);
  46570. return *this;
  46571. }
  46572. //! Load image from a PFM file.
  46573. /**
  46574. \param filename Filename, as a C-string.
  46575. **/
  46576. CImg<T>& load_pfm(const char *const filename) {
  46577. return _load_pfm(0,filename);
  46578. }
  46579. //! Load image from a PFM file \newinstance.
  46580. static CImg<T> get_load_pfm(const char *const filename) {
  46581. return CImg<T>().load_pfm(filename);
  46582. }
  46583. //! Load image from a PFM file \overloading.
  46584. CImg<T>& load_pfm(std::FILE *const file) {
  46585. return _load_pfm(file,0);
  46586. }
  46587. //! Load image from a PFM file \newinstance.
  46588. static CImg<T> get_load_pfm(std::FILE *const file) {
  46589. return CImg<T>().load_pfm(file);
  46590. }
  46591. CImg<T>& _load_pfm(std::FILE *const file, const char *const filename) {
  46592. if (!file && !filename)
  46593. throw CImgArgumentException(_cimg_instance
  46594. "load_pfm(): Specified filename is (null).",
  46595. cimg_instance);
  46596. std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
  46597. char pfm_type;
  46598. CImg<charT> item(16384,1,1,1,0);
  46599. int W = 0, H = 0, err = 0;
  46600. double scale = 0;
  46601. while ((err=std::fscanf(nfile,"%16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
  46602. if (cimg_sscanf(item," P%c",&pfm_type)!=1) {
  46603. if (!file) cimg::fclose(nfile);
  46604. throw CImgIOException(_cimg_instance
  46605. "load_pfm(): PFM header not found in file '%s'.",
  46606. cimg_instance,
  46607. filename?filename:"(FILE*)");
  46608. }
  46609. while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
  46610. if ((err=cimg_sscanf(item," %d %d",&W,&H))<2) {
  46611. if (!file) cimg::fclose(nfile);
  46612. throw CImgIOException(_cimg_instance
  46613. "load_pfm(): WIDTH and HEIGHT fields are undefined in file '%s'.",
  46614. cimg_instance,
  46615. filename?filename:"(FILE*)");
  46616. }
  46617. if (err==2) {
  46618. while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
  46619. if (cimg_sscanf(item,"%lf",&scale)!=1)
  46620. cimg::warn(_cimg_instance
  46621. "load_pfm(): SCALE field is undefined in file '%s'.",
  46622. cimg_instance,
  46623. filename?filename:"(FILE*)");
  46624. }
  46625. std::fgetc(nfile);
  46626. const bool is_color = (pfm_type=='F'), is_inverted = (scale>0)!=cimg::endianness();
  46627. if (is_color) {
  46628. assign(W,H,1,3,(T)0);
  46629. CImg<floatT> buf(3*W);
  46630. T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
  46631. cimg_forY(*this,y) {
  46632. cimg::fread(buf._data,3*W,nfile);
  46633. if (is_inverted) cimg::invert_endianness(buf._data,3*W);
  46634. const float *ptrs = buf._data;
  46635. cimg_forX(*this,x) {
  46636. *(ptr_r++) = (T)*(ptrs++);
  46637. *(ptr_g++) = (T)*(ptrs++);
  46638. *(ptr_b++) = (T)*(ptrs++);
  46639. }
  46640. }
  46641. } else {
  46642. assign(W,H,1,1,(T)0);
  46643. CImg<floatT> buf(W);
  46644. T *ptrd = data(0,0,0,0);
  46645. cimg_forY(*this,y) {
  46646. cimg::fread(buf._data,W,nfile);
  46647. if (is_inverted) cimg::invert_endianness(buf._data,W);
  46648. const float *ptrs = buf._data;
  46649. cimg_forX(*this,x) *(ptrd++) = (T)*(ptrs++);
  46650. }
  46651. }
  46652. if (!file) cimg::fclose(nfile);
  46653. return mirror('y'); // Most of the .pfm files are flipped along the y-axis.
  46654. }
  46655. //! Load image from a RGB file.
  46656. /**
  46657. \param filename Filename, as a C-string.
  46658. \param dimw Width of the image buffer.
  46659. \param dimh Height of the image buffer.
  46660. **/
  46661. CImg<T>& load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
  46662. return _load_rgb(0,filename,dimw,dimh);
  46663. }
  46664. //! Load image from a RGB file \newinstance.
  46665. static CImg<T> get_load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
  46666. return CImg<T>().load_rgb(filename,dimw,dimh);
  46667. }
  46668. //! Load image from a RGB file \overloading.
  46669. CImg<T>& load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
  46670. return _load_rgb(file,0,dimw,dimh);
  46671. }
  46672. //! Load image from a RGB file \newinstance.
  46673. static CImg<T> get_load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
  46674. return CImg<T>().load_rgb(file,dimw,dimh);
  46675. }
  46676. CImg<T>& _load_rgb(std::FILE *const file, const char *const filename,
  46677. const unsigned int dimw, const unsigned int dimh) {
  46678. if (!file && !filename)
  46679. throw CImgArgumentException(_cimg_instance
  46680. "load_rgb(): Specified filename is (null).",
  46681. cimg_instance);
  46682. if (!dimw || !dimh) return assign();
  46683. const longT cimg_iobuffer = (longT)24*1024*1024;
  46684. std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
  46685. CImg<ucharT> raw;
  46686. assign(dimw,dimh,1,3);
  46687. T
  46688. *ptr_r = data(0,0,0,0),
  46689. *ptr_g = data(0,0,0,1),
  46690. *ptr_b = data(0,0,0,2);
  46691. for (longT to_read = (longT)size(); to_read>0; ) {
  46692. raw.assign(std::min(to_read,cimg_iobuffer));
  46693. cimg::fread(raw._data,raw._width,nfile);
  46694. to_read-=raw._width;
  46695. const unsigned char *ptrs = raw._data;
  46696. for (ulongT off = raw._width/3UL; off; --off) {
  46697. *(ptr_r++) = (T)*(ptrs++);
  46698. *(ptr_g++) = (T)*(ptrs++);
  46699. *(ptr_b++) = (T)*(ptrs++);
  46700. }
  46701. }
  46702. if (!file) cimg::fclose(nfile);
  46703. return *this;
  46704. }
  46705. //! Load image from a RGBA file.
  46706. /**
  46707. \param filename Filename, as a C-string.
  46708. \param dimw Width of the image buffer.
  46709. \param dimh Height of the image buffer.
  46710. **/
  46711. CImg<T>& load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
  46712. return _load_rgba(0,filename,dimw,dimh);
  46713. }
  46714. //! Load image from a RGBA file \newinstance.
  46715. static CImg<T> get_load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
  46716. return CImg<T>().load_rgba(filename,dimw,dimh);
  46717. }
  46718. //! Load image from a RGBA file \overloading.
  46719. CImg<T>& load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
  46720. return _load_rgba(file,0,dimw,dimh);
  46721. }
  46722. //! Load image from a RGBA file \newinstance.
  46723. static CImg<T> get_load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
  46724. return CImg<T>().load_rgba(file,dimw,dimh);
  46725. }
  46726. CImg<T>& _load_rgba(std::FILE *const file, const char *const filename,
  46727. const unsigned int dimw, const unsigned int dimh) {
  46728. if (!file && !filename)
  46729. throw CImgArgumentException(_cimg_instance
  46730. "load_rgba(): Specified filename is (null).",
  46731. cimg_instance);
  46732. if (!dimw || !dimh) return assign();
  46733. const longT cimg_iobuffer = (longT)24*1024*1024;
  46734. std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
  46735. CImg<ucharT> raw;
  46736. assign(dimw,dimh,1,4);
  46737. T
  46738. *ptr_r = data(0,0,0,0),
  46739. *ptr_g = data(0,0,0,1),
  46740. *ptr_b = data(0,0,0,2),
  46741. *ptr_a = data(0,0,0,3);
  46742. for (longT to_read = (longT)size(); to_read>0; ) {
  46743. raw.assign(std::min(to_read,cimg_iobuffer));
  46744. cimg::fread(raw._data,raw._width,nfile);
  46745. to_read-=raw._width;
  46746. const unsigned char *ptrs = raw._data;
  46747. for (ulongT off = raw._width/4UL; off; --off) {
  46748. *(ptr_r++) = (T)*(ptrs++);
  46749. *(ptr_g++) = (T)*(ptrs++);
  46750. *(ptr_b++) = (T)*(ptrs++);
  46751. *(ptr_a++) = (T)*(ptrs++);
  46752. }
  46753. }
  46754. if (!file) cimg::fclose(nfile);
  46755. return *this;
  46756. }
  46757. //! Load image from a TIFF file.
  46758. /**
  46759. \param filename Filename, as a C-string.
  46760. \param first_frame First frame to read (for multi-pages tiff).
  46761. \param last_frame Last frame to read (for multi-pages tiff).
  46762. \param step_frame Step value of frame reading.
  46763. \param[out] voxel_size Voxel size, as stored in the filename.
  46764. \param[out] description Description, as stored in the filename.
  46765. \note
  46766. - libtiff support is enabled by defining the precompilation
  46767. directive \c cimg_use_tif.
  46768. - When libtiff is enabled, 2D and 3D (multipage) several
  46769. channel per pixel are supported for
  46770. <tt>char,uchar,short,ushort,float</tt> and \c double pixel types.
  46771. - If \c cimg_use_tif is not defined at compile time the
  46772. function uses CImg<T>& load_other(const char*).
  46773. **/
  46774. CImg<T>& load_tiff(const char *const filename,
  46775. const unsigned int first_frame=0, const unsigned int last_frame=~0U,
  46776. const unsigned int step_frame=1,
  46777. float *const voxel_size=0,
  46778. CImg<charT> *const description=0) {
  46779. if (!filename)
  46780. throw CImgArgumentException(_cimg_instance
  46781. "load_tiff(): Specified filename is (null).",
  46782. cimg_instance);
  46783. const unsigned int
  46784. nfirst_frame = first_frame<last_frame?first_frame:last_frame,
  46785. nstep_frame = step_frame?step_frame:1;
  46786. unsigned int nlast_frame = first_frame<last_frame?last_frame:first_frame;
  46787. #ifndef cimg_use_tiff
  46788. cimg::unused(voxel_size,description);
  46789. if (nfirst_frame || nlast_frame!=~0U || nstep_frame>1)
  46790. throw CImgArgumentException(_cimg_instance
  46791. "load_tiff(): Unable to read sub-images from file '%s' unless libtiff is enabled.",
  46792. cimg_instance,
  46793. filename);
  46794. return load_other(filename);
  46795. #else
  46796. #if cimg_verbosity<3
  46797. TIFFSetWarningHandler(0);
  46798. TIFFSetErrorHandler(0);
  46799. #endif
  46800. TIFF *tif = TIFFOpen(filename,"r");
  46801. if (tif) {
  46802. unsigned int nb_images = 0;
  46803. do ++nb_images; while (TIFFReadDirectory(tif));
  46804. if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images))
  46805. cimg::warn(_cimg_instance
  46806. "load_tiff(): File '%s' contains %u image(s) while specified frame range is [%u,%u] (step %u).",
  46807. cimg_instance,
  46808. filename,nb_images,nfirst_frame,nlast_frame,nstep_frame);
  46809. if (nfirst_frame>=nb_images) return assign();
  46810. if (nlast_frame>=nb_images) nlast_frame = nb_images - 1;
  46811. TIFFSetDirectory(tif,0);
  46812. CImg<T> frame;
  46813. for (unsigned int l = nfirst_frame; l<=nlast_frame; l+=nstep_frame) {
  46814. frame._load_tiff(tif,l,voxel_size,description);
  46815. if (l==nfirst_frame)
  46816. assign(frame._width,frame._height,1 + (nlast_frame - nfirst_frame)/nstep_frame,frame._spectrum);
  46817. if (frame._width>_width || frame._height>_height || frame._spectrum>_spectrum)
  46818. resize(std::max(frame._width,_width),
  46819. std::max(frame._height,_height),-100,
  46820. std::max(frame._spectrum,_spectrum),0);
  46821. draw_image(0,0,(l - nfirst_frame)/nstep_frame,frame);
  46822. }
  46823. TIFFClose(tif);
  46824. } else throw CImgIOException(_cimg_instance
  46825. "load_tiff(): Failed to open file '%s'.",
  46826. cimg_instance,
  46827. filename);
  46828. return *this;
  46829. #endif
  46830. }
  46831. //! Load image from a TIFF file \newinstance.
  46832. static CImg<T> get_load_tiff(const char *const filename,
  46833. const unsigned int first_frame=0, const unsigned int last_frame=~0U,
  46834. const unsigned int step_frame=1,
  46835. float *const voxel_size=0,
  46836. CImg<charT> *const description=0) {
  46837. return CImg<T>().load_tiff(filename,first_frame,last_frame,step_frame,voxel_size,description);
  46838. }
  46839. // (Original contribution by Jerome Boulanger).
  46840. #ifdef cimg_use_tiff
  46841. template<typename t>
  46842. void _load_tiff_tiled_contig(TIFF *const tif, const uint16 samplesperpixel,
  46843. const uint32 nx, const uint32 ny, const uint32 tw, const uint32 th) {
  46844. t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif));
  46845. if (buf) {
  46846. for (unsigned int row = 0; row<ny; row+=th)
  46847. for (unsigned int col = 0; col<nx; col+=tw) {
  46848. if (TIFFReadTile(tif,buf,col,row,0,0)<0) {
  46849. _TIFFfree(buf); TIFFClose(tif);
  46850. throw CImgIOException(_cimg_instance
  46851. "load_tiff(): Invalid tile in file '%s'.",
  46852. cimg_instance,
  46853. TIFFFileName(tif));
  46854. }
  46855. const t *ptr = buf;
  46856. for (unsigned int rr = row; rr<std::min((unsigned int)(row + th),(unsigned int)ny); ++rr)
  46857. for (unsigned int cc = col; cc<std::min((unsigned int)(col + tw),(unsigned int)nx); ++cc)
  46858. for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
  46859. (*this)(cc,rr,vv) = (T)(ptr[(rr - row)*th*samplesperpixel + (cc - col)*samplesperpixel + vv]);
  46860. }
  46861. _TIFFfree(buf);
  46862. }
  46863. }
  46864. template<typename t>
  46865. void _load_tiff_tiled_separate(TIFF *const tif, const uint16 samplesperpixel,
  46866. const uint32 nx, const uint32 ny, const uint32 tw, const uint32 th) {
  46867. t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif));
  46868. if (buf) {
  46869. for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
  46870. for (unsigned int row = 0; row<ny; row+=th)
  46871. for (unsigned int col = 0; col<nx; col+=tw) {
  46872. if (TIFFReadTile(tif,buf,col,row,0,vv)<0) {
  46873. _TIFFfree(buf); TIFFClose(tif);
  46874. throw CImgIOException(_cimg_instance
  46875. "load_tiff(): Invalid tile in file '%s'.",
  46876. cimg_instance,
  46877. TIFFFileName(tif));
  46878. }
  46879. const t *ptr = buf;
  46880. for (unsigned int rr = row; rr<std::min((unsigned int)(row + th),(unsigned int)ny); ++rr)
  46881. for (unsigned int cc = col; cc<std::min((unsigned int)(col + tw),(unsigned int)nx); ++cc)
  46882. (*this)(cc,rr,vv) = (T)*(ptr++);
  46883. }
  46884. _TIFFfree(buf);
  46885. }
  46886. }
  46887. template<typename t>
  46888. void _load_tiff_contig(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny) {
  46889. t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
  46890. if (buf) {
  46891. uint32 row, rowsperstrip = (uint32)-1;
  46892. TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip);
  46893. for (row = 0; row<ny; row+= rowsperstrip) {
  46894. uint32 nrow = (row + rowsperstrip>ny?ny - row:rowsperstrip);
  46895. tstrip_t strip = TIFFComputeStrip(tif, row, 0);
  46896. if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) {
  46897. _TIFFfree(buf); TIFFClose(tif);
  46898. throw CImgIOException(_cimg_instance
  46899. "load_tiff(): Invalid strip in file '%s'.",
  46900. cimg_instance,
  46901. TIFFFileName(tif));
  46902. }
  46903. const t *ptr = buf;
  46904. for (unsigned int rr = 0; rr<nrow; ++rr)
  46905. for (unsigned int cc = 0; cc<nx; ++cc)
  46906. for (unsigned int vv = 0; vv<samplesperpixel; ++vv) (*this)(cc,row + rr,vv) = (T)*(ptr++);
  46907. }
  46908. _TIFFfree(buf);
  46909. }
  46910. }
  46911. template<typename t>
  46912. void _load_tiff_separate(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny) {
  46913. t *buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
  46914. if (buf) {
  46915. uint32 row, rowsperstrip = (uint32)-1;
  46916. TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip);
  46917. for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
  46918. for (row = 0; row<ny; row+= rowsperstrip) {
  46919. uint32 nrow = (row + rowsperstrip>ny?ny - row:rowsperstrip);
  46920. tstrip_t strip = TIFFComputeStrip(tif, row, vv);
  46921. if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) {
  46922. _TIFFfree(buf); TIFFClose(tif);
  46923. throw CImgIOException(_cimg_instance
  46924. "load_tiff(): Invalid strip in file '%s'.",
  46925. cimg_instance,
  46926. TIFFFileName(tif));
  46927. }
  46928. const t *ptr = buf;
  46929. for (unsigned int rr = 0;rr<nrow; ++rr)
  46930. for (unsigned int cc = 0; cc<nx; ++cc)
  46931. (*this)(cc,row + rr,vv) = (T)*(ptr++);
  46932. }
  46933. _TIFFfree(buf);
  46934. }
  46935. }
  46936. CImg<T>& _load_tiff(TIFF *const tif, const unsigned int directory,
  46937. float *const voxel_size, CImg<charT> *const description) {
  46938. if (!TIFFSetDirectory(tif,directory)) return assign();
  46939. uint16 samplesperpixel = 1, bitspersample = 8, photo = 0;
  46940. uint16 sampleformat = 1;
  46941. uint32 nx = 1, ny = 1;
  46942. const char *const filename = TIFFFileName(tif);
  46943. const bool is_spp = (bool)TIFFGetField(tif,TIFFTAG_SAMPLESPERPIXEL,&samplesperpixel);
  46944. TIFFGetField(tif,TIFFTAG_IMAGEWIDTH,&nx);
  46945. TIFFGetField(tif,TIFFTAG_IMAGELENGTH,&ny);
  46946. TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, &sampleformat);
  46947. TIFFGetFieldDefaulted(tif,TIFFTAG_BITSPERSAMPLE,&bitspersample);
  46948. TIFFGetField(tif,TIFFTAG_PHOTOMETRIC,&photo);
  46949. if (voxel_size) {
  46950. const char *s_description = 0;
  46951. float vx = 0, vy = 0, vz = 0;
  46952. if (TIFFGetField(tif,TIFFTAG_IMAGEDESCRIPTION,&s_description) && s_description) {
  46953. const char *s_desc = std::strstr(s_description,"VX=");
  46954. if (s_desc && cimg_sscanf(s_desc,"VX=%f VY=%f VZ=%f",&vx,&vy,&vz)==3) { // CImg format.
  46955. voxel_size[0] = vx; voxel_size[1] = vy; voxel_size[2] = vz;
  46956. }
  46957. s_desc = std::strstr(s_description,"spacing=");
  46958. if (s_desc && cimg_sscanf(s_desc,"spacing=%f",&vz)==1) { // fiji format.
  46959. voxel_size[2] = vz;
  46960. }
  46961. }
  46962. TIFFGetField(tif,TIFFTAG_XRESOLUTION,voxel_size);
  46963. TIFFGetField(tif,TIFFTAG_YRESOLUTION,voxel_size + 1);
  46964. voxel_size[0] = 1.0f/voxel_size[0];
  46965. voxel_size[1] = 1.0f/voxel_size[1];
  46966. }
  46967. if (description) {
  46968. const char *s_description = 0;
  46969. if (TIFFGetField(tif,TIFFTAG_IMAGEDESCRIPTION,&s_description) && s_description)
  46970. CImg<charT>::string(s_description).move_to(*description);
  46971. }
  46972. const unsigned int spectrum = !is_spp || photo>=3?(photo>1?3:1):samplesperpixel;
  46973. assign(nx,ny,1,spectrum);
  46974. if ((photo>=3 && sampleformat==1 &&
  46975. (bitspersample==4 || bitspersample==8) &&
  46976. (samplesperpixel==1 || samplesperpixel==3 || samplesperpixel==4)) ||
  46977. (bitspersample==1 && samplesperpixel==1)) {
  46978. // Special case for unsigned color images.
  46979. uint32 *const raster = (uint32*)_TIFFmalloc(nx*ny*sizeof(uint32));
  46980. if (!raster) {
  46981. _TIFFfree(raster); TIFFClose(tif);
  46982. throw CImgException(_cimg_instance
  46983. "load_tiff(): Failed to allocate memory (%s) for file '%s'.",
  46984. cimg_instance,
  46985. cimg::strbuffersize(nx*ny*sizeof(uint32)),filename);
  46986. }
  46987. TIFFReadRGBAImage(tif,nx,ny,raster,0);
  46988. switch (spectrum) {
  46989. case 1 :
  46990. cimg_forXY(*this,x,y)
  46991. (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 -y) + x]);
  46992. break;
  46993. case 3 :
  46994. cimg_forXY(*this,x,y) {
  46995. (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 -y) + x]);
  46996. (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny - 1 -y) + x]);
  46997. (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny - 1 -y) + x]);
  46998. }
  46999. break;
  47000. case 4 :
  47001. cimg_forXY(*this,x,y) {
  47002. (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 - y) + x]);
  47003. (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny - 1 - y) + x]);
  47004. (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny - 1 - y) + x]);
  47005. (*this)(x,y,3) = (T)(float)TIFFGetA(raster[nx*(ny - 1 - y) + x]);
  47006. }
  47007. break;
  47008. }
  47009. _TIFFfree(raster);
  47010. } else { // Other cases.
  47011. uint16 config;
  47012. TIFFGetField(tif,TIFFTAG_PLANARCONFIG,&config);
  47013. if (TIFFIsTiled(tif)) {
  47014. uint32 tw = 1, th = 1;
  47015. TIFFGetField(tif,TIFFTAG_TILEWIDTH,&tw);
  47016. TIFFGetField(tif,TIFFTAG_TILELENGTH,&th);
  47017. if (config==PLANARCONFIG_CONTIG) switch (bitspersample) {
  47018. case 8 :
  47019. if (sampleformat==SAMPLEFORMAT_UINT)
  47020. _load_tiff_tiled_contig<unsigned char>(tif,samplesperpixel,nx,ny,tw,th);
  47021. else _load_tiff_tiled_contig<signed char>(tif,samplesperpixel,nx,ny,tw,th);
  47022. break;
  47023. case 16 :
  47024. if (sampleformat==SAMPLEFORMAT_UINT)
  47025. _load_tiff_tiled_contig<unsigned short>(tif,samplesperpixel,nx,ny,tw,th);
  47026. else _load_tiff_tiled_contig<short>(tif,samplesperpixel,nx,ny,tw,th);
  47027. break;
  47028. case 32 :
  47029. if (sampleformat==SAMPLEFORMAT_UINT)
  47030. _load_tiff_tiled_contig<unsigned int>(tif,samplesperpixel,nx,ny,tw,th);
  47031. else if (sampleformat==SAMPLEFORMAT_INT)
  47032. _load_tiff_tiled_contig<int>(tif,samplesperpixel,nx,ny,tw,th);
  47033. else _load_tiff_tiled_contig<float>(tif,samplesperpixel,nx,ny,tw,th);
  47034. break;
  47035. case 64 :
  47036. if (sampleformat==SAMPLEFORMAT_UINT)
  47037. _load_tiff_tiled_contig<uint64T>(tif,samplesperpixel,nx,ny,tw,th);
  47038. else if (sampleformat==SAMPLEFORMAT_INT)
  47039. _load_tiff_tiled_contig<int64T>(tif,samplesperpixel,nx,ny,tw,th);
  47040. else _load_tiff_tiled_contig<double>(tif,samplesperpixel,nx,ny,tw,th);
  47041. break;
  47042. } else switch (bitspersample) {
  47043. case 8 :
  47044. if (sampleformat==SAMPLEFORMAT_UINT)
  47045. _load_tiff_tiled_separate<unsigned char>(tif,samplesperpixel,nx,ny,tw,th);
  47046. else _load_tiff_tiled_separate<signed char>(tif,samplesperpixel,nx,ny,tw,th);
  47047. break;
  47048. case 16 :
  47049. if (sampleformat==SAMPLEFORMAT_UINT)
  47050. _load_tiff_tiled_separate<unsigned short>(tif,samplesperpixel,nx,ny,tw,th);
  47051. else _load_tiff_tiled_separate<short>(tif,samplesperpixel,nx,ny,tw,th);
  47052. break;
  47053. case 32 :
  47054. if (sampleformat==SAMPLEFORMAT_UINT)
  47055. _load_tiff_tiled_separate<unsigned int>(tif,samplesperpixel,nx,ny,tw,th);
  47056. else if (sampleformat==SAMPLEFORMAT_INT)
  47057. _load_tiff_tiled_separate<int>(tif,samplesperpixel,nx,ny,tw,th);
  47058. else _load_tiff_tiled_separate<float>(tif,samplesperpixel,nx,ny,tw,th);
  47059. break;
  47060. case 64 :
  47061. if (sampleformat==SAMPLEFORMAT_UINT)
  47062. _load_tiff_tiled_separate<uint64T>(tif,samplesperpixel,nx,ny,tw,th);
  47063. else if (sampleformat==SAMPLEFORMAT_INT)
  47064. _load_tiff_tiled_separate<int64T>(tif,samplesperpixel,nx,ny,tw,th);
  47065. else _load_tiff_tiled_separate<double>(tif,samplesperpixel,nx,ny,tw,th);
  47066. break;
  47067. }
  47068. } else {
  47069. if (config==PLANARCONFIG_CONTIG) switch (bitspersample) {
  47070. case 8 :
  47071. if (sampleformat==SAMPLEFORMAT_UINT)
  47072. _load_tiff_contig<unsigned char>(tif,samplesperpixel,nx,ny);
  47073. else _load_tiff_contig<signed char>(tif,samplesperpixel,nx,ny);
  47074. break;
  47075. case 16 :
  47076. if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<unsigned short>(tif,samplesperpixel,nx,ny);
  47077. else _load_tiff_contig<short>(tif,samplesperpixel,nx,ny);
  47078. break;
  47079. case 32 :
  47080. if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<unsigned int>(tif,samplesperpixel,nx,ny);
  47081. else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_contig<int>(tif,samplesperpixel,nx,ny);
  47082. else _load_tiff_contig<float>(tif,samplesperpixel,nx,ny);
  47083. break;
  47084. case 64 :
  47085. if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<uint64T>(tif,samplesperpixel,nx,ny);
  47086. else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_contig<int64T>(tif,samplesperpixel,nx,ny);
  47087. else _load_tiff_contig<double>(tif,samplesperpixel,nx,ny);
  47088. break;
  47089. } else switch (bitspersample) {
  47090. case 8 :
  47091. if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned char>(tif,samplesperpixel,nx,ny);
  47092. else _load_tiff_separate<signed char>(tif,samplesperpixel,nx,ny);
  47093. break;
  47094. case 16 :
  47095. if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned short>(tif,samplesperpixel,nx,ny);
  47096. else _load_tiff_separate<short>(tif,samplesperpixel,nx,ny);
  47097. break;
  47098. case 32 :
  47099. if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned int>(tif,samplesperpixel,nx,ny);
  47100. else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_separate<int>(tif,samplesperpixel,nx,ny);
  47101. else _load_tiff_separate<float>(tif,samplesperpixel,nx,ny);
  47102. break;
  47103. case 64 :
  47104. if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<uint64T>(tif,samplesperpixel,nx,ny);
  47105. else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_separate<int64T>(tif,samplesperpixel,nx,ny);
  47106. else _load_tiff_separate<double>(tif,samplesperpixel,nx,ny);
  47107. break;
  47108. }
  47109. }
  47110. }
  47111. return *this;
  47112. }
  47113. #endif
  47114. //! Load image from a MINC2 file.
  47115. /**
  47116. \param filename Filename, as a C-string.
  47117. **/
  47118. // (Original code by Haz-Edine Assemlal).
  47119. CImg<T>& load_minc2(const char *const filename) {
  47120. if (!filename)
  47121. throw CImgArgumentException(_cimg_instance
  47122. "load_minc2(): Specified filename is (null).",
  47123. cimg_instance);
  47124. #ifndef cimg_use_minc2
  47125. return load_other(filename);
  47126. #else
  47127. minc::minc_1_reader rdr;
  47128. rdr.open(filename);
  47129. assign(rdr.ndim(1)?rdr.ndim(1):1,
  47130. rdr.ndim(2)?rdr.ndim(2):1,
  47131. rdr.ndim(3)?rdr.ndim(3):1,
  47132. rdr.ndim(4)?rdr.ndim(4):1);
  47133. if (cimg::type<T>::string()==cimg::type<unsigned char>::string())
  47134. rdr.setup_read_byte();
  47135. else if (cimg::type<T>::string()==cimg::type<int>::string())
  47136. rdr.setup_read_int();
  47137. else if (cimg::type<T>::string()==cimg::type<double>::string())
  47138. rdr.setup_read_double();
  47139. else
  47140. rdr.setup_read_float();
  47141. minc::load_standard_volume(rdr,this->_data);
  47142. return *this;
  47143. #endif
  47144. }
  47145. //! Load image from a MINC2 file \newinstance.
  47146. static CImg<T> get_load_minc2(const char *const filename) {
  47147. return CImg<T>().load_analyze(filename);
  47148. }
  47149. //! Load image from an ANALYZE7.5/NIFTI file.
  47150. /**
  47151. \param filename Filename, as a C-string.
  47152. \param[out] voxel_size Pointer to the three voxel sizes read from the file.
  47153. **/
  47154. CImg<T>& load_analyze(const char *const filename, float *const voxel_size=0) {
  47155. return _load_analyze(0,filename,voxel_size);
  47156. }
  47157. //! Load image from an ANALYZE7.5/NIFTI file \newinstance.
  47158. static CImg<T> get_load_analyze(const char *const filename, float *const voxel_size=0) {
  47159. return CImg<T>().load_analyze(filename,voxel_size);
  47160. }
  47161. //! Load image from an ANALYZE7.5/NIFTI file \overloading.
  47162. CImg<T>& load_analyze(std::FILE *const file, float *const voxel_size=0) {
  47163. return _load_analyze(file,0,voxel_size);
  47164. }
  47165. //! Load image from an ANALYZE7.5/NIFTI file \newinstance.
  47166. static CImg<T> get_load_analyze(std::FILE *const file, float *const voxel_size=0) {
  47167. return CImg<T>().load_analyze(file,voxel_size);
  47168. }
  47169. CImg<T>& _load_analyze(std::FILE *const file, const char *const filename, float *const voxel_size=0) {
  47170. if (!file && !filename)
  47171. throw CImgArgumentException(_cimg_instance
  47172. "load_analyze(): Specified filename is (null).",
  47173. cimg_instance);
  47174. std::FILE *nfile_header = 0, *nfile = 0;
  47175. if (!file) {
  47176. CImg<charT> body(1024);
  47177. const char *const ext = cimg::split_filename(filename,body);
  47178. if (!cimg::strcasecmp(ext,"hdr")) { // File is an Analyze header file.
  47179. nfile_header = cimg::fopen(filename,"rb");
  47180. cimg_sprintf(body._data + std::strlen(body),".img");
  47181. nfile = cimg::fopen(body,"rb");
  47182. } else if (!cimg::strcasecmp(ext,"img")) { // File is an Analyze data file.
  47183. nfile = cimg::fopen(filename,"rb");
  47184. cimg_sprintf(body._data + std::strlen(body),".hdr");
  47185. nfile_header = cimg::fopen(body,"rb");
  47186. } else nfile_header = nfile = cimg::fopen(filename,"rb"); // File is a Niftii file.
  47187. } else nfile_header = nfile = file; // File is a Niftii file.
  47188. if (!nfile || !nfile_header)
  47189. throw CImgIOException(_cimg_instance
  47190. "load_analyze(): Invalid Analyze7.5 or NIFTI header in file '%s'.",
  47191. cimg_instance,
  47192. filename?filename:"(FILE*)");
  47193. // Read header.
  47194. bool endian = false;
  47195. unsigned int header_size;
  47196. cimg::fread(&header_size,1,nfile_header);
  47197. if (!header_size)
  47198. throw CImgIOException(_cimg_instance
  47199. "load_analyze(): Invalid zero-size header in file '%s'.",
  47200. cimg_instance,
  47201. filename?filename:"(FILE*)");
  47202. if (header_size>=4096) { endian = true; cimg::invert_endianness(header_size); }
  47203. unsigned char *const header = new unsigned char[header_size];
  47204. cimg::fread(header + 4,header_size - 4,nfile_header);
  47205. if (!file && nfile_header!=nfile) cimg::fclose(nfile_header);
  47206. if (endian) {
  47207. cimg::invert_endianness((short*)(header + 40),5);
  47208. cimg::invert_endianness((short*)(header + 70),1);
  47209. cimg::invert_endianness((short*)(header + 72),1);
  47210. cimg::invert_endianness((float*)(header + 76),4);
  47211. cimg::invert_endianness((float*)(header + 108),1);
  47212. cimg::invert_endianness((float*)(header + 112),1);
  47213. }
  47214. if (nfile_header==nfile) {
  47215. const unsigned int vox_offset = (unsigned int)*(float*)(header + 108);
  47216. std::fseek(nfile,vox_offset,SEEK_SET);
  47217. }
  47218. unsigned short *dim = (unsigned short*)(header + 40), dimx = 1, dimy = 1, dimz = 1, dimv = 1;
  47219. if (!dim[0])
  47220. cimg::warn(_cimg_instance
  47221. "load_analyze(): File '%s' defines an image with zero dimensions.",
  47222. cimg_instance,
  47223. filename?filename:"(FILE*)");
  47224. if (dim[0]>4)
  47225. cimg::warn(_cimg_instance
  47226. "load_analyze(): File '%s' defines an image with %u dimensions, reading only the 4 first.",
  47227. cimg_instance,
  47228. filename?filename:"(FILE*)",dim[0]);
  47229. if (dim[0]>=1) dimx = dim[1];
  47230. if (dim[0]>=2) dimy = dim[2];
  47231. if (dim[0]>=3) dimz = dim[3];
  47232. if (dim[0]>=4) dimv = dim[4];
  47233. float scalefactor = *(float*)(header + 112); if (scalefactor==0) scalefactor = 1;
  47234. const unsigned short datatype = *(unsigned short*)(header + 70);
  47235. if (voxel_size) {
  47236. const float *vsize = (float*)(header + 76);
  47237. voxel_size[0] = vsize[1]; voxel_size[1] = vsize[2]; voxel_size[2] = vsize[3];
  47238. }
  47239. delete[] header;
  47240. // Read pixel data.
  47241. assign(dimx,dimy,dimz,dimv);
  47242. const size_t pdim = (size_t)dimx*dimy*dimz*dimv;
  47243. switch (datatype) {
  47244. case 2 : {
  47245. unsigned char *const buffer = new unsigned char[pdim];
  47246. cimg::fread(buffer,pdim,nfile);
  47247. cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
  47248. delete[] buffer;
  47249. } break;
  47250. case 4 : {
  47251. short *const buffer = new short[pdim];
  47252. cimg::fread(buffer,pdim,nfile);
  47253. if (endian) cimg::invert_endianness(buffer,pdim);
  47254. cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
  47255. delete[] buffer;
  47256. } break;
  47257. case 8 : {
  47258. int *const buffer = new int[pdim];
  47259. cimg::fread(buffer,pdim,nfile);
  47260. if (endian) cimg::invert_endianness(buffer,pdim);
  47261. cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
  47262. delete[] buffer;
  47263. } break;
  47264. case 16 : {
  47265. float *const buffer = new float[pdim];
  47266. cimg::fread(buffer,pdim,nfile);
  47267. if (endian) cimg::invert_endianness(buffer,pdim);
  47268. cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
  47269. delete[] buffer;
  47270. } break;
  47271. case 64 : {
  47272. double *const buffer = new double[pdim];
  47273. cimg::fread(buffer,pdim,nfile);
  47274. if (endian) cimg::invert_endianness(buffer,pdim);
  47275. cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
  47276. delete[] buffer;
  47277. } break;
  47278. default :
  47279. if (!file) cimg::fclose(nfile);
  47280. throw CImgIOException(_cimg_instance
  47281. "load_analyze(): Unable to load datatype %d in file '%s'",
  47282. cimg_instance,
  47283. datatype,filename?filename:"(FILE*)");
  47284. }
  47285. if (!file) cimg::fclose(nfile);
  47286. return *this;
  47287. }
  47288. //! Load image from a .cimg[z] file.
  47289. /**
  47290. \param filename Filename, as a C-string.
  47291. \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
  47292. \param align Appending alignment.
  47293. **/
  47294. CImg<T>& load_cimg(const char *const filename, const char axis='z', const float align=0) {
  47295. CImgList<T> list;
  47296. list.load_cimg(filename);
  47297. if (list._width==1) return list[0].move_to(*this);
  47298. return assign(list.get_append(axis,align));
  47299. }
  47300. //! Load image from a .cimg[z] file \newinstance
  47301. static CImg<T> get_load_cimg(const char *const filename, const char axis='z', const float align=0) {
  47302. return CImg<T>().load_cimg(filename,axis,align);
  47303. }
  47304. //! Load image from a .cimg[z] file \overloading.
  47305. CImg<T>& load_cimg(std::FILE *const file, const char axis='z', const float align=0) {
  47306. CImgList<T> list;
  47307. list.load_cimg(file);
  47308. if (list._width==1) return list[0].move_to(*this);
  47309. return assign(list.get_append(axis,align));
  47310. }
  47311. //! Load image from a .cimg[z] file \newinstance
  47312. static CImg<T> get_load_cimg(std::FILE *const file, const char axis='z', const float align=0) {
  47313. return CImg<T>().load_cimg(file,axis,align);
  47314. }
  47315. //! Load sub-images of a .cimg file.
  47316. /**
  47317. \param filename Filename, as a C-string.
  47318. \param n0 Starting frame.
  47319. \param n1 Ending frame (~0U for max).
  47320. \param x0 X-coordinate of the starting sub-image vertex.
  47321. \param y0 Y-coordinate of the starting sub-image vertex.
  47322. \param z0 Z-coordinate of the starting sub-image vertex.
  47323. \param c0 C-coordinate of the starting sub-image vertex.
  47324. \param x1 X-coordinate of the ending sub-image vertex (~0U for max).
  47325. \param y1 Y-coordinate of the ending sub-image vertex (~0U for max).
  47326. \param z1 Z-coordinate of the ending sub-image vertex (~0U for max).
  47327. \param c1 C-coordinate of the ending sub-image vertex (~0U for max).
  47328. \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
  47329. \param align Appending alignment.
  47330. **/
  47331. CImg<T>& load_cimg(const char *const filename,
  47332. const unsigned int n0, const unsigned int n1,
  47333. const unsigned int x0, const unsigned int y0,
  47334. const unsigned int z0, const unsigned int c0,
  47335. const unsigned int x1, const unsigned int y1,
  47336. const unsigned int z1, const unsigned int c1,
  47337. const char axis='z', const float align=0) {
  47338. CImgList<T> list;
  47339. list.load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
  47340. if (list._width==1) return list[0].move_to(*this);
  47341. return assign(list.get_append(axis,align));
  47342. }
  47343. //! Load sub-images of a .cimg file \newinstance.
  47344. static CImg<T> get_load_cimg(const char *const filename,
  47345. const unsigned int n0, const unsigned int n1,
  47346. const unsigned int x0, const unsigned int y0,
  47347. const unsigned int z0, const unsigned int c0,
  47348. const unsigned int x1, const unsigned int y1,
  47349. const unsigned int z1, const unsigned int c1,
  47350. const char axis='z', const float align=0) {
  47351. return CImg<T>().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align);
  47352. }
  47353. //! Load sub-images of a .cimg file \overloading.
  47354. CImg<T>& load_cimg(std::FILE *const file,
  47355. const unsigned int n0, const unsigned int n1,
  47356. const unsigned int x0, const unsigned int y0,
  47357. const unsigned int z0, const unsigned int c0,
  47358. const unsigned int x1, const unsigned int y1,
  47359. const unsigned int z1, const unsigned int c1,
  47360. const char axis='z', const float align=0) {
  47361. CImgList<T> list;
  47362. list.load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
  47363. if (list._width==1) return list[0].move_to(*this);
  47364. return assign(list.get_append(axis,align));
  47365. }
  47366. //! Load sub-images of a .cimg file \newinstance.
  47367. static CImg<T> get_load_cimg(std::FILE *const file,
  47368. const unsigned int n0, const unsigned int n1,
  47369. const unsigned int x0, const unsigned int y0,
  47370. const unsigned int z0, const unsigned int c0,
  47371. const unsigned int x1, const unsigned int y1,
  47372. const unsigned int z1, const unsigned int c1,
  47373. const char axis='z', const float align=0) {
  47374. return CImg<T>().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align);
  47375. }
  47376. //! Load image from an INRIMAGE-4 file.
  47377. /**
  47378. \param filename Filename, as a C-string.
  47379. \param[out] voxel_size Pointer to the three voxel sizes read from the file.
  47380. **/
  47381. CImg<T>& load_inr(const char *const filename, float *const voxel_size=0) {
  47382. return _load_inr(0,filename,voxel_size);
  47383. }
  47384. //! Load image from an INRIMAGE-4 file \newinstance.
  47385. static CImg<T> get_load_inr(const char *const filename, float *const voxel_size=0) {
  47386. return CImg<T>().load_inr(filename,voxel_size);
  47387. }
  47388. //! Load image from an INRIMAGE-4 file \overloading.
  47389. CImg<T>& load_inr(std::FILE *const file, float *const voxel_size=0) {
  47390. return _load_inr(file,0,voxel_size);
  47391. }
  47392. //! Load image from an INRIMAGE-4 file \newinstance.
  47393. static CImg<T> get_load_inr(std::FILE *const file, float *voxel_size=0) {
  47394. return CImg<T>().load_inr(file,voxel_size);
  47395. }
  47396. static void _load_inr_header(std::FILE *file, int out[8], float *const voxel_size) {
  47397. CImg<charT> item(1024), tmp1(64), tmp2(64);
  47398. *item = *tmp1 = *tmp2 = 0;
  47399. out[0] = std::fscanf(file,"%63s",item._data);
  47400. out[0] = out[1] = out[2] = out[3] = out[5] = 1; out[4] = out[6] = out[7] = -1;
  47401. if (cimg::strncasecmp(item,"#INRIMAGE-4#{",13)!=0)
  47402. throw CImgIOException("CImg<%s>::load_inr(): INRIMAGE-4 header not found.",
  47403. pixel_type());
  47404. while (std::fscanf(file," %63[^\n]%*c",item._data)!=EOF && std::strncmp(item,"##}",3)) {
  47405. cimg_sscanf(item," XDIM%*[^0-9]%d",out);
  47406. cimg_sscanf(item," YDIM%*[^0-9]%d",out + 1);
  47407. cimg_sscanf(item," ZDIM%*[^0-9]%d",out + 2);
  47408. cimg_sscanf(item," VDIM%*[^0-9]%d",out + 3);
  47409. cimg_sscanf(item," PIXSIZE%*[^0-9]%d",out + 6);
  47410. if (voxel_size) {
  47411. cimg_sscanf(item," VX%*[^0-9.+-]%f",voxel_size);
  47412. cimg_sscanf(item," VY%*[^0-9.+-]%f",voxel_size + 1);
  47413. cimg_sscanf(item," VZ%*[^0-9.+-]%f",voxel_size + 2);
  47414. }
  47415. if (cimg_sscanf(item," CPU%*[ =]%s",tmp1._data)) out[7] = cimg::strncasecmp(tmp1,"sun",3)?0:1;
  47416. switch (cimg_sscanf(item," TYPE%*[ =]%s %s",tmp1._data,tmp2._data)) {
  47417. case 0 : break;
  47418. case 2 :
  47419. out[5] = cimg::strncasecmp(tmp1,"unsigned",8)?1:0;
  47420. std::strncpy(tmp1,tmp2,tmp1._width - 1); // fallthrough
  47421. case 1 :
  47422. if (!cimg::strncasecmp(tmp1,"int",3) || !cimg::strncasecmp(tmp1,"fixed",5)) out[4] = 0;
  47423. if (!cimg::strncasecmp(tmp1,"float",5) || !cimg::strncasecmp(tmp1,"double",6)) out[4] = 1;
  47424. if (!cimg::strncasecmp(tmp1,"packed",6)) out[4] = 2;
  47425. if (out[4]>=0) break; // fallthrough
  47426. default :
  47427. throw CImgIOException("CImg<%s>::load_inr(): Invalid pixel type '%s' defined in header.",
  47428. pixel_type(),
  47429. tmp2._data);
  47430. }
  47431. }
  47432. if (out[0]<0 || out[1]<0 || out[2]<0 || out[3]<0)
  47433. throw CImgIOException("CImg<%s>::load_inr(): Invalid dimensions (%d,%d,%d,%d) defined in header.",
  47434. pixel_type(),
  47435. out[0],out[1],out[2],out[3]);
  47436. if (out[4]<0 || out[5]<0)
  47437. throw CImgIOException("CImg<%s>::load_inr(): Incomplete pixel type defined in header.",
  47438. pixel_type());
  47439. if (out[6]<0)
  47440. throw CImgIOException("CImg<%s>::load_inr(): Incomplete PIXSIZE field defined in header.",
  47441. pixel_type());
  47442. if (out[7]<0)
  47443. throw CImgIOException("CImg<%s>::load_inr(): Big/Little Endian coding type undefined in header.",
  47444. pixel_type());
  47445. }
  47446. CImg<T>& _load_inr(std::FILE *const file, const char *const filename, float *const voxel_size) {
  47447. #define _cimg_load_inr_case(Tf,sign,pixsize,Ts) \
  47448. if (!loaded && fopt[6]==pixsize && fopt[4]==Tf && fopt[5]==sign) { \
  47449. Ts *xval, *const val = new Ts[(size_t)fopt[0]*fopt[3]]; \
  47450. cimg_forYZ(*this,y,z) { \
  47451. cimg::fread(val,fopt[0]*fopt[3],nfile); \
  47452. if (fopt[7]!=endian) cimg::invert_endianness(val,fopt[0]*fopt[3]); \
  47453. xval = val; cimg_forX(*this,x) cimg_forC(*this,c) (*this)(x,y,z,c) = (T)*(xval++); \
  47454. } \
  47455. delete[] val; \
  47456. loaded = true; \
  47457. }
  47458. if (!file && !filename)
  47459. throw CImgArgumentException(_cimg_instance
  47460. "load_inr(): Specified filename is (null).",
  47461. cimg_instance);
  47462. std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
  47463. int fopt[8], endian = cimg::endianness()?1:0;
  47464. bool loaded = false;
  47465. if (voxel_size) voxel_size[0] = voxel_size[1] = voxel_size[2] = 1;
  47466. _load_inr_header(nfile,fopt,voxel_size);
  47467. assign(fopt[0],fopt[1],fopt[2],fopt[3]);
  47468. _cimg_load_inr_case(0,0,8,unsigned char);
  47469. _cimg_load_inr_case(0,1,8,char);
  47470. _cimg_load_inr_case(0,0,16,unsigned short);
  47471. _cimg_load_inr_case(0,1,16,short);
  47472. _cimg_load_inr_case(0,0,32,unsigned int);
  47473. _cimg_load_inr_case(0,1,32,int);
  47474. _cimg_load_inr_case(1,0,32,float);
  47475. _cimg_load_inr_case(1,1,32,float);
  47476. _cimg_load_inr_case(1,0,64,double);
  47477. _cimg_load_inr_case(1,1,64,double);
  47478. if (!loaded) {
  47479. if (!file) cimg::fclose(nfile);
  47480. throw CImgIOException(_cimg_instance
  47481. "load_inr(): Unknown pixel type defined in file '%s'.",
  47482. cimg_instance,
  47483. filename?filename:"(FILE*)");
  47484. }
  47485. if (!file) cimg::fclose(nfile);
  47486. return *this;
  47487. }
  47488. //! Load image from a EXR file.
  47489. /**
  47490. \param filename Filename, as a C-string.
  47491. **/
  47492. CImg<T>& load_exr(const char *const filename) {
  47493. if (!filename)
  47494. throw CImgArgumentException(_cimg_instance
  47495. "load_exr(): Specified filename is (null).",
  47496. cimg_instance);
  47497. #if defined(cimg_use_openexr)
  47498. Imf::RgbaInputFile file(filename);
  47499. Imath::Box2i dw = file.dataWindow();
  47500. const int
  47501. inwidth = dw.max.x - dw.min.x + 1,
  47502. inheight = dw.max.y - dw.min.y + 1;
  47503. Imf::Array2D<Imf::Rgba> pixels;
  47504. pixels.resizeErase(inheight,inwidth);
  47505. file.setFrameBuffer(&pixels[0][0] - dw.min.x - dw.min.y*inwidth, 1, inwidth);
  47506. file.readPixels(dw.min.y, dw.max.y);
  47507. assign(inwidth,inheight,1,4);
  47508. 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);
  47509. cimg_forXY(*this,x,y) {
  47510. *(ptr_r++) = (T)pixels[y][x].r;
  47511. *(ptr_g++) = (T)pixels[y][x].g;
  47512. *(ptr_b++) = (T)pixels[y][x].b;
  47513. *(ptr_a++) = (T)pixels[y][x].a;
  47514. }
  47515. #elif defined(cimg_use_tinyexr)
  47516. float *res;
  47517. const char *err = 0;
  47518. int width = 0, height = 0;
  47519. const int ret = LoadEXR(&res,&width,&height,filename,&err);
  47520. if (ret) throw CImgIOException(_cimg_instance
  47521. "load_exr(): Unable to load EXR file '%s'.",
  47522. cimg_instance,filename);
  47523. CImg<floatT>(out,4,width,height,1,true).get_permute_axes("yzcx").move_to(*this);
  47524. std::free(res);
  47525. #else
  47526. return load_other(filename);
  47527. #endif
  47528. return *this;
  47529. }
  47530. //! Load image from a EXR file \newinstance.
  47531. static CImg<T> get_load_exr(const char *const filename) {
  47532. return CImg<T>().load_exr(filename);
  47533. }
  47534. //! Load image from a PANDORE-5 file.
  47535. /**
  47536. \param filename Filename, as a C-string.
  47537. **/
  47538. CImg<T>& load_pandore(const char *const filename) {
  47539. return _load_pandore(0,filename);
  47540. }
  47541. //! Load image from a PANDORE-5 file \newinstance.
  47542. static CImg<T> get_load_pandore(const char *const filename) {
  47543. return CImg<T>().load_pandore(filename);
  47544. }
  47545. //! Load image from a PANDORE-5 file \overloading.
  47546. CImg<T>& load_pandore(std::FILE *const file) {
  47547. return _load_pandore(file,0);
  47548. }
  47549. //! Load image from a PANDORE-5 file \newinstance.
  47550. static CImg<T> get_load_pandore(std::FILE *const file) {
  47551. return CImg<T>().load_pandore(file);
  47552. }
  47553. CImg<T>& _load_pandore(std::FILE *const file, const char *const filename) {
  47554. #define __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,ndim,stype) \
  47555. cimg::fread(dims,nbdim,nfile); \
  47556. if (endian) cimg::invert_endianness(dims,nbdim); \
  47557. assign(nwidth,nheight,ndepth,ndim); \
  47558. const size_t siz = size(); \
  47559. stype *buffer = new stype[siz]; \
  47560. cimg::fread(buffer,siz,nfile); \
  47561. if (endian) cimg::invert_endianness(buffer,siz); \
  47562. T *ptrd = _data; \
  47563. cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); \
  47564. buffer-=siz; \
  47565. delete[] buffer
  47566. #define _cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1,stype2,stype3,ltype) { \
  47567. if (sizeof(stype1)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1); } \
  47568. else if (sizeof(stype2)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype2); } \
  47569. else if (sizeof(stype3)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype3); } \
  47570. else throw CImgIOException(_cimg_instance \
  47571. "load_pandore(): Unknown pixel datatype in file '%s'.", \
  47572. cimg_instance, \
  47573. filename?filename:"(FILE*)"); }
  47574. if (!file && !filename)
  47575. throw CImgArgumentException(_cimg_instance
  47576. "load_pandore(): Specified filename is (null).",
  47577. cimg_instance);
  47578. std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
  47579. CImg<charT> header(32);
  47580. cimg::fread(header._data,12,nfile);
  47581. if (cimg::strncasecmp("PANDORE",header,7)) {
  47582. if (!file) cimg::fclose(nfile);
  47583. throw CImgIOException(_cimg_instance
  47584. "load_pandore(): PANDORE header not found in file '%s'.",
  47585. cimg_instance,
  47586. filename?filename:"(FILE*)");
  47587. }
  47588. unsigned int imageid, dims[8] = { 0 };
  47589. int ptbuf[4] = { 0 };
  47590. cimg::fread(&imageid,1,nfile);
  47591. const bool endian = imageid>255;
  47592. if (endian) cimg::invert_endianness(imageid);
  47593. cimg::fread(header._data,20,nfile);
  47594. switch (imageid) {
  47595. case 2 : _cimg_load_pandore_case(2,dims[1],1,1,1,unsigned char,unsigned char,unsigned char,1); break;
  47596. case 3 : _cimg_load_pandore_case(2,dims[1],1,1,1,long,int,short,4); break;
  47597. case 4 : _cimg_load_pandore_case(2,dims[1],1,1,1,double,float,float,4); break;
  47598. case 5 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,unsigned char,unsigned char,unsigned char,1); break;
  47599. case 6 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,long,int,short,4); break;
  47600. case 7 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,double,float,float,4); break;
  47601. case 8 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,unsigned char,unsigned char,unsigned char,1); break;
  47602. case 9 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,long,int,short,4); break;
  47603. case 10 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,double,float,float,4); break;
  47604. case 11 : { // Region 1d
  47605. cimg::fread(dims,3,nfile);
  47606. if (endian) cimg::invert_endianness(dims,3);
  47607. assign(dims[1],1,1,1);
  47608. const unsigned siz = size();
  47609. if (dims[2]<256) {
  47610. unsigned char *buffer = new unsigned char[siz];
  47611. cimg::fread(buffer,siz,nfile);
  47612. T *ptrd = _data;
  47613. cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
  47614. buffer-=siz;
  47615. delete[] buffer;
  47616. } else {
  47617. if (dims[2]<65536) {
  47618. unsigned short *buffer = new unsigned short[siz];
  47619. cimg::fread(buffer,siz,nfile);
  47620. if (endian) cimg::invert_endianness(buffer,siz);
  47621. T *ptrd = _data;
  47622. cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
  47623. buffer-=siz;
  47624. delete[] buffer;
  47625. } else {
  47626. unsigned int *buffer = new unsigned int[siz];
  47627. cimg::fread(buffer,siz,nfile);
  47628. if (endian) cimg::invert_endianness(buffer,siz);
  47629. T *ptrd = _data;
  47630. cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
  47631. buffer-=siz;
  47632. delete[] buffer;
  47633. }
  47634. }
  47635. }
  47636. break;
  47637. case 12 : { // Region 2d
  47638. cimg::fread(dims,4,nfile);
  47639. if (endian) cimg::invert_endianness(dims,4);
  47640. assign(dims[2],dims[1],1,1);
  47641. const size_t siz = size();
  47642. if (dims[3]<256) {
  47643. unsigned char *buffer = new unsigned char[siz];
  47644. cimg::fread(buffer,siz,nfile);
  47645. T *ptrd = _data;
  47646. cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
  47647. buffer-=siz;
  47648. delete[] buffer;
  47649. } else {
  47650. if (dims[3]<65536) {
  47651. unsigned short *buffer = new unsigned short[siz];
  47652. cimg::fread(buffer,siz,nfile);
  47653. if (endian) cimg::invert_endianness(buffer,siz);
  47654. T *ptrd = _data;
  47655. cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
  47656. buffer-=siz;
  47657. delete[] buffer;
  47658. } else {
  47659. unsigned int *buffer = new unsigned int[siz];
  47660. cimg::fread(buffer,siz,nfile);
  47661. if (endian) cimg::invert_endianness(buffer,siz);
  47662. T *ptrd = _data;
  47663. cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
  47664. buffer-=siz;
  47665. delete[] buffer;
  47666. }
  47667. }
  47668. }
  47669. break;
  47670. case 13 : { // Region 3d
  47671. cimg::fread(dims,5,nfile);
  47672. if (endian) cimg::invert_endianness(dims,5);
  47673. assign(dims[3],dims[2],dims[1],1);
  47674. const size_t siz = size();
  47675. if (dims[4]<256) {
  47676. unsigned char *buffer = new unsigned char[siz];
  47677. cimg::fread(buffer,siz,nfile);
  47678. T *ptrd = _data;
  47679. cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
  47680. buffer-=siz;
  47681. delete[] buffer;
  47682. } else {
  47683. if (dims[4]<65536) {
  47684. unsigned short *buffer = new unsigned short[siz];
  47685. cimg::fread(buffer,siz,nfile);
  47686. if (endian) cimg::invert_endianness(buffer,siz);
  47687. T *ptrd = _data;
  47688. cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
  47689. buffer-=siz;
  47690. delete[] buffer;
  47691. } else {
  47692. unsigned int *buffer = new unsigned int[siz];
  47693. cimg::fread(buffer,siz,nfile);
  47694. if (endian) cimg::invert_endianness(buffer,siz);
  47695. T *ptrd = _data;
  47696. cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
  47697. buffer-=siz;
  47698. delete[] buffer;
  47699. }
  47700. }
  47701. }
  47702. break;
  47703. case 16 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,unsigned char,unsigned char,unsigned char,1); break;
  47704. case 17 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,long,int,short,4); break;
  47705. case 18 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,double,float,float,4); break;
  47706. case 19 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,unsigned char,unsigned char,unsigned char,1); break;
  47707. case 20 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,long,int,short,4); break;
  47708. case 21 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,double,float,float,4); break;
  47709. case 22 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned char,unsigned char,unsigned char,1); break;
  47710. case 23 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],long,int,short,4); break;
  47711. case 24 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned long,unsigned int,unsigned short,4); break;
  47712. case 25 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],double,float,float,4); break;
  47713. case 26 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned char,unsigned char,unsigned char,1); break;
  47714. case 27 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],long,int,short,4); break;
  47715. case 28 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned long,unsigned int,unsigned short,4); break;
  47716. case 29 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],double,float,float,4); break;
  47717. case 30 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],unsigned char,unsigned char,unsigned char,1);
  47718. break;
  47719. case 31 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],long,int,short,4); break;
  47720. case 32 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],unsigned long,unsigned int,unsigned short,4);
  47721. break;
  47722. case 33 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],double,float,float,4); break;
  47723. case 34 : { // Points 1d
  47724. cimg::fread(ptbuf,1,nfile);
  47725. if (endian) cimg::invert_endianness(ptbuf,1);
  47726. assign(1); (*this)(0) = (T)ptbuf[0];
  47727. } break;
  47728. case 35 : { // Points 2d
  47729. cimg::fread(ptbuf,2,nfile);
  47730. if (endian) cimg::invert_endianness(ptbuf,2);
  47731. assign(2); (*this)(0) = (T)ptbuf[1]; (*this)(1) = (T)ptbuf[0];
  47732. } break;
  47733. case 36 : { // Points 3d
  47734. cimg::fread(ptbuf,3,nfile);
  47735. if (endian) cimg::invert_endianness(ptbuf,3);
  47736. assign(3); (*this)(0) = (T)ptbuf[2]; (*this)(1) = (T)ptbuf[1]; (*this)(2) = (T)ptbuf[0];
  47737. } break;
  47738. default :
  47739. if (!file) cimg::fclose(nfile);
  47740. throw CImgIOException(_cimg_instance
  47741. "load_pandore(): Unable to load data with ID_type %u in file '%s'.",
  47742. cimg_instance,
  47743. imageid,filename?filename:"(FILE*)");
  47744. }
  47745. if (!file) cimg::fclose(nfile);
  47746. return *this;
  47747. }
  47748. //! Load image from a PAR-REC (Philips) file.
  47749. /**
  47750. \param filename Filename, as a C-string.
  47751. \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
  47752. \param align Appending alignment.
  47753. **/
  47754. CImg<T>& load_parrec(const char *const filename, const char axis='c', const float align=0) {
  47755. CImgList<T> list;
  47756. list.load_parrec(filename);
  47757. if (list._width==1) return list[0].move_to(*this);
  47758. return assign(list.get_append(axis,align));
  47759. }
  47760. //! Load image from a PAR-REC (Philips) file \newinstance.
  47761. static CImg<T> get_load_parrec(const char *const filename, const char axis='c', const float align=0) {
  47762. return CImg<T>().load_parrec(filename,axis,align);
  47763. }
  47764. //! Load image from a raw binary file.
  47765. /**
  47766. \param filename Filename, as a C-string.
  47767. \param size_x Width of the image buffer.
  47768. \param size_y Height of the image buffer.
  47769. \param size_z Depth of the image buffer.
  47770. \param size_c Spectrum of the image buffer.
  47771. \param is_multiplexed Tells if the image values are multiplexed along the C-axis.
  47772. \param invert_endianness Tells if the endianness of the image buffer must be inverted.
  47773. \param offset Starting offset of the read in the specified file.
  47774. **/
  47775. CImg<T>& load_raw(const char *const filename,
  47776. const unsigned int size_x=0, const unsigned int size_y=1,
  47777. const unsigned int size_z=1, const unsigned int size_c=1,
  47778. const bool is_multiplexed=false, const bool invert_endianness=false,
  47779. const ulongT offset=0) {
  47780. return _load_raw(0,filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
  47781. }
  47782. //! Load image from a raw binary file \newinstance.
  47783. static CImg<T> get_load_raw(const char *const filename,
  47784. const unsigned int size_x=0, const unsigned int size_y=1,
  47785. const unsigned int size_z=1, const unsigned int size_c=1,
  47786. const bool is_multiplexed=false, const bool invert_endianness=false,
  47787. const ulongT offset=0) {
  47788. return CImg<T>().load_raw(filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
  47789. }
  47790. //! Load image from a raw binary file \overloading.
  47791. CImg<T>& load_raw(std::FILE *const file,
  47792. const unsigned int size_x=0, const unsigned int size_y=1,
  47793. const unsigned int size_z=1, const unsigned int size_c=1,
  47794. const bool is_multiplexed=false, const bool invert_endianness=false,
  47795. const ulongT offset=0) {
  47796. return _load_raw(file,0,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
  47797. }
  47798. //! Load image from a raw binary file \newinstance.
  47799. static CImg<T> get_load_raw(std::FILE *const file,
  47800. const unsigned int size_x=0, const unsigned int size_y=1,
  47801. const unsigned int size_z=1, const unsigned int size_c=1,
  47802. const bool is_multiplexed=false, const bool invert_endianness=false,
  47803. const ulongT offset=0) {
  47804. return CImg<T>().load_raw(file,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
  47805. }
  47806. CImg<T>& _load_raw(std::FILE *const file, const char *const filename,
  47807. const unsigned int size_x, const unsigned int size_y,
  47808. const unsigned int size_z, const unsigned int size_c,
  47809. const bool is_multiplexed, const bool invert_endianness,
  47810. const ulongT offset) {
  47811. if (!file && !filename)
  47812. throw CImgArgumentException(_cimg_instance
  47813. "load_raw(): Specified filename is (null).",
  47814. cimg_instance);
  47815. if (cimg::is_directory(filename))
  47816. throw CImgArgumentException(_cimg_instance
  47817. "load_raw(): Specified filename '%s' is a directory.",
  47818. cimg_instance,filename);
  47819. ulongT siz = (ulongT)size_x*size_y*size_z*size_c;
  47820. unsigned int
  47821. _size_x = size_x,
  47822. _size_y = size_y,
  47823. _size_z = size_z,
  47824. _size_c = size_c;
  47825. std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
  47826. if (!siz) { // Retrieve file size.
  47827. const longT fpos = cimg::ftell(nfile);
  47828. if (fpos<0) throw CImgArgumentException(_cimg_instance
  47829. "load_raw(): Cannot determine size of input file '%s'.",
  47830. cimg_instance,filename?filename:"(FILE*)");
  47831. cimg::fseek(nfile,0,SEEK_END);
  47832. siz = cimg::ftell(nfile)/sizeof(T);
  47833. _size_y = (unsigned int)siz;
  47834. _size_x = _size_z = _size_c = 1;
  47835. cimg::fseek(nfile,fpos,SEEK_SET);
  47836. }
  47837. cimg::fseek(nfile,offset,SEEK_SET);
  47838. assign(_size_x,_size_y,_size_z,_size_c,0);
  47839. if (siz && (!is_multiplexed || size_c==1)) {
  47840. cimg::fread(_data,siz,nfile);
  47841. if (invert_endianness) cimg::invert_endianness(_data,siz);
  47842. } else if (siz) {
  47843. CImg<T> buf(1,1,1,_size_c);
  47844. cimg_forXYZ(*this,x,y,z) {
  47845. cimg::fread(buf._data,_size_c,nfile);
  47846. if (invert_endianness) cimg::invert_endianness(buf._data,_size_c);
  47847. set_vector_at(buf,x,y,z);
  47848. }
  47849. }
  47850. if (!file) cimg::fclose(nfile);
  47851. return *this;
  47852. }
  47853. //! Load image sequence from a YUV file.
  47854. /**
  47855. \param filename Filename, as a C-string.
  47856. \param size_x Width of the frames.
  47857. \param size_y Height of the frames.
  47858. \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
  47859. \param first_frame Index of the first frame to read.
  47860. \param last_frame Index of the last frame to read.
  47861. \param step_frame Step value for frame reading.
  47862. \param yuv2rgb Tells if the YUV to RGB transform must be applied.
  47863. \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
  47864. **/
  47865. CImg<T>& load_yuv(const char *const filename,
  47866. const unsigned int size_x, const unsigned int size_y=1,
  47867. const unsigned int chroma_subsampling=444,
  47868. const unsigned int first_frame=0, const unsigned int last_frame=~0U,
  47869. const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
  47870. return get_load_yuv(filename,size_x,size_y,chroma_subsampling,
  47871. first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this);
  47872. }
  47873. //! Load image sequence from a YUV file \newinstance.
  47874. static CImg<T> get_load_yuv(const char *const filename,
  47875. const unsigned int size_x, const unsigned int size_y=1,
  47876. const unsigned int chroma_subsampling=444,
  47877. const unsigned int first_frame=0, const unsigned int last_frame=~0U,
  47878. const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
  47879. return CImgList<T>().load_yuv(filename,size_x,size_y,chroma_subsampling,
  47880. first_frame,last_frame,step_frame,yuv2rgb).get_append(axis);
  47881. }
  47882. //! Load image sequence from a YUV file \overloading.
  47883. CImg<T>& load_yuv(std::FILE *const file,
  47884. const unsigned int size_x, const unsigned int size_y=1,
  47885. const unsigned int chroma_subsampling=444,
  47886. const unsigned int first_frame=0, const unsigned int last_frame=~0U,
  47887. const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
  47888. return get_load_yuv(file,size_x,size_y,chroma_subsampling,
  47889. first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this);
  47890. }
  47891. //! Load image sequence from a YUV file \newinstance.
  47892. static CImg<T> get_load_yuv(std::FILE *const file,
  47893. const unsigned int size_x, const unsigned int size_y=1,
  47894. const unsigned int chroma_subsampling=444,
  47895. const unsigned int first_frame=0, const unsigned int last_frame=~0U,
  47896. const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
  47897. return CImgList<T>().load_yuv(file,size_x,size_y,chroma_subsampling,
  47898. first_frame,last_frame,step_frame,yuv2rgb).get_append(axis);
  47899. }
  47900. //! Load 3d object from a .OFF file.
  47901. /**
  47902. \param[out] primitives Primitives data of the 3d object.
  47903. \param[out] colors Colors data of the 3d object.
  47904. \param filename Filename, as a C-string.
  47905. **/
  47906. template<typename tf, typename tc>
  47907. CImg<T>& load_off(CImgList<tf>& primitives, CImgList<tc>& colors, const char *const filename) {
  47908. return _load_off(primitives,colors,0,filename);
  47909. }
  47910. //! Load 3d object from a .OFF file \newinstance.
  47911. template<typename tf, typename tc>
  47912. static CImg<T> get_load_off(CImgList<tf>& primitives, CImgList<tc>& colors, const char *const filename) {
  47913. return CImg<T>().load_off(primitives,colors,filename);
  47914. }
  47915. //! Load 3d object from a .OFF file \overloading.
  47916. template<typename tf, typename tc>
  47917. CImg<T>& load_off(CImgList<tf>& primitives, CImgList<tc>& colors, std::FILE *const file) {
  47918. return _load_off(primitives,colors,file,0);
  47919. }
  47920. //! Load 3d object from a .OFF file \newinstance.
  47921. template<typename tf, typename tc>
  47922. static CImg<T> get_load_off(CImgList<tf>& primitives, CImgList<tc>& colors, std::FILE *const file) {
  47923. return CImg<T>().load_off(primitives,colors,file);
  47924. }
  47925. template<typename tf, typename tc>
  47926. CImg<T>& _load_off(CImgList<tf>& primitives, CImgList<tc>& colors,
  47927. std::FILE *const file, const char *const filename) {
  47928. if (!file && !filename)
  47929. throw CImgArgumentException(_cimg_instance
  47930. "load_off(): Specified filename is (null).",
  47931. cimg_instance);
  47932. std::FILE *const nfile = file?file:cimg::fopen(filename,"r");
  47933. unsigned int nb_points = 0, nb_primitives = 0, nb_read = 0;
  47934. CImg<charT> line(256); *line = 0;
  47935. int err;
  47936. // Skip comments, and read magic string OFF
  47937. do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#'));
  47938. if (cimg::strncasecmp(line,"OFF",3) && cimg::strncasecmp(line,"COFF",4)) {
  47939. if (!file) cimg::fclose(nfile);
  47940. throw CImgIOException(_cimg_instance
  47941. "load_off(): OFF header not found in file '%s'.",
  47942. cimg_instance,
  47943. filename?filename:"(FILE*)");
  47944. }
  47945. do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#'));
  47946. if ((err = cimg_sscanf(line,"%u%u%*[^\n] ",&nb_points,&nb_primitives))!=2) {
  47947. if (!file) cimg::fclose(nfile);
  47948. throw CImgIOException(_cimg_instance
  47949. "load_off(): Invalid number of vertices or primitives specified in file '%s'.",
  47950. cimg_instance,
  47951. filename?filename:"(FILE*)");
  47952. }
  47953. // Read points data
  47954. assign(nb_points,3);
  47955. float X = 0, Y = 0, Z = 0;
  47956. cimg_forX(*this,l) {
  47957. do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#'));
  47958. if ((err = cimg_sscanf(line,"%f%f%f%*[^\n] ",&X,&Y,&Z))!=3) {
  47959. if (!file) cimg::fclose(nfile);
  47960. throw CImgIOException(_cimg_instance
  47961. "load_off(): Failed to read vertex %u/%u in file '%s'.",
  47962. cimg_instance,
  47963. l + 1,nb_points,filename?filename:"(FILE*)");
  47964. }
  47965. (*this)(l,0) = (T)X; (*this)(l,1) = (T)Y; (*this)(l,2) = (T)Z;
  47966. }
  47967. // Read primitive data
  47968. primitives.assign();
  47969. colors.assign();
  47970. bool stop_flag = false;
  47971. while (!stop_flag) {
  47972. float c0 = 0.7f, c1 = 0.7f, c2 = 0.7f;
  47973. unsigned int prim = 0, i0 = 0, i1 = 0, i2 = 0, i3 = 0, i4 = 0, i5 = 0, i6 = 0, i7 = 0;
  47974. *line = 0;
  47975. if ((err = std::fscanf(nfile,"%u",&prim))!=1) stop_flag = true;
  47976. else {
  47977. ++nb_read;
  47978. switch (prim) {
  47979. case 1 : {
  47980. if ((err = std::fscanf(nfile,"%u%255[^\n] ",&i0,line._data))<2) {
  47981. cimg::warn(_cimg_instance
  47982. "load_off(): Failed to read primitive %u/%u from file '%s'.",
  47983. cimg_instance,
  47984. nb_read,nb_primitives,filename?filename:"(FILE*)");
  47985. err = std::fscanf(nfile,"%*[^\n] ");
  47986. } else {
  47987. err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
  47988. CImg<tf>::vector(i0).move_to(primitives);
  47989. CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
  47990. }
  47991. } break;
  47992. case 2 : {
  47993. if ((err = std::fscanf(nfile,"%u%u%255[^\n] ",&i0,&i1,line._data))<2) {
  47994. cimg::warn(_cimg_instance
  47995. "load_off(): Failed to read primitive %u/%u from file '%s'.",
  47996. cimg_instance,
  47997. nb_read,nb_primitives,filename?filename:"(FILE*)");
  47998. err = std::fscanf(nfile,"%*[^\n] ");
  47999. } else {
  48000. err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
  48001. CImg<tf>::vector(i0,i1).move_to(primitives);
  48002. CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
  48003. }
  48004. } break;
  48005. case 3 : {
  48006. if ((err = std::fscanf(nfile,"%u%u%u%255[^\n] ",&i0,&i1,&i2,line._data))<3) {
  48007. cimg::warn(_cimg_instance
  48008. "load_off(): Failed to read primitive %u/%u from file '%s'.",
  48009. cimg_instance,
  48010. nb_read,nb_primitives,filename?filename:"(FILE*)");
  48011. err = std::fscanf(nfile,"%*[^\n] ");
  48012. } else {
  48013. err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
  48014. CImg<tf>::vector(i0,i2,i1).move_to(primitives);
  48015. CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
  48016. }
  48017. } break;
  48018. case 4 : {
  48019. if ((err = std::fscanf(nfile,"%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,line._data))<4) {
  48020. cimg::warn(_cimg_instance
  48021. "load_off(): Failed to read primitive %u/%u from file '%s'.",
  48022. cimg_instance,
  48023. nb_read,nb_primitives,filename?filename:"(FILE*)");
  48024. err = std::fscanf(nfile,"%*[^\n] ");
  48025. } else {
  48026. err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
  48027. CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
  48028. CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
  48029. }
  48030. } break;
  48031. case 5 : {
  48032. if ((err = std::fscanf(nfile,"%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,line._data))<5) {
  48033. cimg::warn(_cimg_instance
  48034. "load_off(): Failed to read primitive %u/%u from file '%s'.",
  48035. cimg_instance,
  48036. nb_read,nb_primitives,filename?filename:"(FILE*)");
  48037. err = std::fscanf(nfile,"%*[^\n] ");
  48038. } else {
  48039. err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
  48040. CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
  48041. CImg<tf>::vector(i0,i4,i3).move_to(primitives);
  48042. colors.insert(2,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
  48043. ++nb_primitives;
  48044. }
  48045. } break;
  48046. case 6 : {
  48047. if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,line._data))<6) {
  48048. cimg::warn(_cimg_instance
  48049. "load_off(): Failed to read primitive %u/%u from file '%s'.",
  48050. cimg_instance,
  48051. nb_read,nb_primitives,filename?filename:"(FILE*)");
  48052. err = std::fscanf(nfile,"%*[^\n] ");
  48053. } else {
  48054. err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
  48055. CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
  48056. CImg<tf>::vector(i0,i5,i4,i3).move_to(primitives);
  48057. colors.insert(2,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
  48058. ++nb_primitives;
  48059. }
  48060. } break;
  48061. case 7 : {
  48062. if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,&i6,line._data))<7) {
  48063. cimg::warn(_cimg_instance
  48064. "load_off(): Failed to read primitive %u/%u from file '%s'.",
  48065. cimg_instance,
  48066. nb_read,nb_primitives,filename?filename:"(FILE*)");
  48067. err = std::fscanf(nfile,"%*[^\n] ");
  48068. } else {
  48069. err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
  48070. CImg<tf>::vector(i0,i4,i3,i1).move_to(primitives);
  48071. CImg<tf>::vector(i0,i6,i5,i4).move_to(primitives);
  48072. CImg<tf>::vector(i3,i2,i1).move_to(primitives);
  48073. colors.insert(3,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
  48074. ++(++nb_primitives);
  48075. }
  48076. } break;
  48077. case 8 : {
  48078. 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) {
  48079. cimg::warn(_cimg_instance
  48080. "load_off(): Failed to read primitive %u/%u from file '%s'.",
  48081. cimg_instance,
  48082. nb_read,nb_primitives,filename?filename:"(FILE*)");
  48083. err = std::fscanf(nfile,"%*[^\n] ");
  48084. } else {
  48085. err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
  48086. CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
  48087. CImg<tf>::vector(i0,i5,i4,i3).move_to(primitives);
  48088. CImg<tf>::vector(i0,i7,i6,i5).move_to(primitives);
  48089. colors.insert(3,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
  48090. ++(++nb_primitives);
  48091. }
  48092. } break;
  48093. default :
  48094. cimg::warn(_cimg_instance
  48095. "load_off(): Failed to read primitive %u/%u (%u vertices) from file '%s'.",
  48096. cimg_instance,
  48097. nb_read,nb_primitives,prim,filename?filename:"(FILE*)");
  48098. err = std::fscanf(nfile,"%*[^\n] ");
  48099. }
  48100. }
  48101. }
  48102. if (!file) cimg::fclose(nfile);
  48103. if (primitives._width!=nb_primitives)
  48104. cimg::warn(_cimg_instance
  48105. "load_off(): Only %u/%u primitives read from file '%s'.",
  48106. cimg_instance,
  48107. primitives._width,nb_primitives,filename?filename:"(FILE*)");
  48108. return *this;
  48109. }
  48110. //! Load image sequence from a video file, using OpenCV library.
  48111. /**
  48112. \param filename Filename, as a C-string.
  48113. \param first_frame Index of the first frame to read.
  48114. \param last_frame Index of the last frame to read.
  48115. \param step_frame Step value for frame reading.
  48116. \param axis Alignment axis.
  48117. \param align Apending alignment.
  48118. **/
  48119. CImg<T>& load_video(const char *const filename,
  48120. const unsigned int first_frame=0, const unsigned int last_frame=~0U,
  48121. const unsigned int step_frame=1,
  48122. const char axis='z', const float align=0) {
  48123. return get_load_video(filename,first_frame,last_frame,step_frame,axis,align).move_to(*this);
  48124. }
  48125. //! Load image sequence from a video file, using OpenCV library \newinstance.
  48126. static CImg<T> get_load_video(const char *const filename,
  48127. const unsigned int first_frame=0, const unsigned int last_frame=~0U,
  48128. const unsigned int step_frame=1,
  48129. const char axis='z', const float align=0) {
  48130. return CImgList<T>().load_video(filename,first_frame,last_frame,step_frame).get_append(axis,align);
  48131. }
  48132. //! Load image sequence using FFMPEG's external tool 'ffmpeg'.
  48133. /**
  48134. \param filename Filename, as a C-string.
  48135. \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
  48136. \param align Appending alignment.
  48137. **/
  48138. CImg<T>& load_ffmpeg_external(const char *const filename, const char axis='z', const float align=0) {
  48139. return get_load_ffmpeg_external(filename,axis,align).move_to(*this);
  48140. }
  48141. //! Load image sequence using FFMPEG's external tool 'ffmpeg' \newinstance.
  48142. static CImg<T> get_load_ffmpeg_external(const char *const filename, const char axis='z', const float align=0) {
  48143. return CImgList<T>().load_ffmpeg_external(filename).get_append(axis,align);
  48144. }
  48145. //! Load gif file, using Imagemagick or GraphicsMagicks's external tools.
  48146. /**
  48147. \param filename Filename, as a C-string.
  48148. \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
  48149. \param align Appending alignment.
  48150. **/
  48151. CImg<T>& load_gif_external(const char *const filename,
  48152. const char axis='z', const float align=0) {
  48153. return get_load_gif_external(filename,axis,align).move_to(*this);
  48154. }
  48155. //! Load gif file, using ImageMagick or GraphicsMagick's external tool 'convert' \newinstance.
  48156. static CImg<T> get_load_gif_external(const char *const filename,
  48157. const char axis='z', const float align=0) {
  48158. return CImgList<T>().load_gif_external(filename).get_append(axis,align);
  48159. }
  48160. //! Load image using GraphicsMagick's external tool 'gm'.
  48161. /**
  48162. \param filename Filename, as a C-string.
  48163. **/
  48164. CImg<T>& load_graphicsmagick_external(const char *const filename) {
  48165. if (!filename)
  48166. throw CImgArgumentException(_cimg_instance
  48167. "load_graphicsmagick_external(): Specified filename is (null).",
  48168. cimg_instance);
  48169. std::fclose(cimg::fopen(filename,"rb")); // Check if file exists.
  48170. CImg<charT> command(1024), filename_tmp(256);
  48171. std::FILE *file = 0;
  48172. const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape();
  48173. #if cimg_OS==1
  48174. if (!cimg::system("which gm")) {
  48175. cimg_snprintf(command,command._width,"%s convert \"%s\" pnm:-",
  48176. cimg::graphicsmagick_path(),s_filename.data());
  48177. file = popen(command,"r");
  48178. if (file) {
  48179. const unsigned int omode = cimg::exception_mode();
  48180. cimg::exception_mode(0);
  48181. try { load_pnm(file); } catch (...) {
  48182. pclose(file);
  48183. cimg::exception_mode(omode);
  48184. throw CImgIOException(_cimg_instance
  48185. "load_graphicsmagick_external(): Failed to load file '%s' "
  48186. "with external command 'gm'.",
  48187. cimg_instance,
  48188. filename);
  48189. }
  48190. pclose(file);
  48191. return *this;
  48192. }
  48193. }
  48194. #endif
  48195. do {
  48196. cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.pnm",
  48197. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
  48198. if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
  48199. } while (file);
  48200. cimg_snprintf(command,command._width,"%s convert \"%s\" \"%s\"",
  48201. cimg::graphicsmagick_path(),s_filename.data(),
  48202. CImg<charT>::string(filename_tmp)._system_strescape().data());
  48203. cimg::system(command,cimg::graphicsmagick_path());
  48204. if (!(file = std_fopen(filename_tmp,"rb"))) {
  48205. cimg::fclose(cimg::fopen(filename,"r"));
  48206. throw CImgIOException(_cimg_instance
  48207. "load_graphicsmagick_external(): Failed to load file '%s' with external command 'gm'.",
  48208. cimg_instance,
  48209. filename);
  48210. } else cimg::fclose(file);
  48211. load_pnm(filename_tmp);
  48212. std::remove(filename_tmp);
  48213. return *this;
  48214. }
  48215. //! Load image using GraphicsMagick's external tool 'gm' \newinstance.
  48216. static CImg<T> get_load_graphicsmagick_external(const char *const filename) {
  48217. return CImg<T>().load_graphicsmagick_external(filename);
  48218. }
  48219. //! Load gzipped image file, using external tool 'gunzip'.
  48220. /**
  48221. \param filename Filename, as a C-string.
  48222. **/
  48223. CImg<T>& load_gzip_external(const char *const filename) {
  48224. if (!filename)
  48225. throw CImgIOException(_cimg_instance
  48226. "load_gzip_external(): Specified filename is (null).",
  48227. cimg_instance);
  48228. std::fclose(cimg::fopen(filename,"rb")); // Check if file exists.
  48229. CImg<charT> command(1024), filename_tmp(256), body(256);
  48230. const char
  48231. *const ext = cimg::split_filename(filename,body),
  48232. *const ext2 = cimg::split_filename(body,0);
  48233. std::FILE *file = 0;
  48234. do {
  48235. if (!cimg::strcasecmp(ext,"gz")) {
  48236. if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
  48237. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
  48238. else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
  48239. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
  48240. } else {
  48241. if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
  48242. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
  48243. else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
  48244. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
  48245. }
  48246. if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
  48247. } while (file);
  48248. cimg_snprintf(command,command._width,"%s -c \"%s\" > \"%s\"",
  48249. cimg::gunzip_path(),
  48250. CImg<charT>::string(filename)._system_strescape().data(),
  48251. CImg<charT>::string(filename_tmp)._system_strescape().data());
  48252. cimg::system(command);
  48253. if (!(file = std_fopen(filename_tmp,"rb"))) {
  48254. cimg::fclose(cimg::fopen(filename,"r"));
  48255. throw CImgIOException(_cimg_instance
  48256. "load_gzip_external(): Failed to load file '%s' with external command 'gunzip'.",
  48257. cimg_instance,
  48258. filename);
  48259. } else cimg::fclose(file);
  48260. load(filename_tmp);
  48261. std::remove(filename_tmp);
  48262. return *this;
  48263. }
  48264. //! Load gzipped image file, using external tool 'gunzip' \newinstance.
  48265. static CImg<T> get_load_gzip_external(const char *const filename) {
  48266. return CImg<T>().load_gzip_external(filename);
  48267. }
  48268. //! Load image using ImageMagick's external tool 'convert'.
  48269. /**
  48270. \param filename Filename, as a C-string.
  48271. **/
  48272. CImg<T>& load_imagemagick_external(const char *const filename) {
  48273. if (!filename)
  48274. throw CImgArgumentException(_cimg_instance
  48275. "load_imagemagick_external(): Specified filename is (null).",
  48276. cimg_instance);
  48277. std::fclose(cimg::fopen(filename,"rb")); // Check if file exists.
  48278. CImg<charT> command(1024), filename_tmp(256);
  48279. std::FILE *file = 0;
  48280. const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape();
  48281. #if cimg_OS==1
  48282. if (!cimg::system("which convert")) {
  48283. cimg_snprintf(command,command._width,"%s%s \"%s\" pnm:-",
  48284. cimg::imagemagick_path(),
  48285. !cimg::strcasecmp(cimg::split_filename(filename),"pdf")?" -density 400x400":"",
  48286. s_filename.data());
  48287. file = popen(command,"r");
  48288. if (file) {
  48289. const unsigned int omode = cimg::exception_mode();
  48290. cimg::exception_mode(0);
  48291. try { load_pnm(file); } catch (...) {
  48292. pclose(file);
  48293. cimg::exception_mode(omode);
  48294. throw CImgIOException(_cimg_instance
  48295. "load_imagemagick_external(): Failed to load file '%s' with "
  48296. "external command 'magick/convert'.",
  48297. cimg_instance,
  48298. filename);
  48299. }
  48300. pclose(file);
  48301. return *this;
  48302. }
  48303. }
  48304. #endif
  48305. do {
  48306. cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.pnm",
  48307. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
  48308. if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
  48309. } while (file);
  48310. cimg_snprintf(command,command._width,"%s%s \"%s\" \"%s\"",
  48311. cimg::imagemagick_path(),
  48312. !cimg::strcasecmp(cimg::split_filename(filename),"pdf")?" -density 400x400":"",
  48313. s_filename.data(),CImg<charT>::string(filename_tmp)._system_strescape().data());
  48314. cimg::system(command,cimg::imagemagick_path());
  48315. if (!(file = std_fopen(filename_tmp,"rb"))) {
  48316. cimg::fclose(cimg::fopen(filename,"r"));
  48317. throw CImgIOException(_cimg_instance
  48318. "load_imagemagick_external(): Failed to load file '%s' with "
  48319. "external command 'magick/convert'.",
  48320. cimg_instance,
  48321. filename);
  48322. } else cimg::fclose(file);
  48323. load_pnm(filename_tmp);
  48324. std::remove(filename_tmp);
  48325. return *this;
  48326. }
  48327. //! Load image using ImageMagick's external tool 'convert' \newinstance.
  48328. static CImg<T> get_load_imagemagick_external(const char *const filename) {
  48329. return CImg<T>().load_imagemagick_external(filename);
  48330. }
  48331. //! Load image from a DICOM file, using XMedcon's external tool 'medcon'.
  48332. /**
  48333. \param filename Filename, as a C-string.
  48334. **/
  48335. CImg<T>& load_medcon_external(const char *const filename) {
  48336. if (!filename)
  48337. throw CImgArgumentException(_cimg_instance
  48338. "load_medcon_external(): Specified filename is (null).",
  48339. cimg_instance);
  48340. std::fclose(cimg::fopen(filename,"rb")); // Check if file exists.
  48341. CImg<charT> command(1024), filename_tmp(256), body(256);
  48342. cimg::fclose(cimg::fopen(filename,"r"));
  48343. std::FILE *file = 0;
  48344. do {
  48345. cimg_snprintf(filename_tmp,filename_tmp._width,"%s.hdr",cimg::filenamerand());
  48346. if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
  48347. } while (file);
  48348. cimg_snprintf(command,command._width,"%s -w -c anlz -o \"%s\" -f \"%s\"",
  48349. cimg::medcon_path(),
  48350. CImg<charT>::string(filename_tmp)._system_strescape().data(),
  48351. CImg<charT>::string(filename)._system_strescape().data());
  48352. cimg::system(command);
  48353. cimg::split_filename(filename_tmp,body);
  48354. cimg_snprintf(command,command._width,"%s.hdr",body._data);
  48355. file = std_fopen(command,"rb");
  48356. if (!file) {
  48357. cimg_snprintf(command,command._width,"m000-%s.hdr",body._data);
  48358. file = std_fopen(command,"rb");
  48359. if (!file) {
  48360. throw CImgIOException(_cimg_instance
  48361. "load_medcon_external(): Failed to load file '%s' with external command 'medcon'.",
  48362. cimg_instance,
  48363. filename);
  48364. }
  48365. }
  48366. cimg::fclose(file);
  48367. load_analyze(command);
  48368. std::remove(command);
  48369. cimg::split_filename(command,body);
  48370. cimg_snprintf(command,command._width,"%s.img",body._data);
  48371. std::remove(command);
  48372. return *this;
  48373. }
  48374. //! Load image from a DICOM file, using XMedcon's external tool 'medcon' \newinstance.
  48375. static CImg<T> get_load_medcon_external(const char *const filename) {
  48376. return CImg<T>().load_medcon_external(filename);
  48377. }
  48378. //! Load image from a RAW Color Camera file, using external tool 'dcraw'.
  48379. /**
  48380. \param filename Filename, as a C-string.
  48381. **/
  48382. CImg<T>& load_dcraw_external(const char *const filename) {
  48383. if (!filename)
  48384. throw CImgArgumentException(_cimg_instance
  48385. "load_dcraw_external(): Specified filename is (null).",
  48386. cimg_instance);
  48387. std::fclose(cimg::fopen(filename,"rb")); // Check if file exists.
  48388. CImg<charT> command(1024), filename_tmp(256);
  48389. std::FILE *file = 0;
  48390. const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape();
  48391. #if cimg_OS==1
  48392. cimg_snprintf(command,command._width,"%s -w -4 -c \"%s\"",
  48393. cimg::dcraw_path(),s_filename.data());
  48394. file = popen(command,"r");
  48395. if (file) {
  48396. const unsigned int omode = cimg::exception_mode();
  48397. cimg::exception_mode(0);
  48398. try { load_pnm(file); } catch (...) {
  48399. pclose(file);
  48400. cimg::exception_mode(omode);
  48401. throw CImgIOException(_cimg_instance
  48402. "load_dcraw_external(): Failed to load file '%s' with external command 'dcraw'.",
  48403. cimg_instance,
  48404. filename);
  48405. }
  48406. pclose(file);
  48407. return *this;
  48408. }
  48409. #endif
  48410. do {
  48411. cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.ppm",
  48412. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
  48413. if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
  48414. } while (file);
  48415. cimg_snprintf(command,command._width,"%s -w -4 -c \"%s\" > \"%s\"",
  48416. cimg::dcraw_path(),s_filename.data(),CImg<charT>::string(filename_tmp)._system_strescape().data());
  48417. cimg::system(command,cimg::dcraw_path());
  48418. if (!(file = std_fopen(filename_tmp,"rb"))) {
  48419. cimg::fclose(cimg::fopen(filename,"r"));
  48420. throw CImgIOException(_cimg_instance
  48421. "load_dcraw_external(): Failed to load file '%s' with external command 'dcraw'.",
  48422. cimg_instance,
  48423. filename);
  48424. } else cimg::fclose(file);
  48425. load_pnm(filename_tmp);
  48426. std::remove(filename_tmp);
  48427. return *this;
  48428. }
  48429. //! Load image from a RAW Color Camera file, using external tool 'dcraw' \newinstance.
  48430. static CImg<T> get_load_dcraw_external(const char *const filename) {
  48431. return CImg<T>().load_dcraw_external(filename);
  48432. }
  48433. //! Load image from a camera stream, using OpenCV.
  48434. /**
  48435. \param camera_index Index of the camera to capture images from.
  48436. \param skip_frames Number of frames to skip before the capture.
  48437. \param release_camera Tells if the camera ressource must be released at the end of the method.
  48438. \param capture_width Width of the desired image.
  48439. \param capture_height Height of the desired image.
  48440. **/
  48441. CImg<T>& load_camera(const unsigned int camera_index=0, const unsigned int skip_frames=0,
  48442. const bool release_camera=true, const unsigned int capture_width=0,
  48443. const unsigned int capture_height=0) {
  48444. #ifdef cimg_use_opencv
  48445. if (camera_index>99)
  48446. throw CImgArgumentException(_cimg_instance
  48447. "load_camera(): Invalid request for camera #%u "
  48448. "(no more than 100 cameras can be managed simultaneously).",
  48449. cimg_instance,
  48450. camera_index);
  48451. static CvCapture *capture[100] = { 0 };
  48452. static unsigned int capture_w[100], capture_h[100];
  48453. if (release_camera) {
  48454. cimg::mutex(9);
  48455. if (capture[camera_index]) cvReleaseCapture(&(capture[camera_index]));
  48456. capture[camera_index] = 0;
  48457. capture_w[camera_index] = capture_h[camera_index] = 0;
  48458. cimg::mutex(9,0);
  48459. return *this;
  48460. }
  48461. if (!capture[camera_index]) {
  48462. cimg::mutex(9);
  48463. capture[camera_index] = cvCreateCameraCapture(camera_index);
  48464. capture_w[camera_index] = 0;
  48465. capture_h[camera_index] = 0;
  48466. cimg::mutex(9,0);
  48467. if (!capture[camera_index]) {
  48468. throw CImgIOException(_cimg_instance
  48469. "load_camera(): Failed to initialize camera #%u.",
  48470. cimg_instance,
  48471. camera_index);
  48472. }
  48473. }
  48474. cimg::mutex(9);
  48475. if (capture_width!=capture_w[camera_index]) {
  48476. cvSetCaptureProperty(capture[camera_index],CV_CAP_PROP_FRAME_WIDTH,capture_width);
  48477. capture_w[camera_index] = capture_width;
  48478. }
  48479. if (capture_height!=capture_h[camera_index]) {
  48480. cvSetCaptureProperty(capture[camera_index],CV_CAP_PROP_FRAME_HEIGHT,capture_height);
  48481. capture_h[camera_index] = capture_height;
  48482. }
  48483. const IplImage *img = 0;
  48484. for (unsigned int i = 0; i<skip_frames; ++i) img = cvQueryFrame(capture[camera_index]);
  48485. img = cvQueryFrame(capture[camera_index]);
  48486. if (img) {
  48487. const int step = (int)(img->widthStep - 3*img->width);
  48488. assign(img->width,img->height,1,3);
  48489. const unsigned char* ptrs = (unsigned char*)img->imageData;
  48490. T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
  48491. if (step>0) cimg_forY(*this,y) {
  48492. cimg_forX(*this,x) { *(ptr_b++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_r++) = (T)*(ptrs++); }
  48493. ptrs+=step;
  48494. } else for (ulongT siz = (ulongT)img->width*img->height; siz; --siz) {
  48495. *(ptr_b++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_r++) = (T)*(ptrs++);
  48496. }
  48497. }
  48498. cimg::mutex(9,0);
  48499. return *this;
  48500. #else
  48501. cimg::unused(camera_index,skip_frames,release_camera,capture_width,capture_height);
  48502. throw CImgIOException(_cimg_instance
  48503. "load_camera(): This function requires the OpenCV library to run "
  48504. "(macro 'cimg_use_opencv' must be defined).",
  48505. cimg_instance);
  48506. #endif
  48507. }
  48508. //! Load image from a camera stream, using OpenCV \newinstance.
  48509. static CImg<T> get_load_camera(const unsigned int camera_index=0, const unsigned int skip_frames=0,
  48510. const bool release_camera=true,
  48511. const unsigned int capture_width=0, const unsigned int capture_height=0) {
  48512. return CImg<T>().load_camera(camera_index,skip_frames,release_camera,capture_width,capture_height);
  48513. }
  48514. //! Load image using various non-native ways.
  48515. /**
  48516. \param filename Filename, as a C-string.
  48517. **/
  48518. CImg<T>& load_other(const char *const filename) {
  48519. if (!filename)
  48520. throw CImgArgumentException(_cimg_instance
  48521. "load_other(): Specified filename is (null).",
  48522. cimg_instance);
  48523. const unsigned int omode = cimg::exception_mode();
  48524. cimg::exception_mode(0);
  48525. try { load_magick(filename); }
  48526. catch (CImgException&) {
  48527. try { load_imagemagick_external(filename); }
  48528. catch (CImgException&) {
  48529. try { load_graphicsmagick_external(filename); }
  48530. catch (CImgException&) {
  48531. try { load_cimg(filename); }
  48532. catch (CImgException&) {
  48533. try {
  48534. std::fclose(cimg::fopen(filename,"rb"));
  48535. } catch (CImgException&) {
  48536. cimg::exception_mode(omode);
  48537. throw CImgIOException(_cimg_instance
  48538. "load_other(): Failed to open file '%s'.",
  48539. cimg_instance,
  48540. filename);
  48541. }
  48542. cimg::exception_mode(omode);
  48543. throw CImgIOException(_cimg_instance
  48544. "load_other(): Failed to recognize format of file '%s'.",
  48545. cimg_instance,
  48546. filename);
  48547. }
  48548. }
  48549. }
  48550. }
  48551. cimg::exception_mode(omode);
  48552. return *this;
  48553. }
  48554. //! Load image using various non-native ways \newinstance.
  48555. static CImg<T> get_load_other(const char *const filename) {
  48556. return CImg<T>().load_other(filename);
  48557. }
  48558. //@}
  48559. //---------------------------
  48560. //
  48561. //! \name Data Output
  48562. //@{
  48563. //---------------------------
  48564. //! Display information about the image data.
  48565. /**
  48566. \param title Name for the considered image.
  48567. \param display_stats Tells to compute and display image statistics.
  48568. **/
  48569. const CImg<T>& print(const char *const title=0, const bool display_stats=true) const {
  48570. int xm = 0, ym = 0, zm = 0, vm = 0, xM = 0, yM = 0, zM = 0, vM = 0;
  48571. CImg<doubleT> st;
  48572. if (!is_empty() && display_stats) {
  48573. st = get_stats();
  48574. xm = (int)st[4]; ym = (int)st[5], zm = (int)st[6], vm = (int)st[7];
  48575. xM = (int)st[8]; yM = (int)st[9], zM = (int)st[10], vM = (int)st[11];
  48576. }
  48577. const ulongT siz = size(), msiz = siz*sizeof(T), siz1 = siz - 1,
  48578. mdisp = msiz<8*1024?0U:msiz<8*1024*1024?1U:2U, width1 = _width - 1;
  48579. CImg<charT> _title(64);
  48580. if (!title) cimg_snprintf(_title,_title._width,"CImg<%s>",pixel_type());
  48581. std::fprintf(cimg::output(),"%s%s%s%s: %sthis%s = %p, %ssize%s = (%u,%u,%u,%u) [%lu %s], %sdata%s = (%s*)%p",
  48582. cimg::t_magenta,cimg::t_bold,title?title:_title._data,cimg::t_normal,
  48583. cimg::t_bold,cimg::t_normal,(void*)this,
  48584. cimg::t_bold,cimg::t_normal,_width,_height,_depth,_spectrum,
  48585. (unsigned long)(mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20))),
  48586. mdisp==0?"b":(mdisp==1?"Kio":"Mio"),
  48587. cimg::t_bold,cimg::t_normal,pixel_type(),(void*)begin());
  48588. if (_data)
  48589. std::fprintf(cimg::output(),"..%p (%s) = [ ",(void*)((char*)end() - 1),_is_shared?"shared":"non-shared");
  48590. else std::fprintf(cimg::output()," (%s) = [ ",_is_shared?"shared":"non-shared");
  48591. if (!is_empty()) cimg_foroff(*this,off) {
  48592. std::fprintf(cimg::output(),"%g",(double)_data[off]);
  48593. if (off!=siz1) std::fprintf(cimg::output(),"%s",off%_width==width1?" ; ":" ");
  48594. if (off==7 && siz>16) { off = siz1 - 8; std::fprintf(cimg::output(),"... "); }
  48595. }
  48596. if (!is_empty() && display_stats)
  48597. std::fprintf(cimg::output(),
  48598. " ], %smin%s = %g, %smax%s = %g, %smean%s = %g, %sstd%s = %g, %scoords_min%s = (%u,%u,%u,%u), "
  48599. "%scoords_max%s = (%u,%u,%u,%u).\n",
  48600. cimg::t_bold,cimg::t_normal,st[0],
  48601. cimg::t_bold,cimg::t_normal,st[1],
  48602. cimg::t_bold,cimg::t_normal,st[2],
  48603. cimg::t_bold,cimg::t_normal,std::sqrt(st[3]),
  48604. cimg::t_bold,cimg::t_normal,xm,ym,zm,vm,
  48605. cimg::t_bold,cimg::t_normal,xM,yM,zM,vM);
  48606. else std::fprintf(cimg::output(),"%s].\n",is_empty()?"":" ");
  48607. std::fflush(cimg::output());
  48608. return *this;
  48609. }
  48610. //! Display image into a CImgDisplay window.
  48611. /**
  48612. \param disp Display window.
  48613. **/
  48614. const CImg<T>& display(CImgDisplay& disp) const {
  48615. disp.display(*this);
  48616. return *this;
  48617. }
  48618. //! Display image into a CImgDisplay window, in an interactive way.
  48619. /**
  48620. \param disp Display window.
  48621. \param display_info Tells if image information are displayed on the standard output.
  48622. \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function.
  48623. \param exit_on_anykey Exit function when any key is pressed.
  48624. **/
  48625. const CImg<T>& display(CImgDisplay &disp, const bool display_info, unsigned int *const XYZ=0,
  48626. const bool exit_on_anykey=false) const {
  48627. return _display(disp,0,display_info,XYZ,exit_on_anykey,false);
  48628. }
  48629. //! Display image into an interactive window.
  48630. /**
  48631. \param title Window title
  48632. \param display_info Tells if image information are displayed on the standard output.
  48633. \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function.
  48634. \param exit_on_anykey Exit function when any key is pressed.
  48635. **/
  48636. const CImg<T>& display(const char *const title=0, const bool display_info=true, unsigned int *const XYZ=0,
  48637. const bool exit_on_anykey=false) const {
  48638. CImgDisplay disp;
  48639. return _display(disp,title,display_info,XYZ,exit_on_anykey,false);
  48640. }
  48641. const CImg<T>& _display(CImgDisplay &disp, const char *const title, const bool display_info,
  48642. unsigned int *const XYZ, const bool exit_on_anykey,
  48643. const bool exit_on_simpleclick) const {
  48644. unsigned int oldw = 0, oldh = 0, _XYZ[3] = { 0 }, key = 0;
  48645. int x0 = 0, y0 = 0, z0 = 0, x1 = width() - 1, y1 = height() - 1, z1 = depth() - 1,
  48646. old_mouse_x = -1, old_mouse_y = -1;
  48647. if (!disp) {
  48648. disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1);
  48649. if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum);
  48650. else disp.set_title("%s",title);
  48651. } else if (title) disp.set_title("%s",title);
  48652. disp.show().flush();
  48653. const CImg<char> dtitle = CImg<char>::string(disp.title());
  48654. if (display_info) print(dtitle);
  48655. CImg<T> zoom;
  48656. for (bool reset_view = true, resize_disp = false, is_first_select = true; !key && !disp.is_closed(); ) {
  48657. if (reset_view) {
  48658. if (XYZ) { _XYZ[0] = XYZ[0]; _XYZ[1] = XYZ[1]; _XYZ[2] = XYZ[2]; }
  48659. else {
  48660. _XYZ[0] = (unsigned int)(x0 + x1)/2;
  48661. _XYZ[1] = (unsigned int)(y0 + y1)/2;
  48662. _XYZ[2] = (unsigned int)(z0 + z1)/2;
  48663. }
  48664. x0 = 0; y0 = 0; z0 = 0; x1 = width() - 1; y1 = height() - 1; z1 = depth() - 1;
  48665. oldw = disp._width; oldh = disp._height;
  48666. reset_view = false;
  48667. }
  48668. if (!x0 && !y0 && !z0 && x1==width() - 1 && y1==height() - 1 && z1==depth() - 1) {
  48669. if (is_empty()) zoom.assign(1,1,1,1,(T)0); else zoom.assign();
  48670. } else zoom = get_crop(x0,y0,z0,x1,y1,z1);
  48671. const CImg<T>& visu = zoom?zoom:*this;
  48672. const unsigned int
  48673. dx = 1U + x1 - x0, dy = 1U + y1 - y0, dz = 1U + z1 - z0,
  48674. tw = dx + (dz>1?dz:0U), th = dy + (dz>1?dz:0U);
  48675. if (!is_empty() && !disp.is_fullscreen() && resize_disp) {
  48676. const unsigned int
  48677. ttw = tw*disp.width()/oldw, tth = th*disp.height()/oldh,
  48678. dM = std::max(ttw,tth), diM = (unsigned int)std::max(disp.width(),disp.height()),
  48679. imgw = std::max(16U,ttw*diM/dM), imgh = std::max(16U,tth*diM/dM);
  48680. disp.set_fullscreen(false).resize(cimg_fitscreen(imgw,imgh,1),false);
  48681. resize_disp = false;
  48682. }
  48683. oldw = tw; oldh = th;
  48684. bool
  48685. go_up = false, go_down = false, go_left = false, go_right = false,
  48686. go_inc = false, go_dec = false, go_in = false, go_out = false,
  48687. go_in_center = false;
  48688. disp.set_title("%s",dtitle._data);
  48689. if (_width>1 && visu._width==1) disp.set_title("%s | x=%u",disp._title,x0);
  48690. if (_height>1 && visu._height==1) disp.set_title("%s | y=%u",disp._title,y0);
  48691. if (_depth>1 && visu._depth==1) disp.set_title("%s | z=%u",disp._title,z0);
  48692. disp._mouse_x = old_mouse_x; disp._mouse_y = old_mouse_y;
  48693. CImg<intT> selection = visu._select(disp,0,2,_XYZ,x0,y0,z0,true,is_first_select,_depth>1,true);
  48694. old_mouse_x = disp._mouse_x; old_mouse_y = disp._mouse_y;
  48695. is_first_select = false;
  48696. if (disp.wheel()) {
  48697. if ((disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) &&
  48698. (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT())) {
  48699. go_left = !(go_right = disp.wheel()>0);
  48700. } else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) {
  48701. go_down = !(go_up = disp.wheel()>0);
  48702. } else if (depth()==1 || disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  48703. go_out = !(go_in = disp.wheel()>0); go_in_center = false;
  48704. }
  48705. disp.set_wheel();
  48706. }
  48707. const int
  48708. sx0 = selection(0), sy0 = selection(1), sz0 = selection(2),
  48709. sx1 = selection(3), sy1 = selection(4), sz1 = selection(5);
  48710. if (sx0>=0 && sy0>=0 && sz0>=0 && sx1>=0 && sy1>=0 && sz1>=0) {
  48711. x1 = x0 + sx1; y1 = y0 + sy1; z1 = z0 + sz1;
  48712. x0+=sx0; y0+=sy0; z0+=sz0;
  48713. if ((sx0==sx1 && sy0==sy1) || (_depth>1 && sx0==sx1 && sz0==sz1) || (_depth>1 && sy0==sy1 && sz0==sz1)) {
  48714. if (exit_on_simpleclick && (!zoom || is_empty())) break; else reset_view = true;
  48715. }
  48716. resize_disp = true;
  48717. } else switch (key = disp.key()) {
  48718. #if cimg_OS!=2
  48719. case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT :
  48720. #endif
  48721. case 0 : case cimg::keyCTRLLEFT : case cimg::keySHIFTLEFT : key = 0; break;
  48722. case cimg::keyP : if (visu._depth>1 && (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT())) {
  48723. // Special mode: play stack of frames
  48724. const unsigned int
  48725. w1 = visu._width*disp.width()/(visu._width + (visu._depth>1?visu._depth:0)),
  48726. h1 = visu._height*disp.height()/(visu._height + (visu._depth>1?visu._depth:0));
  48727. float frame_timing = 5;
  48728. bool is_stopped = false;
  48729. disp.set_key(key,false).set_wheel().resize(cimg_fitscreen(w1,h1,1),false); key = 0;
  48730. for (unsigned int timer = 0; !key && !disp.is_closed() && !disp.button(); ) {
  48731. if (disp.is_resized()) disp.resize(false);
  48732. if (!timer) {
  48733. visu.get_slice((int)_XYZ[2]).display(disp.set_title("%s | z=%d",dtitle.data(),_XYZ[2]));
  48734. (++_XYZ[2])%=visu._depth;
  48735. }
  48736. if (!is_stopped) { if (++timer>(unsigned int)frame_timing) timer = 0; } else timer = ~0U;
  48737. if (disp.wheel()) { frame_timing-=disp.wheel()/3.0f; disp.set_wheel(); }
  48738. switch (key = disp.key()) {
  48739. #if cimg_OS!=2
  48740. case cimg::keyCTRLRIGHT :
  48741. #endif
  48742. case cimg::keyCTRLLEFT : key = 0; break;
  48743. case cimg::keyPAGEUP : frame_timing-=0.3f; key = 0; break;
  48744. case cimg::keyPAGEDOWN : frame_timing+=0.3f; key = 0; break;
  48745. case cimg::keySPACE : is_stopped = !is_stopped; disp.set_key(key,false); key = 0; break;
  48746. case cimg::keyARROWLEFT : case cimg::keyARROWUP : is_stopped = true; timer = 0; key = 0; break;
  48747. case cimg::keyARROWRIGHT : case cimg::keyARROWDOWN : is_stopped = true;
  48748. (_XYZ[2]+=visu._depth - 2)%=visu._depth; timer = 0; key = 0; break;
  48749. case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  48750. disp.set_fullscreen(false).
  48751. resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
  48752. CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false);
  48753. disp.set_key(key,false); key = 0;
  48754. } break;
  48755. case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  48756. disp.set_fullscreen(false).
  48757. resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false).set_key(key,false); key = 0;
  48758. } break;
  48759. case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  48760. disp.set_fullscreen(false).
  48761. resize(cimg_fitscreen(_width,_height,_depth),false).set_key(key,false); key = 0;
  48762. } break;
  48763. case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  48764. disp.resize(disp.screen_width(),disp.screen_height(),false).
  48765. toggle_fullscreen().set_key(key,false); key = 0;
  48766. } break;
  48767. }
  48768. frame_timing = frame_timing<1?1:(frame_timing>39?39:frame_timing);
  48769. disp.wait(20);
  48770. }
  48771. const unsigned int
  48772. w2 = (visu._width + (visu._depth>1?visu._depth:0))*disp.width()/visu._width,
  48773. h2 = (visu._height + (visu._depth>1?visu._depth:0))*disp.height()/visu._height;
  48774. disp.resize(cimg_fitscreen(w2,h2,1),false).set_title(dtitle.data()).set_key().set_button().set_wheel();
  48775. key = 0;
  48776. } break;
  48777. case cimg::keyHOME : reset_view = resize_disp = true; key = 0; break;
  48778. case cimg::keyPADADD : go_in = true; go_in_center = true; key = 0; break;
  48779. case cimg::keyPADSUB : go_out = true; key = 0; break;
  48780. case cimg::keyARROWLEFT : case cimg::keyPAD4: go_left = true; key = 0; break;
  48781. case cimg::keyARROWRIGHT : case cimg::keyPAD6: go_right = true; key = 0; break;
  48782. case cimg::keyARROWUP : case cimg::keyPAD8: go_up = true; key = 0; break;
  48783. case cimg::keyARROWDOWN : case cimg::keyPAD2: go_down = true; key = 0; break;
  48784. case cimg::keyPAD7 : go_up = go_left = true; key = 0; break;
  48785. case cimg::keyPAD9 : go_up = go_right = true; key = 0; break;
  48786. case cimg::keyPAD1 : go_down = go_left = true; key = 0; break;
  48787. case cimg::keyPAD3 : go_down = go_right = true; key = 0; break;
  48788. case cimg::keyPAGEUP : go_inc = true; key = 0; break;
  48789. case cimg::keyPAGEDOWN : go_dec = true; key = 0; break;
  48790. }
  48791. if (go_in) {
  48792. const int
  48793. mx = go_in_center?disp.width()/2:disp.mouse_x(),
  48794. my = go_in_center?disp.height()/2:disp.mouse_y(),
  48795. mX = mx*(width() + (depth()>1?depth():0))/disp.width(),
  48796. mY = my*(height() + (depth()>1?depth():0))/disp.height();
  48797. int X = (int)_XYZ[0], Y = (int)_XYZ[1], Z = (int)_XYZ[2];
  48798. if (mX<width() && mY<height()) {
  48799. X = x0 + mX*(1 + x1 - x0)/width(); Y = y0 + mY*(1 + y1 - y0)/height();
  48800. }
  48801. if (mX<width() && mY>=height()) {
  48802. X = x0 + mX*(1 + x1 - x0)/width(); Z = z0 + (mY - height())*(1 + z1 - z0)/depth();
  48803. }
  48804. if (mX>=width() && mY<height()) {
  48805. Y = y0 + mY*(1 + y1 - y0)/height(); Z = z0 + (mX - width())*(1 + z1 - z0)/depth();
  48806. }
  48807. if (x1 - x0>4) { x0 = X - 3*(X - x0)/4; x1 = X + 3*(x1 - X)/4; }
  48808. if (y1 - y0>4) { y0 = Y - 3*(Y - y0)/4; y1 = Y + 3*(y1 - Y)/4; }
  48809. if (z1 - z0>4) { z0 = Z - 3*(Z - z0)/4; z1 = Z + 3*(z1 - Z)/4; }
  48810. }
  48811. if (go_out) {
  48812. const int
  48813. delta_x = (x1 - x0)/8, delta_y = (y1 - y0)/8, delta_z = (z1 - z0)/8,
  48814. ndelta_x = delta_x?delta_x:(_width>1),
  48815. ndelta_y = delta_y?delta_y:(_height>1),
  48816. ndelta_z = delta_z?delta_z:(_depth>1);
  48817. x0-=ndelta_x; y0-=ndelta_y; z0-=ndelta_z;
  48818. x1+=ndelta_x; y1+=ndelta_y; z1+=ndelta_z;
  48819. if (x0<0) { x1-=x0; x0 = 0; if (x1>=width()) x1 = width() - 1; }
  48820. if (y0<0) { y1-=y0; y0 = 0; if (y1>=height()) y1 = height() - 1; }
  48821. if (z0<0) { z1-=z0; z0 = 0; if (z1>=depth()) z1 = depth() - 1; }
  48822. if (x1>=width()) { x0-=(x1 - width() + 1); x1 = width() - 1; if (x0<0) x0 = 0; }
  48823. if (y1>=height()) { y0-=(y1 - height() + 1); y1 = height() - 1; if (y0<0) y0 = 0; }
  48824. if (z1>=depth()) { z0-=(z1 - depth() + 1); z1 = depth() - 1; if (z0<0) z0 = 0; }
  48825. const float
  48826. ratio = (float)(x1-x0)/(y1-y0),
  48827. ratiow = (float)disp._width/disp._height,
  48828. sub = std::min(cimg::abs(ratio - ratiow),cimg::abs(1/ratio-1/ratiow));
  48829. if (sub>0.01) resize_disp = true;
  48830. }
  48831. if (go_left) {
  48832. const int delta = (x1 - x0)/4, ndelta = delta?delta:(_width>1);
  48833. if (x0 - ndelta>=0) { x0-=ndelta; x1-=ndelta; }
  48834. else { x1-=x0; x0 = 0; }
  48835. }
  48836. if (go_right) {
  48837. const int delta = (x1 - x0)/4, ndelta = delta?delta:(_width>1);
  48838. if (x1+ndelta<width()) { x0+=ndelta; x1+=ndelta; }
  48839. else { x0+=(width() - 1 - x1); x1 = width() - 1; }
  48840. }
  48841. if (go_up) {
  48842. const int delta = (y1 - y0)/4, ndelta = delta?delta:(_height>1);
  48843. if (y0 - ndelta>=0) { y0-=ndelta; y1-=ndelta; }
  48844. else { y1-=y0; y0 = 0; }
  48845. }
  48846. if (go_down) {
  48847. const int delta = (y1 - y0)/4, ndelta = delta?delta:(_height>1);
  48848. if (y1+ndelta<height()) { y0+=ndelta; y1+=ndelta; }
  48849. else { y0+=(height() - 1 - y1); y1 = height() - 1; }
  48850. }
  48851. if (go_inc) {
  48852. const int delta = (z1 - z0)/4, ndelta = delta?delta:(_depth>1);
  48853. if (z0 - ndelta>=0) { z0-=ndelta; z1-=ndelta; }
  48854. else { z1-=z0; z0 = 0; }
  48855. }
  48856. if (go_dec) {
  48857. const int delta = (z1 - z0)/4, ndelta = delta?delta:(_depth>1);
  48858. if (z1+ndelta<depth()) { z0+=ndelta; z1+=ndelta; }
  48859. else { z0+=(depth() - 1 - z1); z1 = depth() - 1; }
  48860. }
  48861. disp.wait(100);
  48862. if (!exit_on_anykey && key && key!=cimg::keyESC &&
  48863. (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
  48864. key = 0;
  48865. }
  48866. }
  48867. disp.set_key(key);
  48868. if (XYZ) { XYZ[0] = _XYZ[0]; XYZ[1] = _XYZ[1]; XYZ[2] = _XYZ[2]; }
  48869. return *this;
  48870. }
  48871. //! Display object 3d in an interactive window.
  48872. /**
  48873. \param disp Display window.
  48874. \param vertices Vertices data of the 3d object.
  48875. \param primitives Primitives data of the 3d object.
  48876. \param colors Colors data of the 3d object.
  48877. \param opacities Opacities data of the 3d object.
  48878. \param centering Tells if the 3d object must be centered for the display.
  48879. \param render_static Rendering mode.
  48880. \param render_motion Rendering mode, when the 3d object is moved.
  48881. \param is_double_sided Tells if the object primitives are double-sided.
  48882. \param focale Focale
  48883. \param light_x X-coordinate of the light source.
  48884. \param light_y Y-coordinate of the light source.
  48885. \param light_z Z-coordinate of the light source.
  48886. \param specular_lightness Amount of specular light.
  48887. \param specular_shininess Shininess of the object material.
  48888. \param display_axes Tells if the 3d axes are displayed.
  48889. \param pose_matrix Pointer to 12 values, defining a 3d pose (as a 4x3 matrix).
  48890. \param exit_on_anykey Exit function when any key is pressed.
  48891. **/
  48892. template<typename tp, typename tf, typename tc, typename to>
  48893. const CImg<T>& display_object3d(CImgDisplay& disp,
  48894. const CImg<tp>& vertices,
  48895. const CImgList<tf>& primitives,
  48896. const CImgList<tc>& colors,
  48897. const to& opacities,
  48898. const bool centering=true,
  48899. const int render_static=4, const int render_motion=1,
  48900. const bool is_double_sided=true, const float focale=700,
  48901. const float light_x=0, const float light_y=0, const float light_z=-5e8f,
  48902. const float specular_lightness=0.2f, const float specular_shininess=0.1f,
  48903. const bool display_axes=true, float *const pose_matrix=0,
  48904. const bool exit_on_anykey=false) const {
  48905. return _display_object3d(disp,0,vertices,primitives,colors,opacities,centering,render_static,
  48906. render_motion,is_double_sided,focale,
  48907. light_x,light_y,light_z,specular_lightness,specular_shininess,
  48908. display_axes,pose_matrix,exit_on_anykey);
  48909. }
  48910. //! Display object 3d in an interactive window \simplification.
  48911. template<typename tp, typename tf, typename tc, typename to>
  48912. const CImg<T>& display_object3d(const char *const title,
  48913. const CImg<tp>& vertices,
  48914. const CImgList<tf>& primitives,
  48915. const CImgList<tc>& colors,
  48916. const to& opacities,
  48917. const bool centering=true,
  48918. const int render_static=4, const int render_motion=1,
  48919. const bool is_double_sided=true, const float focale=700,
  48920. const float light_x=0, const float light_y=0, const float light_z=-5e8f,
  48921. const float specular_lightness=0.2f, const float specular_shininess=0.1f,
  48922. const bool display_axes=true, float *const pose_matrix=0,
  48923. const bool exit_on_anykey=false) const {
  48924. CImgDisplay disp;
  48925. return _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,render_static,
  48926. render_motion,is_double_sided,focale,
  48927. light_x,light_y,light_z,specular_lightness,specular_shininess,
  48928. display_axes,pose_matrix,exit_on_anykey);
  48929. }
  48930. //! Display object 3d in an interactive window \simplification.
  48931. template<typename tp, typename tf, typename tc>
  48932. const CImg<T>& display_object3d(CImgDisplay &disp,
  48933. const CImg<tp>& vertices,
  48934. const CImgList<tf>& primitives,
  48935. const CImgList<tc>& colors,
  48936. const bool centering=true,
  48937. const int render_static=4, const int render_motion=1,
  48938. const bool is_double_sided=true, const float focale=700,
  48939. const float light_x=0, const float light_y=0, const float light_z=-5e8f,
  48940. const float specular_lightness=0.2f, const float specular_shininess=0.1f,
  48941. const bool display_axes=true, float *const pose_matrix=0,
  48942. const bool exit_on_anykey=false) const {
  48943. return display_object3d(disp,vertices,primitives,colors,CImgList<floatT>(),centering,
  48944. render_static,render_motion,is_double_sided,focale,
  48945. light_x,light_y,light_z,specular_lightness,specular_shininess,
  48946. display_axes,pose_matrix,exit_on_anykey);
  48947. }
  48948. //! Display object 3d in an interactive window \simplification.
  48949. template<typename tp, typename tf, typename tc>
  48950. const CImg<T>& display_object3d(const char *const title,
  48951. const CImg<tp>& vertices,
  48952. const CImgList<tf>& primitives,
  48953. const CImgList<tc>& colors,
  48954. const bool centering=true,
  48955. const int render_static=4, const int render_motion=1,
  48956. const bool is_double_sided=true, const float focale=700,
  48957. const float light_x=0, const float light_y=0, const float light_z=-5e8f,
  48958. const float specular_lightness=0.2f, const float specular_shininess=0.1f,
  48959. const bool display_axes=true, float *const pose_matrix=0,
  48960. const bool exit_on_anykey=false) const {
  48961. return display_object3d(title,vertices,primitives,colors,CImgList<floatT>(),centering,
  48962. render_static,render_motion,is_double_sided,focale,
  48963. light_x,light_y,light_z,specular_lightness,specular_shininess,
  48964. display_axes,pose_matrix,exit_on_anykey);
  48965. }
  48966. //! Display object 3d in an interactive window \simplification.
  48967. template<typename tp, typename tf>
  48968. const CImg<T>& display_object3d(CImgDisplay &disp,
  48969. const CImg<tp>& vertices,
  48970. const CImgList<tf>& primitives,
  48971. const bool centering=true,
  48972. const int render_static=4, const int render_motion=1,
  48973. const bool is_double_sided=true, const float focale=700,
  48974. const float light_x=0, const float light_y=0, const float light_z=-5e8f,
  48975. const float specular_lightness=0.2f, const float specular_shininess=0.1f,
  48976. const bool display_axes=true, float *const pose_matrix=0,
  48977. const bool exit_on_anykey=false) const {
  48978. return display_object3d(disp,vertices,primitives,CImgList<T>(),centering,
  48979. render_static,render_motion,is_double_sided,focale,
  48980. light_x,light_y,light_z,specular_lightness,specular_shininess,
  48981. display_axes,pose_matrix,exit_on_anykey);
  48982. }
  48983. //! Display object 3d in an interactive window \simplification.
  48984. template<typename tp, typename tf>
  48985. const CImg<T>& display_object3d(const char *const title,
  48986. const CImg<tp>& vertices,
  48987. const CImgList<tf>& primitives,
  48988. const bool centering=true,
  48989. const int render_static=4, const int render_motion=1,
  48990. const bool is_double_sided=true, const float focale=700,
  48991. const float light_x=0, const float light_y=0, const float light_z=-5e8f,
  48992. const float specular_lightness=0.2f, const float specular_shininess=0.1f,
  48993. const bool display_axes=true, float *const pose_matrix=0,
  48994. const bool exit_on_anykey=false) const {
  48995. return display_object3d(title,vertices,primitives,CImgList<T>(),centering,
  48996. render_static,render_motion,is_double_sided,focale,
  48997. light_x,light_y,light_z,specular_lightness,specular_shininess,
  48998. display_axes,pose_matrix,exit_on_anykey);
  48999. }
  49000. //! Display object 3d in an interactive window \simplification.
  49001. template<typename tp>
  49002. const CImg<T>& display_object3d(CImgDisplay &disp,
  49003. const CImg<tp>& vertices,
  49004. const bool centering=true,
  49005. const int render_static=4, const int render_motion=1,
  49006. const bool is_double_sided=true, const float focale=700,
  49007. const float light_x=0, const float light_y=0, const float light_z=-5e8f,
  49008. const float specular_lightness=0.2f, const float specular_shininess=0.1f,
  49009. const bool display_axes=true, float *const pose_matrix=0,
  49010. const bool exit_on_anykey=false) const {
  49011. return display_object3d(disp,vertices,CImgList<uintT>(),centering,
  49012. render_static,render_motion,is_double_sided,focale,
  49013. light_x,light_y,light_z,specular_lightness,specular_shininess,
  49014. display_axes,pose_matrix,exit_on_anykey);
  49015. }
  49016. //! Display object 3d in an interactive window \simplification.
  49017. template<typename tp>
  49018. const CImg<T>& display_object3d(const char *const title,
  49019. const CImg<tp>& vertices,
  49020. const bool centering=true,
  49021. const int render_static=4, const int render_motion=1,
  49022. const bool is_double_sided=true, const float focale=700,
  49023. const float light_x=0, const float light_y=0, const float light_z=-5e8f,
  49024. const float specular_lightness=0.2f, const float specular_shininess=0.1f,
  49025. const bool display_axes=true, float *const pose_matrix=0,
  49026. const bool exit_on_anykey=false) const {
  49027. return display_object3d(title,vertices,CImgList<uintT>(),centering,
  49028. render_static,render_motion,is_double_sided,focale,
  49029. light_x,light_y,light_z,specular_lightness,specular_shininess,
  49030. display_axes,pose_matrix,exit_on_anykey);
  49031. }
  49032. template<typename tp, typename tf, typename tc, typename to>
  49033. const CImg<T>& _display_object3d(CImgDisplay& disp, const char *const title,
  49034. const CImg<tp>& vertices,
  49035. const CImgList<tf>& primitives,
  49036. const CImgList<tc>& colors,
  49037. const to& opacities,
  49038. const bool centering,
  49039. const int render_static, const int render_motion,
  49040. const bool is_double_sided, const float focale,
  49041. const float light_x, const float light_y, const float light_z,
  49042. const float specular_lightness, const float specular_shininess,
  49043. const bool display_axes, float *const pose_matrix,
  49044. const bool exit_on_anykey) const {
  49045. typedef typename cimg::superset<tp,float>::type tpfloat;
  49046. // Check input arguments
  49047. if (is_empty()) {
  49048. if (disp) return CImg<T>(disp.width(),disp.height(),1,(colors && colors[0].size()==1)?1:3,0).
  49049. _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,
  49050. render_static,render_motion,is_double_sided,focale,
  49051. light_x,light_y,light_z,specular_lightness,specular_shininess,
  49052. display_axes,pose_matrix,exit_on_anykey);
  49053. else return CImg<T>(1,2,1,1,64,128).resize(cimg_fitscreen(CImgDisplay::screen_width()/2,
  49054. CImgDisplay::screen_height()/2,1),
  49055. 1,(colors && colors[0].size()==1)?1:3,3).
  49056. _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,
  49057. render_static,render_motion,is_double_sided,focale,
  49058. light_x,light_y,light_z,specular_lightness,specular_shininess,
  49059. display_axes,pose_matrix,exit_on_anykey);
  49060. } else { if (disp) disp.resize(*this,false); }
  49061. CImg<charT> error_message(1024);
  49062. if (!vertices.is_object3d(primitives,colors,opacities,true,error_message))
  49063. throw CImgArgumentException(_cimg_instance
  49064. "display_object3d(): Invalid specified 3d object (%u,%u) (%s).",
  49065. cimg_instance,vertices._width,primitives._width,error_message.data());
  49066. if (vertices._width && !primitives) {
  49067. CImgList<tf> nprimitives(vertices._width,1,1,1,1);
  49068. cimglist_for(nprimitives,l) nprimitives(l,0) = (tf)l;
  49069. return _display_object3d(disp,title,vertices,nprimitives,colors,opacities,centering,
  49070. render_static,render_motion,is_double_sided,focale,
  49071. light_x,light_y,light_z,specular_lightness,specular_shininess,
  49072. display_axes,pose_matrix,exit_on_anykey);
  49073. }
  49074. if (!disp) {
  49075. disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,3);
  49076. if (!title) disp.set_title("CImg<%s> (%u vertices, %u primitives)",
  49077. pixel_type(),vertices._width,primitives._width);
  49078. } else if (title) disp.set_title("%s",title);
  49079. // Init 3d objects and compute object statistics
  49080. CImg<floatT>
  49081. pose,
  49082. rotated_vertices(vertices._width,3),
  49083. bbox_vertices, rotated_bbox_vertices,
  49084. axes_vertices, rotated_axes_vertices,
  49085. bbox_opacities, axes_opacities;
  49086. CImgList<uintT> bbox_primitives, axes_primitives;
  49087. CImgList<tf> reverse_primitives;
  49088. CImgList<T> bbox_colors, bbox_colors2, axes_colors;
  49089. unsigned int ns_width = 0, ns_height = 0;
  49090. int _is_double_sided = (int)is_double_sided;
  49091. bool ndisplay_axes = display_axes;
  49092. const CImg<T>
  49093. background_color(1,1,1,_spectrum,0),
  49094. foreground_color(1,1,1,_spectrum,255);
  49095. float
  49096. Xoff = 0, Yoff = 0, Zoff = 0, sprite_scale = 1,
  49097. xm = 0, xM = vertices?vertices.get_shared_row(0).max_min(xm):0,
  49098. ym = 0, yM = vertices?vertices.get_shared_row(1).max_min(ym):0,
  49099. zm = 0, zM = vertices?vertices.get_shared_row(2).max_min(zm):0;
  49100. const float delta = cimg::max(xM - xm,yM - ym,zM - zm);
  49101. rotated_bbox_vertices = bbox_vertices.assign(8,3,1,1,
  49102. xm,xM,xM,xm,xm,xM,xM,xm,
  49103. ym,ym,yM,yM,ym,ym,yM,yM,
  49104. zm,zm,zm,zm,zM,zM,zM,zM);
  49105. 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);
  49106. bbox_colors.assign(6,_spectrum,1,1,1,background_color[0]);
  49107. bbox_colors2.assign(6,_spectrum,1,1,1,foreground_color[0]);
  49108. bbox_opacities.assign(bbox_colors._width,1,1,1,0.3f);
  49109. rotated_axes_vertices = axes_vertices.assign(7,3,1,1,
  49110. 0,20,0,0,22,-6,-6,
  49111. 0,0,20,0,-6,22,-6,
  49112. 0,0,0,20,0,0,22);
  49113. axes_opacities.assign(3,1,1,1,1);
  49114. axes_colors.assign(3,_spectrum,1,1,1,foreground_color[0]);
  49115. axes_primitives.assign(3,1,2,1,1, 0,1, 0,2, 0,3);
  49116. // Begin user interaction loop
  49117. CImg<T> visu0(*this,false), visu;
  49118. CImg<tpfloat> zbuffer(visu0.width(),visu0.height(),1,1,0);
  49119. bool init_pose = true, clicked = false, redraw = true;
  49120. unsigned int key = 0;
  49121. int
  49122. x0 = 0, y0 = 0, x1 = 0, y1 = 0,
  49123. nrender_static = render_static,
  49124. nrender_motion = render_motion;
  49125. disp.show().flush();
  49126. while (!disp.is_closed() && !key) {
  49127. // Init object pose
  49128. if (init_pose) {
  49129. const float
  49130. ratio = delta>0?(2.0f*std::min(disp.width(),disp.height())/(3.0f*delta)):1,
  49131. dx = (xM + xm)/2, dy = (yM + ym)/2, dz = (zM + zm)/2;
  49132. if (centering)
  49133. CImg<floatT>(4,3,1,1, ratio,0.,0.,-ratio*dx, 0.,ratio,0.,-ratio*dy, 0.,0.,ratio,-ratio*dz).move_to(pose);
  49134. else CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose);
  49135. if (pose_matrix) {
  49136. CImg<floatT> pose0(pose_matrix,4,3,1,1,false);
  49137. pose0.resize(4,4,1,1,0); pose.resize(4,4,1,1,0);
  49138. pose0(3,3) = pose(3,3) = 1;
  49139. (pose0*pose).get_crop(0,0,3,2).move_to(pose);
  49140. Xoff = pose_matrix[12]; Yoff = pose_matrix[13]; Zoff = pose_matrix[14]; sprite_scale = pose_matrix[15];
  49141. } else { Xoff = Yoff = Zoff = 0; sprite_scale = 1; }
  49142. init_pose = false;
  49143. redraw = true;
  49144. }
  49145. // Rotate and draw 3d object
  49146. if (redraw) {
  49147. const float
  49148. r00 = pose(0,0), r10 = pose(1,0), r20 = pose(2,0), r30 = pose(3,0),
  49149. r01 = pose(0,1), r11 = pose(1,1), r21 = pose(2,1), r31 = pose(3,1),
  49150. r02 = pose(0,2), r12 = pose(1,2), r22 = pose(2,2), r32 = pose(3,2);
  49151. if ((clicked && nrender_motion>=0) || (!clicked && nrender_static>=0))
  49152. cimg_forX(vertices,l) {
  49153. const float x = (float)vertices(l,0), y = (float)vertices(l,1), z = (float)vertices(l,2);
  49154. rotated_vertices(l,0) = r00*x + r10*y + r20*z + r30;
  49155. rotated_vertices(l,1) = r01*x + r11*y + r21*z + r31;
  49156. rotated_vertices(l,2) = r02*x + r12*y + r22*z + r32;
  49157. }
  49158. else cimg_forX(bbox_vertices,l) {
  49159. const float x = bbox_vertices(l,0), y = bbox_vertices(l,1), z = bbox_vertices(l,2);
  49160. rotated_bbox_vertices(l,0) = r00*x + r10*y + r20*z + r30;
  49161. rotated_bbox_vertices(l,1) = r01*x + r11*y + r21*z + r31;
  49162. rotated_bbox_vertices(l,2) = r02*x + r12*y + r22*z + r32;
  49163. }
  49164. // Draw objects
  49165. const bool render_with_zbuffer = !clicked && nrender_static>0;
  49166. visu = visu0;
  49167. if ((clicked && nrender_motion<0) || (!clicked && nrender_static<0))
  49168. visu.draw_object3d(Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
  49169. rotated_bbox_vertices,bbox_primitives,bbox_colors,bbox_opacities,2,false,focale).
  49170. draw_object3d(Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
  49171. rotated_bbox_vertices,bbox_primitives,bbox_colors2,1,false,focale);
  49172. else visu._draw_object3d((void*)0,render_with_zbuffer?zbuffer.fill(0):CImg<tpfloat>::empty(),
  49173. Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
  49174. rotated_vertices,reverse_primitives?reverse_primitives:primitives,
  49175. colors,opacities,clicked?nrender_motion:nrender_static,_is_double_sided==1,focale,
  49176. width()/2.0f + light_x,height()/2.0f + light_y,light_z + Zoff,
  49177. specular_lightness,specular_shininess,sprite_scale);
  49178. // Draw axes
  49179. if (ndisplay_axes) {
  49180. const float
  49181. n = 1e-8f + cimg::hypot(r00,r01,r02),
  49182. _r00 = r00/n, _r10 = r10/n, _r20 = r20/n,
  49183. _r01 = r01/n, _r11 = r11/n, _r21 = r21/n,
  49184. _r02 = r01/n, _r12 = r12/n, _r22 = r22/n,
  49185. Xaxes = 25, Yaxes = visu._height - 38.0f;
  49186. cimg_forX(axes_vertices,l) {
  49187. const float
  49188. x = axes_vertices(l,0),
  49189. y = axes_vertices(l,1),
  49190. z = axes_vertices(l,2);
  49191. rotated_axes_vertices(l,0) = _r00*x + _r10*y + _r20*z;
  49192. rotated_axes_vertices(l,1) = _r01*x + _r11*y + _r21*z;
  49193. rotated_axes_vertices(l,2) = _r02*x + _r12*y + _r22*z;
  49194. }
  49195. axes_opacities(0,0) = (rotated_axes_vertices(1,2)>0)?0.5f:1.0f;
  49196. axes_opacities(1,0) = (rotated_axes_vertices(2,2)>0)?0.5f:1.0f;
  49197. axes_opacities(2,0) = (rotated_axes_vertices(3,2)>0)?0.5f:1.0f;
  49198. visu.draw_object3d(Xaxes,Yaxes,0,rotated_axes_vertices,axes_primitives,
  49199. axes_colors,axes_opacities,1,false,focale).
  49200. draw_text((int)(Xaxes + rotated_axes_vertices(4,0)),
  49201. (int)(Yaxes + rotated_axes_vertices(4,1)),
  49202. "X",axes_colors[0]._data,0,axes_opacities(0,0),13).
  49203. draw_text((int)(Xaxes + rotated_axes_vertices(5,0)),
  49204. (int)(Yaxes + rotated_axes_vertices(5,1)),
  49205. "Y",axes_colors[1]._data,0,axes_opacities(1,0),13).
  49206. draw_text((int)(Xaxes + rotated_axes_vertices(6,0)),
  49207. (int)(Yaxes + rotated_axes_vertices(6,1)),
  49208. "Z",axes_colors[2]._data,0,axes_opacities(2,0),13);
  49209. }
  49210. visu.display(disp);
  49211. if (!clicked || nrender_motion==nrender_static) redraw = false;
  49212. }
  49213. // Handle user interaction
  49214. disp.wait();
  49215. if ((disp.button() || disp.wheel()) && disp.mouse_x()>=0 && disp.mouse_y()>=0) {
  49216. redraw = true;
  49217. if (!clicked) { x0 = x1 = disp.mouse_x(); y0 = y1 = disp.mouse_y(); if (!disp.wheel()) clicked = true; }
  49218. else { x1 = disp.mouse_x(); y1 = disp.mouse_y(); }
  49219. if (disp.button()&1) {
  49220. const float
  49221. R = 0.45f*std::min(disp.width(),disp.height()),
  49222. R2 = R*R,
  49223. u0 = (float)(x0 - disp.width()/2),
  49224. v0 = (float)(y0 - disp.height()/2),
  49225. u1 = (float)(x1 - disp.width()/2),
  49226. v1 = (float)(y1 - disp.height()/2),
  49227. n0 = cimg::hypot(u0,v0),
  49228. n1 = cimg::hypot(u1,v1),
  49229. nu0 = n0>R?(u0*R/n0):u0,
  49230. nv0 = n0>R?(v0*R/n0):v0,
  49231. nw0 = (float)std::sqrt(std::max(0.0f,R2 - nu0*nu0 - nv0*nv0)),
  49232. nu1 = n1>R?(u1*R/n1):u1,
  49233. nv1 = n1>R?(v1*R/n1):v1,
  49234. nw1 = (float)std::sqrt(std::max(0.0f,R2 - nu1*nu1 - nv1*nv1)),
  49235. u = nv0*nw1 - nw0*nv1,
  49236. v = nw0*nu1 - nu0*nw1,
  49237. w = nv0*nu1 - nu0*nv1,
  49238. n = cimg::hypot(u,v,w),
  49239. alpha = (float)std::asin(n/R2)*180/cimg::PI;
  49240. (CImg<floatT>::rotation_matrix(u,v,w,-alpha)*pose).move_to(pose);
  49241. x0 = x1; y0 = y1;
  49242. }
  49243. if (disp.button()&2) {
  49244. if (focale>0) Zoff-=(y0 - y1)*focale/400;
  49245. else { const float s = std::exp((y0 - y1)/400.0f); pose*=s; sprite_scale*=s; }
  49246. x0 = x1; y0 = y1;
  49247. }
  49248. if (disp.wheel()) {
  49249. if (focale>0) Zoff-=disp.wheel()*focale/20;
  49250. else { const float s = std::exp(disp.wheel()/20.0f); pose*=s; sprite_scale*=s; }
  49251. disp.set_wheel();
  49252. }
  49253. if (disp.button()&4) { Xoff+=(x1 - x0); Yoff+=(y1 - y0); x0 = x1; y0 = y1; }
  49254. if ((disp.button()&1) && (disp.button()&2)) {
  49255. init_pose = true; disp.set_button(); x0 = x1; y0 = y1;
  49256. pose = CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0);
  49257. }
  49258. } else if (clicked) { x0 = x1; y0 = y1; clicked = false; redraw = true; }
  49259. CImg<charT> filename(32);
  49260. switch (key = disp.key()) {
  49261. #if cimg_OS!=2
  49262. case cimg::keyCTRLRIGHT :
  49263. #endif
  49264. case 0 : case cimg::keyCTRLLEFT : key = 0; break;
  49265. case cimg::keyD: if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  49266. disp.set_fullscreen(false).
  49267. resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
  49268. CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
  49269. _is_resized = true;
  49270. disp.set_key(key,false); key = 0;
  49271. } break;
  49272. case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  49273. disp.set_fullscreen(false).
  49274. resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
  49275. disp.set_key(key,false); key = 0;
  49276. } break;
  49277. case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  49278. disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true;
  49279. disp.set_key(key,false); key = 0;
  49280. } break;
  49281. case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  49282. if (!ns_width || !ns_height ||
  49283. ns_width>(unsigned int)disp.screen_width() || ns_height>(unsigned int)disp.screen_height()) {
  49284. ns_width = disp.screen_width()*3U/4;
  49285. ns_height = disp.screen_height()*3U/4;
  49286. }
  49287. if (disp.is_fullscreen()) disp.resize(ns_width,ns_height,false);
  49288. else {
  49289. ns_width = disp._width; ns_height = disp._height;
  49290. disp.resize(disp.screen_width(),disp.screen_height(),false);
  49291. }
  49292. disp.toggle_fullscreen()._is_resized = true;
  49293. disp.set_key(key,false); key = 0;
  49294. } break;
  49295. case cimg::keyT : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  49296. // Switch single/double-sided primitives.
  49297. if (--_is_double_sided==-2) _is_double_sided = 1;
  49298. if (_is_double_sided>=0) reverse_primitives.assign();
  49299. else primitives.get_reverse_object3d().move_to(reverse_primitives);
  49300. disp.set_key(key,false); key = 0; redraw = true;
  49301. } break;
  49302. case cimg::keyZ : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Enable/disable Z-buffer
  49303. if (zbuffer) zbuffer.assign();
  49304. else zbuffer.assign(visu0.width(),visu0.height(),1,1,0);
  49305. disp.set_key(key,false); key = 0; redraw = true;
  49306. } break;
  49307. case cimg::keyA : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Show/hide 3d axes.
  49308. ndisplay_axes = !ndisplay_axes;
  49309. disp.set_key(key,false); key = 0; redraw = true;
  49310. } break;
  49311. case cimg::keyF1 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to points.
  49312. nrender_motion = (nrender_static==0 && nrender_motion!=0)?0:-1; nrender_static = 0;
  49313. disp.set_key(key,false); key = 0; redraw = true;
  49314. } break;
  49315. case cimg::keyF2 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to lines.
  49316. nrender_motion = (nrender_static==1 && nrender_motion!=1)?1:-1; nrender_static = 1;
  49317. disp.set_key(key,false); key = 0; redraw = true;
  49318. } break;
  49319. case cimg::keyF3 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat.
  49320. nrender_motion = (nrender_static==2 && nrender_motion!=2)?2:-1; nrender_static = 2;
  49321. disp.set_key(key,false); key = 0; redraw = true;
  49322. } break;
  49323. case cimg::keyF4 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat-shaded.
  49324. nrender_motion = (nrender_static==3 && nrender_motion!=3)?3:-1; nrender_static = 3;
  49325. disp.set_key(key,false); key = 0; redraw = true;
  49326. } break;
  49327. case cimg::keyF5 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  49328. // Set rendering mode to gouraud-shaded.
  49329. nrender_motion = (nrender_static==4 && nrender_motion!=4)?4:-1; nrender_static = 4;
  49330. disp.set_key(key,false); key = 0; redraw = true;
  49331. } break;
  49332. case cimg::keyF6 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to phong-shaded.
  49333. nrender_motion = (nrender_static==5 && nrender_motion!=5)?5:-1; nrender_static = 5;
  49334. disp.set_key(key,false); key = 0; redraw = true;
  49335. } break;
  49336. case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save snapshot
  49337. static unsigned int snap_number = 0;
  49338. std::FILE *file;
  49339. do {
  49340. cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++);
  49341. if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
  49342. } while (file);
  49343. (+visu).draw_text(0,0," Saving snapshot... ",
  49344. foreground_color._data,background_color._data,0.7f,13).display(disp);
  49345. visu.save(filename);
  49346. (+visu).draw_text(0,0," Snapshot '%s' saved. ",
  49347. foreground_color._data,background_color._data,0.7f,13,filename._data).display(disp);
  49348. disp.set_key(key,false); key = 0;
  49349. } break;
  49350. case cimg::keyG : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .off file
  49351. static unsigned int snap_number = 0;
  49352. std::FILE *file;
  49353. do {
  49354. cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.off",snap_number++);
  49355. if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
  49356. } while (file);
  49357. (+visu).draw_text(0,0," Saving object... ",
  49358. foreground_color._data,background_color._data,0.7f,13).display(disp);
  49359. vertices.save_off(reverse_primitives?reverse_primitives:primitives,colors,filename);
  49360. (+visu).draw_text(0,0," Object '%s' saved. ",
  49361. foreground_color._data,background_color._data,0.7f,13,filename._data).display(disp);
  49362. disp.set_key(key,false); key = 0;
  49363. } break;
  49364. case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .cimg file
  49365. static unsigned int snap_number = 0;
  49366. std::FILE *file;
  49367. do {
  49368. #ifdef cimg_use_zlib
  49369. cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++);
  49370. #else
  49371. cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++);
  49372. #endif
  49373. if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
  49374. } while (file);
  49375. (+visu).draw_text(0,0," Saving object... ",
  49376. foreground_color._data,background_color._data,0.7f,13).display(disp);
  49377. vertices.get_object3dtoCImg3d(reverse_primitives?reverse_primitives:primitives,colors,opacities).
  49378. save(filename);
  49379. (+visu).draw_text(0,0," Object '%s' saved. ",
  49380. foreground_color._data,background_color._data,0.7f,13,filename._data).display(disp);
  49381. disp.set_key(key,false); key = 0;
  49382. } break;
  49383. #ifdef cimg_use_board
  49384. case cimg::keyP : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .EPS file
  49385. static unsigned int snap_number = 0;
  49386. std::FILE *file;
  49387. do {
  49388. cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.eps",snap_number++);
  49389. if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
  49390. } while (file);
  49391. (+visu).draw_text(0,0," Saving EPS snapshot... ",
  49392. foreground_color._data,background_color._data,0.7f,13).display(disp);
  49393. LibBoard::Board board;
  49394. (+visu)._draw_object3d(&board,zbuffer.fill(0),
  49395. Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
  49396. rotated_vertices,reverse_primitives?reverse_primitives:primitives,
  49397. colors,opacities,clicked?nrender_motion:nrender_static,
  49398. _is_double_sided==1,focale,
  49399. visu.width()/2.0f + light_x,visu.height()/2.0f + light_y,light_z + Zoff,
  49400. specular_lightness,specular_shininess,
  49401. sprite_scale);
  49402. board.saveEPS(filename);
  49403. (+visu).draw_text(0,0," Object '%s' saved. ",
  49404. foreground_color._data,background_color._data,0.7f,13,filename._data).display(disp);
  49405. disp.set_key(key,false); key = 0;
  49406. } break;
  49407. case cimg::keyV : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .SVG file
  49408. static unsigned int snap_number = 0;
  49409. std::FILE *file;
  49410. do {
  49411. cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.svg",snap_number++);
  49412. if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
  49413. } while (file);
  49414. (+visu).draw_text(0,0," Saving SVG snapshot... ",
  49415. foreground_color._data,background_color._data,0.7f,13).display(disp);
  49416. LibBoard::Board board;
  49417. (+visu)._draw_object3d(&board,zbuffer.fill(0),
  49418. Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
  49419. rotated_vertices,reverse_primitives?reverse_primitives:primitives,
  49420. colors,opacities,clicked?nrender_motion:nrender_static,
  49421. _is_double_sided==1,focale,
  49422. visu.width()/2.0f + light_x,visu.height()/2.0f + light_y,light_z + Zoff,
  49423. specular_lightness,specular_shininess,
  49424. sprite_scale);
  49425. board.saveSVG(filename);
  49426. (+visu).draw_text(0,0," Object '%s' saved. ",
  49427. foreground_color._data,background_color._data,0.7f,13,filename._data).display(disp);
  49428. disp.set_key(key,false); key = 0;
  49429. } break;
  49430. #endif
  49431. }
  49432. if (disp.is_resized()) {
  49433. disp.resize(false); visu0 = get_resize(disp,1);
  49434. if (zbuffer) zbuffer.assign(disp.width(),disp.height());
  49435. redraw = true;
  49436. }
  49437. if (!exit_on_anykey && key && key!=cimg::keyESC &&
  49438. (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
  49439. key = 0;
  49440. }
  49441. }
  49442. if (pose_matrix) {
  49443. std::memcpy(pose_matrix,pose._data,12*sizeof(float));
  49444. pose_matrix[12] = Xoff; pose_matrix[13] = Yoff; pose_matrix[14] = Zoff; pose_matrix[15] = sprite_scale;
  49445. }
  49446. disp.set_button().set_key(key);
  49447. return *this;
  49448. }
  49449. //! Display 1d graph in an interactive window.
  49450. /**
  49451. \param disp Display window.
  49452. \param plot_type Plot type. Can be <tt>{ 0=points | 1=segments | 2=splines | 3=bars }</tt>.
  49453. \param vertex_type Vertex type.
  49454. \param labelx Title for the horizontal axis, as a C-string.
  49455. \param xmin Minimum value along the X-axis.
  49456. \param xmax Maximum value along the X-axis.
  49457. \param labely Title for the vertical axis, as a C-string.
  49458. \param ymin Minimum value along the X-axis.
  49459. \param ymax Maximum value along the X-axis.
  49460. \param exit_on_anykey Exit function when any key is pressed.
  49461. **/
  49462. const CImg<T>& display_graph(CImgDisplay &disp,
  49463. const unsigned int plot_type=1, const unsigned int vertex_type=1,
  49464. const char *const labelx=0, const double xmin=0, const double xmax=0,
  49465. const char *const labely=0, const double ymin=0, const double ymax=0,
  49466. const bool exit_on_anykey=false) const {
  49467. return _display_graph(disp,0,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax,exit_on_anykey);
  49468. }
  49469. //! Display 1d graph in an interactive window \overloading.
  49470. const CImg<T>& display_graph(const char *const title=0,
  49471. const unsigned int plot_type=1, const unsigned int vertex_type=1,
  49472. const char *const labelx=0, const double xmin=0, const double xmax=0,
  49473. const char *const labely=0, const double ymin=0, const double ymax=0,
  49474. const bool exit_on_anykey=false) const {
  49475. CImgDisplay disp;
  49476. return _display_graph(disp,title,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax,exit_on_anykey);
  49477. }
  49478. const CImg<T>& _display_graph(CImgDisplay &disp, const char *const title=0,
  49479. const unsigned int plot_type=1, const unsigned int vertex_type=1,
  49480. const char *const labelx=0, const double xmin=0, const double xmax=0,
  49481. const char *const labely=0, const double ymin=0, const double ymax=0,
  49482. const bool exit_on_anykey=false) const {
  49483. if (is_empty())
  49484. throw CImgInstanceException(_cimg_instance
  49485. "display_graph(): Empty instance.",
  49486. cimg_instance);
  49487. if (!disp) disp.assign(cimg_fitscreen(CImgDisplay::screen_width()/2,CImgDisplay::screen_height()/2,1),0,0).
  49488. set_title(title?"%s":"CImg<%s>",title?title:pixel_type());
  49489. const ulongT siz = (ulongT)_width*_height*_depth, siz1 = std::max((ulongT)1,siz - 1);
  49490. const unsigned int old_normalization = disp.normalization();
  49491. disp.show().flush()._normalization = 0;
  49492. double y0 = ymin, y1 = ymax, nxmin = xmin, nxmax = xmax;
  49493. if (nxmin==nxmax) { nxmin = 0; nxmax = siz1; }
  49494. int x0 = 0, x1 = width()*height()*depth() - 1, key = 0;
  49495. for (bool reset_view = true; !key && !disp.is_closed(); ) {
  49496. if (reset_view) { x0 = 0; x1 = width()*height()*depth() - 1; y0 = ymin; y1 = ymax; reset_view = false; }
  49497. CImg<T> zoom(x1 - x0 + 1,1,1,spectrum());
  49498. cimg_forC(*this,c) zoom.get_shared_channel(c) = CImg<T>(data(x0,0,0,c),x1 - x0 + 1,1,1,1,true);
  49499. if (y0==y1) { y0 = zoom.min_max(y1); const double dy = y1 - y0; y0-=dy/20; y1+=dy/20; }
  49500. if (y0==y1) { --y0; ++y1; }
  49501. const CImg<intT> selection = zoom.get_select_graph(disp,plot_type,vertex_type,
  49502. labelx,
  49503. nxmin + x0*(nxmax - nxmin)/siz1,
  49504. nxmin + x1*(nxmax - nxmin)/siz1,
  49505. labely,y0,y1,true);
  49506. const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y();
  49507. if (selection[0]>=0) {
  49508. if (selection[2]<0) reset_view = true;
  49509. else {
  49510. x1 = x0 + selection[2]; x0+=selection[0];
  49511. if (selection[1]>=0 && selection[3]>=0) {
  49512. y0 = y1 - selection[3]*(y1 - y0)/(disp.height() - 32);
  49513. y1-=selection[1]*(y1 - y0)/(disp.height() - 32);
  49514. }
  49515. }
  49516. } else {
  49517. bool go_in = false, go_out = false, go_left = false, go_right = false, go_up = false, go_down = false;
  49518. switch (key = (int)disp.key()) {
  49519. case cimg::keyHOME : reset_view = true; key = 0; disp.set_key(); break;
  49520. case cimg::keyPADADD : go_in = true; go_out = false; key = 0; disp.set_key(); break;
  49521. case cimg::keyPADSUB : go_out = true; go_in = false; key = 0; disp.set_key(); break;
  49522. case cimg::keyARROWLEFT : case cimg::keyPAD4 : go_left = true; go_right = false; key = 0; disp.set_key();
  49523. break;
  49524. case cimg::keyARROWRIGHT : case cimg::keyPAD6 : go_right = true; go_left = false; key = 0; disp.set_key();
  49525. break;
  49526. case cimg::keyARROWUP : case cimg::keyPAD8 : go_up = true; go_down = false; key = 0; disp.set_key(); break;
  49527. case cimg::keyARROWDOWN : case cimg::keyPAD2 : go_down = true; go_up = false; key = 0; disp.set_key(); break;
  49528. case cimg::keyPAD7 : go_left = true; go_up = true; key = 0; disp.set_key(); break;
  49529. case cimg::keyPAD9 : go_right = true; go_up = true; key = 0; disp.set_key(); break;
  49530. case cimg::keyPAD1 : go_left = true; go_down = true; key = 0; disp.set_key(); break;
  49531. case cimg::keyPAD3 : go_right = true; go_down = true; key = 0; disp.set_key(); break;
  49532. }
  49533. if (disp.wheel()) {
  49534. if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) go_up = !(go_down = disp.wheel()<0);
  49535. else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) go_left = !(go_right = disp.wheel()>0);
  49536. else go_out = !(go_in = disp.wheel()>0);
  49537. key = 0;
  49538. }
  49539. if (go_in) {
  49540. const int
  49541. xsiz = x1 - x0,
  49542. mx = (mouse_x - 16)*xsiz/(disp.width() - 32),
  49543. cx = x0 + cimg::cut(mx,0,xsiz);
  49544. if (x1 - x0>4) {
  49545. x0 = cx - 7*(cx - x0)/8; x1 = cx + 7*(x1 - cx)/8;
  49546. if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  49547. const double
  49548. ysiz = y1 - y0,
  49549. my = (mouse_y - 16)*ysiz/(disp.height() - 32),
  49550. cy = y1 - cimg::cut(my,0.0,ysiz);
  49551. y0 = cy - 7*(cy - y0)/8; y1 = cy + 7*(y1 - cy)/8;
  49552. } else y0 = y1 = 0;
  49553. }
  49554. }
  49555. if (go_out) {
  49556. if (x0>0 || x1<(int)siz1) {
  49557. const int delta_x = (x1 - x0)/8, ndelta_x = delta_x?delta_x:(siz>1);
  49558. const double ndelta_y = (y1 - y0)/8;
  49559. x0-=ndelta_x; x1+=ndelta_x;
  49560. y0-=ndelta_y; y1+=ndelta_y;
  49561. if (x0<0) { x1-=x0; x0 = 0; if (x1>=(int)siz) x1 = (int)siz1; }
  49562. if (x1>=(int)siz) { x0-=(x1 - siz1); x1 = (int)siz1; if (x0<0) x0 = 0; }
  49563. }
  49564. }
  49565. if (go_left) {
  49566. const int delta = (x1 - x0)/5, ndelta = delta?delta:1;
  49567. if (x0 - ndelta>=0) { x0-=ndelta; x1-=ndelta; }
  49568. else { x1-=x0; x0 = 0; }
  49569. go_left = false;
  49570. }
  49571. if (go_right) {
  49572. const int delta = (x1 - x0)/5, ndelta = delta?delta:1;
  49573. if (x1 + ndelta<(int)siz) { x0+=ndelta; x1+=ndelta; }
  49574. else { x0+=(siz1 - x1); x1 = (int)siz1; }
  49575. go_right = false;
  49576. }
  49577. if (go_up) {
  49578. const double delta = (y1 - y0)/10, ndelta = delta?delta:1;
  49579. y0+=ndelta; y1+=ndelta;
  49580. go_up = false;
  49581. }
  49582. if (go_down) {
  49583. const double delta = (y1 - y0)/10, ndelta = delta?delta:1;
  49584. y0-=ndelta; y1-=ndelta;
  49585. go_down = false;
  49586. }
  49587. }
  49588. if (!exit_on_anykey && key && key!=(int)cimg::keyESC &&
  49589. (key!=(int)cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
  49590. disp.set_key(key,false);
  49591. key = 0;
  49592. }
  49593. }
  49594. disp._normalization = old_normalization;
  49595. return *this;
  49596. }
  49597. //! Save image as a file.
  49598. /**
  49599. \param filename Filename, as a C-string.
  49600. \param number When positive, represents an index added to the filename. Otherwise, no number is added.
  49601. \param digits Number of digits used for adding the number to the filename.
  49602. \note
  49603. - The used file format is defined by the file extension in the filename \p filename.
  49604. - Parameter \p number can be used to add a 6-digit number to the filename before saving.
  49605. **/
  49606. const CImg<T>& save(const char *const filename, const int number=-1, const unsigned int digits=6) const {
  49607. if (!filename)
  49608. throw CImgArgumentException(_cimg_instance
  49609. "save(): Specified filename is (null).",
  49610. cimg_instance);
  49611. // Do not test for empty instances, since .cimg format is able to manage empty instances.
  49612. const bool is_stdout = *filename=='-' && (!filename[1] || filename[1]=='.');
  49613. const char *const ext = cimg::split_filename(filename);
  49614. CImg<charT> nfilename(1024);
  49615. const char *const fn = is_stdout?filename:(number>=0)?cimg::number_filename(filename,number,digits,nfilename):
  49616. filename;
  49617. #ifdef cimg_save_plugin
  49618. cimg_save_plugin(fn);
  49619. #endif
  49620. #ifdef cimg_save_plugin1
  49621. cimg_save_plugin1(fn);
  49622. #endif
  49623. #ifdef cimg_save_plugin2
  49624. cimg_save_plugin2(fn);
  49625. #endif
  49626. #ifdef cimg_save_plugin3
  49627. cimg_save_plugin3(fn);
  49628. #endif
  49629. #ifdef cimg_save_plugin4
  49630. cimg_save_plugin4(fn);
  49631. #endif
  49632. #ifdef cimg_save_plugin5
  49633. cimg_save_plugin5(fn);
  49634. #endif
  49635. #ifdef cimg_save_plugin6
  49636. cimg_save_plugin6(fn);
  49637. #endif
  49638. #ifdef cimg_save_plugin7
  49639. cimg_save_plugin7(fn);
  49640. #endif
  49641. #ifdef cimg_save_plugin8
  49642. cimg_save_plugin8(fn);
  49643. #endif
  49644. // Ascii formats
  49645. if (!cimg::strcasecmp(ext,"asc")) return save_ascii(fn);
  49646. else if (!cimg::strcasecmp(ext,"dlm") ||
  49647. !cimg::strcasecmp(ext,"txt")) return save_dlm(fn);
  49648. else if (!cimg::strcasecmp(ext,"cpp") ||
  49649. !cimg::strcasecmp(ext,"hpp") ||
  49650. !cimg::strcasecmp(ext,"h") ||
  49651. !cimg::strcasecmp(ext,"c")) return save_cpp(fn);
  49652. // 2d binary formats
  49653. else if (!cimg::strcasecmp(ext,"bmp")) return save_bmp(fn);
  49654. else if (!cimg::strcasecmp(ext,"jpg") ||
  49655. !cimg::strcasecmp(ext,"jpeg") ||
  49656. !cimg::strcasecmp(ext,"jpe") ||
  49657. !cimg::strcasecmp(ext,"jfif") ||
  49658. !cimg::strcasecmp(ext,"jif")) return save_jpeg(fn);
  49659. else if (!cimg::strcasecmp(ext,"rgb")) return save_rgb(fn);
  49660. else if (!cimg::strcasecmp(ext,"rgba")) return save_rgba(fn);
  49661. else if (!cimg::strcasecmp(ext,"png")) return save_png(fn);
  49662. else if (!cimg::strcasecmp(ext,"pgm") ||
  49663. !cimg::strcasecmp(ext,"ppm") ||
  49664. !cimg::strcasecmp(ext,"pnm")) return save_pnm(fn);
  49665. else if (!cimg::strcasecmp(ext,"pnk")) return save_pnk(fn);
  49666. else if (!cimg::strcasecmp(ext,"pfm")) return save_pfm(fn);
  49667. else if (!cimg::strcasecmp(ext,"exr")) return save_exr(fn);
  49668. else if (!cimg::strcasecmp(ext,"tif") ||
  49669. !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn);
  49670. // 3d binary formats
  49671. else if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true);
  49672. else if (!cimg::strcasecmp(ext,"cimg") || !*ext) return save_cimg(fn,false);
  49673. else if (!cimg::strcasecmp(ext,"dcm")) return save_medcon_external(fn);
  49674. else if (!cimg::strcasecmp(ext,"hdr") ||
  49675. !cimg::strcasecmp(ext,"nii")) return save_analyze(fn);
  49676. else if (!cimg::strcasecmp(ext,"inr")) return save_inr(fn);
  49677. else if (!cimg::strcasecmp(ext,"mnc")) return save_minc2(fn);
  49678. else if (!cimg::strcasecmp(ext,"pan")) return save_pandore(fn);
  49679. else if (!cimg::strcasecmp(ext,"raw")) return save_raw(fn);
  49680. // Archive files
  49681. else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn);
  49682. // Image sequences
  49683. else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,444,true);
  49684. else if (!cimg::strcasecmp(ext,"avi") ||
  49685. !cimg::strcasecmp(ext,"mov") ||
  49686. !cimg::strcasecmp(ext,"asf") ||
  49687. !cimg::strcasecmp(ext,"divx") ||
  49688. !cimg::strcasecmp(ext,"flv") ||
  49689. !cimg::strcasecmp(ext,"mpg") ||
  49690. !cimg::strcasecmp(ext,"m1v") ||
  49691. !cimg::strcasecmp(ext,"m2v") ||
  49692. !cimg::strcasecmp(ext,"m4v") ||
  49693. !cimg::strcasecmp(ext,"mjp") ||
  49694. !cimg::strcasecmp(ext,"mp4") ||
  49695. !cimg::strcasecmp(ext,"mkv") ||
  49696. !cimg::strcasecmp(ext,"mpe") ||
  49697. !cimg::strcasecmp(ext,"movie") ||
  49698. !cimg::strcasecmp(ext,"ogm") ||
  49699. !cimg::strcasecmp(ext,"ogg") ||
  49700. !cimg::strcasecmp(ext,"ogv") ||
  49701. !cimg::strcasecmp(ext,"qt") ||
  49702. !cimg::strcasecmp(ext,"rm") ||
  49703. !cimg::strcasecmp(ext,"vob") ||
  49704. !cimg::strcasecmp(ext,"wmv") ||
  49705. !cimg::strcasecmp(ext,"xvid") ||
  49706. !cimg::strcasecmp(ext,"mpeg")) return save_video(fn);
  49707. return save_other(fn);
  49708. }
  49709. //! Save image as an ascii file.
  49710. /**
  49711. \param filename Filename, as a C-string.
  49712. **/
  49713. const CImg<T>& save_ascii(const char *const filename) const {
  49714. return _save_ascii(0,filename);
  49715. }
  49716. //! Save image as an ascii file \overloading.
  49717. const CImg<T>& save_ascii(std::FILE *const file) const {
  49718. return _save_ascii(file,0);
  49719. }
  49720. const CImg<T>& _save_ascii(std::FILE *const file, const char *const filename) const {
  49721. if (!file && !filename)
  49722. throw CImgArgumentException(_cimg_instance
  49723. "save_ascii(): Specified filename is (null).",
  49724. cimg_instance);
  49725. std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
  49726. std::fprintf(nfile,"%u %u %u %u\n",_width,_height,_depth,_spectrum);
  49727. const T* ptrs = _data;
  49728. cimg_forYZC(*this,y,z,c) {
  49729. cimg_forX(*this,x) std::fprintf(nfile,"%.17g ",(double)*(ptrs++));
  49730. std::fputc('\n',nfile);
  49731. }
  49732. if (!file) cimg::fclose(nfile);
  49733. return *this;
  49734. }
  49735. //! Save image as a .cpp source file.
  49736. /**
  49737. \param filename Filename, as a C-string.
  49738. **/
  49739. const CImg<T>& save_cpp(const char *const filename) const {
  49740. return _save_cpp(0,filename);
  49741. }
  49742. //! Save image as a .cpp source file \overloading.
  49743. const CImg<T>& save_cpp(std::FILE *const file) const {
  49744. return _save_cpp(file,0);
  49745. }
  49746. const CImg<T>& _save_cpp(std::FILE *const file, const char *const filename) const {
  49747. if (!file && !filename)
  49748. throw CImgArgumentException(_cimg_instance
  49749. "save_cpp(): Specified filename is (null).",
  49750. cimg_instance);
  49751. std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
  49752. CImg<charT> varname(1024); *varname = 0;
  49753. if (filename) cimg_sscanf(cimg::basename(filename),"%1023[a-zA-Z0-9_]",varname._data);
  49754. if (!*varname) cimg_snprintf(varname,varname._width,"unnamed");
  49755. std::fprintf(nfile,
  49756. "/* Define image '%s' of size %ux%ux%ux%u and type '%s' */\n"
  49757. "%s data_%s[] = { %s\n ",
  49758. varname._data,_width,_height,_depth,_spectrum,pixel_type(),pixel_type(),varname._data,
  49759. is_empty()?"};":"");
  49760. if (!is_empty()) for (ulongT off = 0, siz = size() - 1; off<=siz; ++off) {
  49761. std::fprintf(nfile,cimg::type<T>::format(),cimg::type<T>::format((*this)[off]));
  49762. if (off==siz) std::fprintf(nfile," };\n");
  49763. else if (!((off + 1)%16)) std::fprintf(nfile,",\n ");
  49764. else std::fprintf(nfile,", ");
  49765. }
  49766. if (!file) cimg::fclose(nfile);
  49767. return *this;
  49768. }
  49769. //! Save image as a DLM file.
  49770. /**
  49771. \param filename Filename, as a C-string.
  49772. **/
  49773. const CImg<T>& save_dlm(const char *const filename) const {
  49774. return _save_dlm(0,filename);
  49775. }
  49776. //! Save image as a DLM file \overloading.
  49777. const CImg<T>& save_dlm(std::FILE *const file) const {
  49778. return _save_dlm(file,0);
  49779. }
  49780. const CImg<T>& _save_dlm(std::FILE *const file, const char *const filename) const {
  49781. if (!file && !filename)
  49782. throw CImgArgumentException(_cimg_instance
  49783. "save_dlm(): Specified filename is (null).",
  49784. cimg_instance);
  49785. if (is_empty()) { cimg::fempty(file,filename); return *this; }
  49786. if (_depth>1)
  49787. cimg::warn(_cimg_instance
  49788. "save_dlm(): Instance is volumetric, values along Z will be unrolled in file '%s'.",
  49789. cimg_instance,
  49790. filename?filename:"(FILE*)");
  49791. if (_spectrum>1)
  49792. cimg::warn(_cimg_instance
  49793. "save_dlm(): Instance is multispectral, values along C will be unrolled in file '%s'.",
  49794. cimg_instance,
  49795. filename?filename:"(FILE*)");
  49796. std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
  49797. const T* ptrs = _data;
  49798. cimg_forYZC(*this,y,z,c) {
  49799. cimg_forX(*this,x) std::fprintf(nfile,"%.17g%s",(double)*(ptrs++),(x==width() - 1)?"":",");
  49800. std::fputc('\n',nfile);
  49801. }
  49802. if (!file) cimg::fclose(nfile);
  49803. return *this;
  49804. }
  49805. //! Save image as a BMP file.
  49806. /**
  49807. \param filename Filename, as a C-string.
  49808. **/
  49809. const CImg<T>& save_bmp(const char *const filename) const {
  49810. return _save_bmp(0,filename);
  49811. }
  49812. //! Save image as a BMP file \overloading.
  49813. const CImg<T>& save_bmp(std::FILE *const file) const {
  49814. return _save_bmp(file,0);
  49815. }
  49816. const CImg<T>& _save_bmp(std::FILE *const file, const char *const filename) const {
  49817. if (!file && !filename)
  49818. throw CImgArgumentException(_cimg_instance
  49819. "save_bmp(): Specified filename is (null).",
  49820. cimg_instance);
  49821. if (is_empty()) { cimg::fempty(file,filename); return *this; }
  49822. if (_depth>1)
  49823. cimg::warn(_cimg_instance
  49824. "save_bmp(): Instance is volumetric, only the first slice will be saved in file '%s'.",
  49825. cimg_instance,
  49826. filename?filename:"(FILE*)");
  49827. if (_spectrum>3)
  49828. cimg::warn(_cimg_instance
  49829. "save_bmp(): Instance is multispectral, only the three first channels will be saved in file '%s'.",
  49830. cimg_instance,
  49831. filename?filename:"(FILE*)");
  49832. std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
  49833. CImg<ucharT> header(54,1,1,1,0);
  49834. unsigned char align_buf[4] = { 0 };
  49835. const unsigned int
  49836. align = (4 - (3*_width)%4)%4,
  49837. buf_size = (3*_width + align)*height(),
  49838. file_size = 54 + buf_size;
  49839. header[0] = 'B'; header[1] = 'M';
  49840. header[0x02] = file_size&0xFF;
  49841. header[0x03] = (file_size>>8)&0xFF;
  49842. header[0x04] = (file_size>>16)&0xFF;
  49843. header[0x05] = (file_size>>24)&0xFF;
  49844. header[0x0A] = 0x36;
  49845. header[0x0E] = 0x28;
  49846. header[0x12] = _width&0xFF;
  49847. header[0x13] = (_width>>8)&0xFF;
  49848. header[0x14] = (_width>>16)&0xFF;
  49849. header[0x15] = (_width>>24)&0xFF;
  49850. header[0x16] = _height&0xFF;
  49851. header[0x17] = (_height>>8)&0xFF;
  49852. header[0x18] = (_height>>16)&0xFF;
  49853. header[0x19] = (_height>>24)&0xFF;
  49854. header[0x1A] = 1;
  49855. header[0x1B] = 0;
  49856. header[0x1C] = 24;
  49857. header[0x1D] = 0;
  49858. header[0x22] = buf_size&0xFF;
  49859. header[0x23] = (buf_size>>8)&0xFF;
  49860. header[0x24] = (buf_size>>16)&0xFF;
  49861. header[0x25] = (buf_size>>24)&0xFF;
  49862. header[0x27] = 0x1;
  49863. header[0x2B] = 0x1;
  49864. cimg::fwrite(header._data,54,nfile);
  49865. const T
  49866. *ptr_r = data(0,_height - 1,0,0),
  49867. *ptr_g = (_spectrum>=2)?data(0,_height - 1,0,1):0,
  49868. *ptr_b = (_spectrum>=3)?data(0,_height - 1,0,2):0;
  49869. switch (_spectrum) {
  49870. case 1 : {
  49871. cimg_forY(*this,y) {
  49872. cimg_forX(*this,x) {
  49873. const unsigned char val = (unsigned char)*(ptr_r++);
  49874. std::fputc(val,nfile); std::fputc(val,nfile); std::fputc(val,nfile);
  49875. }
  49876. cimg::fwrite(align_buf,align,nfile);
  49877. ptr_r-=2*_width;
  49878. }
  49879. } break;
  49880. case 2 : {
  49881. cimg_forY(*this,y) {
  49882. cimg_forX(*this,x) {
  49883. std::fputc(0,nfile);
  49884. std::fputc((unsigned char)(*(ptr_g++)),nfile);
  49885. std::fputc((unsigned char)(*(ptr_r++)),nfile);
  49886. }
  49887. cimg::fwrite(align_buf,align,nfile);
  49888. ptr_r-=2*_width; ptr_g-=2*_width;
  49889. }
  49890. } break;
  49891. default : {
  49892. cimg_forY(*this,y) {
  49893. cimg_forX(*this,x) {
  49894. std::fputc((unsigned char)(*(ptr_b++)),nfile);
  49895. std::fputc((unsigned char)(*(ptr_g++)),nfile);
  49896. std::fputc((unsigned char)(*(ptr_r++)),nfile);
  49897. }
  49898. cimg::fwrite(align_buf,align,nfile);
  49899. ptr_r-=2*_width; ptr_g-=2*_width; ptr_b-=2*_width;
  49900. }
  49901. }
  49902. }
  49903. if (!file) cimg::fclose(nfile);
  49904. return *this;
  49905. }
  49906. //! Save image as a JPEG file.
  49907. /**
  49908. \param filename Filename, as a C-string.
  49909. \param quality Image quality (in %)
  49910. **/
  49911. const CImg<T>& save_jpeg(const char *const filename, const unsigned int quality=100) const {
  49912. return _save_jpeg(0,filename,quality);
  49913. }
  49914. //! Save image as a JPEG file \overloading.
  49915. const CImg<T>& save_jpeg(std::FILE *const file, const unsigned int quality=100) const {
  49916. return _save_jpeg(file,0,quality);
  49917. }
  49918. const CImg<T>& _save_jpeg(std::FILE *const file, const char *const filename, const unsigned int quality) const {
  49919. if (!file && !filename)
  49920. throw CImgArgumentException(_cimg_instance
  49921. "save_jpeg(): Specified filename is (null).",
  49922. cimg_instance);
  49923. if (is_empty()) { cimg::fempty(file,filename); return *this; }
  49924. if (_depth>1)
  49925. cimg::warn(_cimg_instance
  49926. "save_jpeg(): Instance is volumetric, only the first slice will be saved in file '%s'.",
  49927. cimg_instance,
  49928. filename?filename:"(FILE*)");
  49929. #ifndef cimg_use_jpeg
  49930. if (!file) return save_other(filename,quality);
  49931. else throw CImgIOException(_cimg_instance
  49932. "save_jpeg(): Unable to save data in '(*FILE)' unless libjpeg is enabled.",
  49933. cimg_instance);
  49934. #else
  49935. unsigned int dimbuf = 0;
  49936. J_COLOR_SPACE colortype = JCS_RGB;
  49937. switch (_spectrum) {
  49938. case 1 : dimbuf = 1; colortype = JCS_GRAYSCALE; break;
  49939. case 2 : dimbuf = 3; colortype = JCS_RGB; break;
  49940. case 3 : dimbuf = 3; colortype = JCS_RGB; break;
  49941. default : dimbuf = 4; colortype = JCS_CMYK; break;
  49942. }
  49943. // Call libjpeg functions
  49944. struct jpeg_compress_struct cinfo;
  49945. struct jpeg_error_mgr jerr;
  49946. cinfo.err = jpeg_std_error(&jerr);
  49947. jpeg_create_compress(&cinfo);
  49948. std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
  49949. jpeg_stdio_dest(&cinfo,nfile);
  49950. cinfo.image_width = _width;
  49951. cinfo.image_height = _height;
  49952. cinfo.input_components = dimbuf;
  49953. cinfo.in_color_space = colortype;
  49954. jpeg_set_defaults(&cinfo);
  49955. jpeg_set_quality(&cinfo,quality<100?quality:100,TRUE);
  49956. jpeg_start_compress(&cinfo,TRUE);
  49957. JSAMPROW row_pointer[1];
  49958. CImg<ucharT> buffer(_width*dimbuf);
  49959. while (cinfo.next_scanline<cinfo.image_height) {
  49960. unsigned char *ptrd = buffer._data;
  49961. // Fill pixel buffer
  49962. switch (_spectrum) {
  49963. case 1 : { // Greyscale images
  49964. const T *ptr_g = data(0, cinfo.next_scanline);
  49965. for (unsigned int b = 0; b<cinfo.image_width; b++)
  49966. *(ptrd++) = (unsigned char)*(ptr_g++);
  49967. } break;
  49968. case 2 : { // RG images
  49969. const T *ptr_r = data(0,cinfo.next_scanline,0,0),
  49970. *ptr_g = data(0,cinfo.next_scanline,0,1);
  49971. for (unsigned int b = 0; b<cinfo.image_width; ++b) {
  49972. *(ptrd++) = (unsigned char)*(ptr_r++);
  49973. *(ptrd++) = (unsigned char)*(ptr_g++);
  49974. *(ptrd++) = 0;
  49975. }
  49976. } break;
  49977. case 3 : { // RGB images
  49978. const T *ptr_r = data(0,cinfo.next_scanline,0,0),
  49979. *ptr_g = data(0,cinfo.next_scanline,0,1),
  49980. *ptr_b = data(0,cinfo.next_scanline,0,2);
  49981. for (unsigned int b = 0; b<cinfo.image_width; ++b) {
  49982. *(ptrd++) = (unsigned char)*(ptr_r++);
  49983. *(ptrd++) = (unsigned char)*(ptr_g++);
  49984. *(ptrd++) = (unsigned char)*(ptr_b++);
  49985. }
  49986. } break;
  49987. default : { // CMYK images
  49988. const T *ptr_r = data(0,cinfo.next_scanline,0,0),
  49989. *ptr_g = data(0,cinfo.next_scanline,0,1),
  49990. *ptr_b = data(0,cinfo.next_scanline,0,2),
  49991. *ptr_a = data(0,cinfo.next_scanline,0,3);
  49992. for (unsigned int b = 0; b<cinfo.image_width; ++b) {
  49993. *(ptrd++) = (unsigned char)*(ptr_r++);
  49994. *(ptrd++) = (unsigned char)*(ptr_g++);
  49995. *(ptrd++) = (unsigned char)*(ptr_b++);
  49996. *(ptrd++) = (unsigned char)*(ptr_a++);
  49997. }
  49998. }
  49999. }
  50000. *row_pointer = buffer._data;
  50001. jpeg_write_scanlines(&cinfo,row_pointer,1);
  50002. }
  50003. jpeg_finish_compress(&cinfo);
  50004. if (!file) cimg::fclose(nfile);
  50005. jpeg_destroy_compress(&cinfo);
  50006. return *this;
  50007. #endif
  50008. }
  50009. //! Save image, using built-in ImageMagick++ library.
  50010. /**
  50011. \param filename Filename, as a C-string.
  50012. \param bytes_per_pixel Force the number of bytes per pixel for the saving, when possible.
  50013. **/
  50014. const CImg<T>& save_magick(const char *const filename, const unsigned int bytes_per_pixel=0) const {
  50015. if (!filename)
  50016. throw CImgArgumentException(_cimg_instance
  50017. "save_magick(): Specified filename is (null).",
  50018. cimg_instance);
  50019. if (is_empty()) { cimg::fempty(0,filename); return *this; }
  50020. #ifdef cimg_use_magick
  50021. double stmin, stmax = (double)max_min(stmin);
  50022. if (_depth>1)
  50023. cimg::warn(_cimg_instance
  50024. "save_magick(): Instance is volumetric, only the first slice will be saved in file '%s'.",
  50025. cimg_instance,
  50026. filename);
  50027. if (_spectrum>3)
  50028. cimg::warn(_cimg_instance
  50029. "save_magick(): Instance is multispectral, only the three first channels will be "
  50030. "saved in file '%s'.",
  50031. cimg_instance,
  50032. filename);
  50033. if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
  50034. cimg::warn(_cimg_instance
  50035. "save_magick(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
  50036. cimg_instance,
  50037. filename,stmin,stmax);
  50038. Magick::Image image(Magick::Geometry(_width,_height),"black");
  50039. image.type(Magick::TrueColorType);
  50040. image.depth(bytes_per_pixel?(8*bytes_per_pixel):(stmax>=256?16:8));
  50041. const T
  50042. *ptr_r = data(0,0,0,0),
  50043. *ptr_g = _spectrum>1?data(0,0,0,1):0,
  50044. *ptr_b = _spectrum>2?data(0,0,0,2):0;
  50045. Magick::PixelPacket *pixels = image.getPixels(0,0,_width,_height);
  50046. switch (_spectrum) {
  50047. case 1 : // Scalar images
  50048. for (ulongT off = (ulongT)_width*_height; off; --off) {
  50049. pixels->red = pixels->green = pixels->blue = (Magick::Quantum)*(ptr_r++);
  50050. ++pixels;
  50051. }
  50052. break;
  50053. case 2 : // RG images
  50054. for (ulongT off = (ulongT)_width*_height; off; --off) {
  50055. pixels->red = (Magick::Quantum)*(ptr_r++);
  50056. pixels->green = (Magick::Quantum)*(ptr_g++);
  50057. pixels->blue = 0; ++pixels;
  50058. }
  50059. break;
  50060. default : // RGB images
  50061. for (ulongT off = (ulongT)_width*_height; off; --off) {
  50062. pixels->red = (Magick::Quantum)*(ptr_r++);
  50063. pixels->green = (Magick::Quantum)*(ptr_g++);
  50064. pixels->blue = (Magick::Quantum)*(ptr_b++);
  50065. ++pixels;
  50066. }
  50067. }
  50068. image.syncPixels();
  50069. image.write(filename);
  50070. return *this;
  50071. #else
  50072. cimg::unused(bytes_per_pixel);
  50073. throw CImgIOException(_cimg_instance
  50074. "save_magick(): Unable to save file '%s' unless libMagick++ is enabled.",
  50075. cimg_instance,
  50076. filename);
  50077. #endif
  50078. }
  50079. //! Save image as a PNG file.
  50080. /**
  50081. \param filename Filename, as a C-string.
  50082. \param bytes_per_pixel Force the number of bytes per pixels for the saving, when possible.
  50083. **/
  50084. const CImg<T>& save_png(const char *const filename, const unsigned int bytes_per_pixel=0) const {
  50085. return _save_png(0,filename,bytes_per_pixel);
  50086. }
  50087. //! Save image as a PNG file \overloading.
  50088. const CImg<T>& save_png(std::FILE *const file, const unsigned int bytes_per_pixel=0) const {
  50089. return _save_png(file,0,bytes_per_pixel);
  50090. }
  50091. const CImg<T>& _save_png(std::FILE *const file, const char *const filename,
  50092. const unsigned int bytes_per_pixel=0) const {
  50093. if (!file && !filename)
  50094. throw CImgArgumentException(_cimg_instance
  50095. "save_png(): Specified filename is (null).",
  50096. cimg_instance);
  50097. if (is_empty()) { cimg::fempty(file,filename); return *this; }
  50098. #ifndef cimg_use_png
  50099. cimg::unused(bytes_per_pixel);
  50100. if (!file) return save_other(filename);
  50101. else throw CImgIOException(_cimg_instance
  50102. "save_png(): Unable to save data in '(*FILE)' unless libpng is enabled.",
  50103. cimg_instance);
  50104. #else
  50105. #if defined __GNUC__
  50106. const char *volatile nfilename = filename; // Use 'volatile' to avoid (wrong) g++ warning.
  50107. std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"wb");
  50108. volatile double stmin, stmax = (double)max_min(stmin);
  50109. #else
  50110. const char *nfilename = filename;
  50111. std::FILE *nfile = file?file:cimg::fopen(nfilename,"wb");
  50112. double stmin, stmax = (double)max_min(stmin);
  50113. #endif
  50114. if (_depth>1)
  50115. cimg::warn(_cimg_instance
  50116. "save_png(): Instance is volumetric, only the first slice will be saved in file '%s'.",
  50117. cimg_instance,
  50118. filename);
  50119. if (_spectrum>4)
  50120. cimg::warn(_cimg_instance
  50121. "save_png(): Instance is multispectral, only the three first channels will be saved in file '%s'.",
  50122. cimg_instance,
  50123. filename);
  50124. if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
  50125. cimg::warn(_cimg_instance
  50126. "save_png(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
  50127. cimg_instance,
  50128. filename,stmin,stmax);
  50129. // Setup PNG structures for write
  50130. png_voidp user_error_ptr = 0;
  50131. png_error_ptr user_error_fn = 0, user_warning_fn = 0;
  50132. png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,user_error_ptr, user_error_fn,
  50133. user_warning_fn);
  50134. if (!png_ptr){
  50135. if (!file) cimg::fclose(nfile);
  50136. throw CImgIOException(_cimg_instance
  50137. "save_png(): Failed to initialize 'png_ptr' structure when saving file '%s'.",
  50138. cimg_instance,
  50139. nfilename?nfilename:"(FILE*)");
  50140. }
  50141. png_infop info_ptr = png_create_info_struct(png_ptr);
  50142. if (!info_ptr) {
  50143. png_destroy_write_struct(&png_ptr,(png_infopp)0);
  50144. if (!file) cimg::fclose(nfile);
  50145. throw CImgIOException(_cimg_instance
  50146. "save_png(): Failed to initialize 'info_ptr' structure when saving file '%s'.",
  50147. cimg_instance,
  50148. nfilename?nfilename:"(FILE*)");
  50149. }
  50150. if (setjmp(png_jmpbuf(png_ptr))) {
  50151. png_destroy_write_struct(&png_ptr, &info_ptr);
  50152. if (!file) cimg::fclose(nfile);
  50153. throw CImgIOException(_cimg_instance
  50154. "save_png(): Encountered unknown fatal error in libpng when saving file '%s'.",
  50155. cimg_instance,
  50156. nfilename?nfilename:"(FILE*)");
  50157. }
  50158. png_init_io(png_ptr, nfile);
  50159. const int bit_depth = bytes_per_pixel?(bytes_per_pixel*8):(stmax>=256?16:8);
  50160. int color_type;
  50161. switch (spectrum()) {
  50162. case 1 : color_type = PNG_COLOR_TYPE_GRAY; break;
  50163. case 2 : color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break;
  50164. case 3 : color_type = PNG_COLOR_TYPE_RGB; break;
  50165. default : color_type = PNG_COLOR_TYPE_RGB_ALPHA;
  50166. }
  50167. const int interlace_type = PNG_INTERLACE_NONE;
  50168. const int compression_type = PNG_COMPRESSION_TYPE_DEFAULT;
  50169. const int filter_method = PNG_FILTER_TYPE_DEFAULT;
  50170. png_set_IHDR(png_ptr,info_ptr,_width,_height,bit_depth,color_type,interlace_type,compression_type,filter_method);
  50171. png_write_info(png_ptr,info_ptr);
  50172. const int byte_depth = bit_depth>>3;
  50173. const int numChan = spectrum()>4?4:spectrum();
  50174. const int pixel_bit_depth_flag = numChan * (bit_depth - 1);
  50175. // Allocate Memory for Image Save and Fill pixel data
  50176. png_bytep *const imgData = new png_byte*[_height];
  50177. for (unsigned int row = 0; row<_height; ++row) imgData[row] = new png_byte[byte_depth*numChan*_width];
  50178. const T *pC0 = data(0,0,0,0);
  50179. switch (pixel_bit_depth_flag) {
  50180. case 7 : { // Gray 8-bit
  50181. cimg_forY(*this,y) {
  50182. unsigned char *ptrd = imgData[y];
  50183. cimg_forX(*this,x) *(ptrd++) = (unsigned char)*(pC0++);
  50184. }
  50185. } break;
  50186. case 14 : { // Gray w/ Alpha 8-bit
  50187. const T *pC1 = data(0,0,0,1);
  50188. cimg_forY(*this,y) {
  50189. unsigned char *ptrd = imgData[y];
  50190. cimg_forX(*this,x) {
  50191. *(ptrd++) = (unsigned char)*(pC0++);
  50192. *(ptrd++) = (unsigned char)*(pC1++);
  50193. }
  50194. }
  50195. } break;
  50196. case 21 : { // RGB 8-bit
  50197. const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2);
  50198. cimg_forY(*this,y) {
  50199. unsigned char *ptrd = imgData[y];
  50200. cimg_forX(*this,x) {
  50201. *(ptrd++) = (unsigned char)*(pC0++);
  50202. *(ptrd++) = (unsigned char)*(pC1++);
  50203. *(ptrd++) = (unsigned char)*(pC2++);
  50204. }
  50205. }
  50206. } break;
  50207. case 28 : { // RGB x/ Alpha 8-bit
  50208. const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(0,0,0,3);
  50209. cimg_forY(*this,y){
  50210. unsigned char *ptrd = imgData[y];
  50211. cimg_forX(*this,x){
  50212. *(ptrd++) = (unsigned char)*(pC0++);
  50213. *(ptrd++) = (unsigned char)*(pC1++);
  50214. *(ptrd++) = (unsigned char)*(pC2++);
  50215. *(ptrd++) = (unsigned char)*(pC3++);
  50216. }
  50217. }
  50218. } break;
  50219. case 15 : { // Gray 16-bit
  50220. cimg_forY(*this,y){
  50221. unsigned short *ptrd = (unsigned short*)(imgData[y]);
  50222. cimg_forX(*this,x) *(ptrd++) = (unsigned short)*(pC0++);
  50223. if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],_width);
  50224. }
  50225. } break;
  50226. case 30 : { // Gray w/ Alpha 16-bit
  50227. const T *pC1 = data(0,0,0,1);
  50228. cimg_forY(*this,y){
  50229. unsigned short *ptrd = (unsigned short*)(imgData[y]);
  50230. cimg_forX(*this,x) {
  50231. *(ptrd++) = (unsigned short)*(pC0++);
  50232. *(ptrd++) = (unsigned short)*(pC1++);
  50233. }
  50234. if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],2*_width);
  50235. }
  50236. } break;
  50237. case 45 : { // RGB 16-bit
  50238. const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2);
  50239. cimg_forY(*this,y) {
  50240. unsigned short *ptrd = (unsigned short*)(imgData[y]);
  50241. cimg_forX(*this,x) {
  50242. *(ptrd++) = (unsigned short)*(pC0++);
  50243. *(ptrd++) = (unsigned short)*(pC1++);
  50244. *(ptrd++) = (unsigned short)*(pC2++);
  50245. }
  50246. if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],3*_width);
  50247. }
  50248. } break;
  50249. case 60 : { // RGB w/ Alpha 16-bit
  50250. const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(0,0,0,3);
  50251. cimg_forY(*this,y) {
  50252. unsigned short *ptrd = (unsigned short*)(imgData[y]);
  50253. cimg_forX(*this,x) {
  50254. *(ptrd++) = (unsigned short)*(pC0++);
  50255. *(ptrd++) = (unsigned short)*(pC1++);
  50256. *(ptrd++) = (unsigned short)*(pC2++);
  50257. *(ptrd++) = (unsigned short)*(pC3++);
  50258. }
  50259. if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],4*_width);
  50260. }
  50261. } break;
  50262. default :
  50263. if (!file) cimg::fclose(nfile);
  50264. throw CImgIOException(_cimg_instance
  50265. "save_png(): Encountered unknown fatal error in libpng when saving file '%s'.",
  50266. cimg_instance,
  50267. nfilename?nfilename:"(FILE*)");
  50268. }
  50269. png_write_image(png_ptr,imgData);
  50270. png_write_end(png_ptr,info_ptr);
  50271. png_destroy_write_struct(&png_ptr, &info_ptr);
  50272. // Deallocate Image Write Memory
  50273. cimg_forY(*this,n) delete[] imgData[n];
  50274. delete[] imgData;
  50275. if (!file) cimg::fclose(nfile);
  50276. return *this;
  50277. #endif
  50278. }
  50279. //! Save image as a PNM file.
  50280. /**
  50281. \param filename Filename, as a C-string.
  50282. \param bytes_per_pixel Force the number of bytes per pixels for the saving.
  50283. **/
  50284. const CImg<T>& save_pnm(const char *const filename, const unsigned int bytes_per_pixel=0) const {
  50285. return _save_pnm(0,filename,bytes_per_pixel);
  50286. }
  50287. //! Save image as a PNM file \overloading.
  50288. const CImg<T>& save_pnm(std::FILE *const file, const unsigned int bytes_per_pixel=0) const {
  50289. return _save_pnm(file,0,bytes_per_pixel);
  50290. }
  50291. const CImg<T>& _save_pnm(std::FILE *const file, const char *const filename,
  50292. const unsigned int bytes_per_pixel=0) const {
  50293. if (!file && !filename)
  50294. throw CImgArgumentException(_cimg_instance
  50295. "save_pnm(): Specified filename is (null).",
  50296. cimg_instance);
  50297. if (is_empty()) { cimg::fempty(file,filename); return *this; }
  50298. double stmin, stmax = (double)max_min(stmin);
  50299. if (_depth>1)
  50300. cimg::warn(_cimg_instance
  50301. "save_pnm(): Instance is volumetric, only the first slice will be saved in file '%s'.",
  50302. cimg_instance,
  50303. filename?filename:"(FILE*)");
  50304. if (_spectrum>3)
  50305. cimg::warn(_cimg_instance
  50306. "save_pnm(): Instance is multispectral, only the three first channels will be saved in file '%s'.",
  50307. cimg_instance,
  50308. filename?filename:"(FILE*)");
  50309. if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
  50310. cimg::warn(_cimg_instance
  50311. "save_pnm(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
  50312. cimg_instance,
  50313. stmin,stmax,filename?filename:"(FILE*)");
  50314. std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
  50315. const T
  50316. *ptr_r = data(0,0,0,0),
  50317. *ptr_g = (_spectrum>=2)?data(0,0,0,1):0,
  50318. *ptr_b = (_spectrum>=3)?data(0,0,0,2):0;
  50319. const ulongT buf_size = std::min((ulongT)(1024*1024),(ulongT)(_width*_height*(_spectrum==1?1UL:3UL)));
  50320. std::fprintf(nfile,"P%c\n%u %u\n%u\n",
  50321. (_spectrum==1?'5':'6'),_width,_height,stmax<256?255:(stmax<4096?4095:65535));
  50322. switch (_spectrum) {
  50323. case 1 : { // Scalar image
  50324. if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PGM 8 bits
  50325. CImg<ucharT> buf((unsigned int)buf_size);
  50326. for (longT to_write = (longT)width()*height(); to_write>0; ) {
  50327. const ulongT N = std::min((ulongT)to_write,buf_size);
  50328. unsigned char *ptrd = buf._data;
  50329. for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr_r++);
  50330. cimg::fwrite(buf._data,N,nfile);
  50331. to_write-=N;
  50332. }
  50333. } else { // Binary PGM 16 bits
  50334. CImg<ushortT> buf((unsigned int)buf_size);
  50335. for (longT to_write = (longT)width()*height(); to_write>0; ) {
  50336. const ulongT N = std::min((ulongT)to_write,buf_size);
  50337. unsigned short *ptrd = buf._data;
  50338. for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned short)*(ptr_r++);
  50339. if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
  50340. cimg::fwrite(buf._data,N,nfile);
  50341. to_write-=N;
  50342. }
  50343. }
  50344. } break;
  50345. case 2 : { // RG image
  50346. if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits
  50347. CImg<ucharT> buf((unsigned int)buf_size);
  50348. for (longT to_write = (longT)width()*height(); to_write>0; ) {
  50349. const ulongT N = std::min((ulongT)to_write,buf_size/3);
  50350. unsigned char *ptrd = buf._data;
  50351. for (ulongT i = N; i>0; --i) {
  50352. *(ptrd++) = (unsigned char)*(ptr_r++);
  50353. *(ptrd++) = (unsigned char)*(ptr_g++);
  50354. *(ptrd++) = 0;
  50355. }
  50356. cimg::fwrite(buf._data,3*N,nfile);
  50357. to_write-=N;
  50358. }
  50359. } else { // Binary PPM 16 bits
  50360. CImg<ushortT> buf((unsigned int)buf_size);
  50361. for (longT to_write = (longT)width()*height(); to_write>0; ) {
  50362. const ulongT N = std::min((ulongT)to_write,buf_size/3);
  50363. unsigned short *ptrd = buf._data;
  50364. for (ulongT i = N; i>0; --i) {
  50365. *(ptrd++) = (unsigned short)*(ptr_r++);
  50366. *(ptrd++) = (unsigned short)*(ptr_g++);
  50367. *(ptrd++) = 0;
  50368. }
  50369. if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
  50370. cimg::fwrite(buf._data,3*N,nfile);
  50371. to_write-=N;
  50372. }
  50373. }
  50374. } break;
  50375. default : { // RGB image
  50376. if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits
  50377. CImg<ucharT> buf((unsigned int)buf_size);
  50378. for (longT to_write = (longT)width()*height(); to_write>0; ) {
  50379. const ulongT N = std::min((ulongT)to_write,buf_size/3);
  50380. unsigned char *ptrd = buf._data;
  50381. for (ulongT i = N; i>0; --i) {
  50382. *(ptrd++) = (unsigned char)*(ptr_r++);
  50383. *(ptrd++) = (unsigned char)*(ptr_g++);
  50384. *(ptrd++) = (unsigned char)*(ptr_b++);
  50385. }
  50386. cimg::fwrite(buf._data,3*N,nfile);
  50387. to_write-=N;
  50388. }
  50389. } else { // Binary PPM 16 bits
  50390. CImg<ushortT> buf((unsigned int)buf_size);
  50391. for (longT to_write = (longT)width()*height(); to_write>0; ) {
  50392. const ulongT N = std::min((ulongT)to_write,buf_size/3);
  50393. unsigned short *ptrd = buf._data;
  50394. for (ulongT i = N; i>0; --i) {
  50395. *(ptrd++) = (unsigned short)*(ptr_r++);
  50396. *(ptrd++) = (unsigned short)*(ptr_g++);
  50397. *(ptrd++) = (unsigned short)*(ptr_b++);
  50398. }
  50399. if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
  50400. cimg::fwrite(buf._data,3*N,nfile);
  50401. to_write-=N;
  50402. }
  50403. }
  50404. }
  50405. }
  50406. if (!file) cimg::fclose(nfile);
  50407. return *this;
  50408. }
  50409. //! Save image as a PNK file.
  50410. /**
  50411. \param filename Filename, as a C-string.
  50412. **/
  50413. const CImg<T>& save_pnk(const char *const filename) const {
  50414. return _save_pnk(0,filename);
  50415. }
  50416. //! Save image as a PNK file \overloading.
  50417. const CImg<T>& save_pnk(std::FILE *const file) const {
  50418. return _save_pnk(file,0);
  50419. }
  50420. const CImg<T>& _save_pnk(std::FILE *const file, const char *const filename) const {
  50421. if (!file && !filename)
  50422. throw CImgArgumentException(_cimg_instance
  50423. "save_pnk(): Specified filename is (null).",
  50424. cimg_instance);
  50425. if (is_empty()) { cimg::fempty(file,filename); return *this; }
  50426. if (_spectrum>1)
  50427. cimg::warn(_cimg_instance
  50428. "save_pnk(): Instance is multispectral, only the first channel will be saved in file '%s'.",
  50429. cimg_instance,
  50430. filename?filename:"(FILE*)");
  50431. const ulongT buf_size = std::min((ulongT)1024*1024,(ulongT)_width*_height*_depth);
  50432. std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
  50433. const T *ptr = data(0,0,0,0);
  50434. if (!cimg::type<T>::is_float() && sizeof(T)==1 && _depth<2) // Can be saved as regular PNM file.
  50435. _save_pnm(file,filename,0);
  50436. else if (!cimg::type<T>::is_float() && sizeof(T)==1) { // Save as extended P5 file: Binary byte-valued 3d.
  50437. std::fprintf(nfile,"P5\n%u %u %u\n255\n",_width,_height,_depth);
  50438. CImg<ucharT> buf((unsigned int)buf_size);
  50439. for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) {
  50440. const ulongT N = std::min((ulongT)to_write,buf_size);
  50441. unsigned char *ptrd = buf._data;
  50442. for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr++);
  50443. cimg::fwrite(buf._data,N,nfile);
  50444. to_write-=N;
  50445. }
  50446. } else if (!cimg::type<T>::is_float()) { // Save as P8: Binary int32-valued 3d.
  50447. if (_depth>1) std::fprintf(nfile,"P8\n%u %u %u\n%d\n",_width,_height,_depth,(int)max());
  50448. else std::fprintf(nfile,"P8\n%u %u\n%d\n",_width,_height,(int)max());
  50449. CImg<intT> buf((unsigned int)buf_size);
  50450. for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) {
  50451. const ulongT N = std::min((ulongT)to_write,buf_size);
  50452. int *ptrd = buf._data;
  50453. for (ulongT i = N; i>0; --i) *(ptrd++) = (int)*(ptr++);
  50454. cimg::fwrite(buf._data,N,nfile);
  50455. to_write-=N;
  50456. }
  50457. } else { // Save as P9: Binary float-valued 3d.
  50458. if (_depth>1) std::fprintf(nfile,"P9\n%u %u %u\n%g\n",_width,_height,_depth,(double)max());
  50459. else std::fprintf(nfile,"P9\n%u %u\n%g\n",_width,_height,(double)max());
  50460. CImg<floatT> buf((unsigned int)buf_size);
  50461. for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) {
  50462. const ulongT N = std::min((ulongT)to_write,buf_size);
  50463. float *ptrd = buf._data;
  50464. for (ulongT i = N; i>0; --i) *(ptrd++) = (float)*(ptr++);
  50465. cimg::fwrite(buf._data,N,nfile);
  50466. to_write-=N;
  50467. }
  50468. }
  50469. if (!file) cimg::fclose(nfile);
  50470. return *this;
  50471. }
  50472. //! Save image as a PFM file.
  50473. /**
  50474. \param filename Filename, as a C-string.
  50475. **/
  50476. const CImg<T>& save_pfm(const char *const filename) const {
  50477. get_mirror('y')._save_pfm(0,filename);
  50478. return *this;
  50479. }
  50480. //! Save image as a PFM file \overloading.
  50481. const CImg<T>& save_pfm(std::FILE *const file) const {
  50482. get_mirror('y')._save_pfm(file,0);
  50483. return *this;
  50484. }
  50485. const CImg<T>& _save_pfm(std::FILE *const file, const char *const filename) const {
  50486. if (!file && !filename)
  50487. throw CImgArgumentException(_cimg_instance
  50488. "save_pfm(): Specified filename is (null).",
  50489. cimg_instance);
  50490. if (is_empty()) { cimg::fempty(file,filename); return *this; }
  50491. if (_depth>1)
  50492. cimg::warn(_cimg_instance
  50493. "save_pfm(): Instance is volumetric, only the first slice will be saved in file '%s'.",
  50494. cimg_instance,
  50495. filename?filename:"(FILE*)");
  50496. if (_spectrum>3)
  50497. cimg::warn(_cimg_instance
  50498. "save_pfm(): image instance is multispectral, only the three first channels will be saved "
  50499. "in file '%s'.",
  50500. cimg_instance,
  50501. filename?filename:"(FILE*)");
  50502. std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
  50503. const T
  50504. *ptr_r = data(0,0,0,0),
  50505. *ptr_g = (_spectrum>=2)?data(0,0,0,1):0,
  50506. *ptr_b = (_spectrum>=3)?data(0,0,0,2):0;
  50507. const unsigned int buf_size = std::min(1024*1024U,_width*_height*(_spectrum==1?1:3));
  50508. std::fprintf(nfile,"P%c\n%u %u\n1.0\n",
  50509. (_spectrum==1?'f':'F'),_width,_height);
  50510. switch (_spectrum) {
  50511. case 1 : { // Scalar image
  50512. CImg<floatT> buf(buf_size);
  50513. for (longT to_write = (longT)width()*height(); to_write>0; ) {
  50514. const ulongT N = std::min((ulongT)to_write,(ulongT)buf_size);
  50515. float *ptrd = buf._data;
  50516. for (ulongT i = N; i>0; --i) *(ptrd++) = (float)*(ptr_r++);
  50517. if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
  50518. cimg::fwrite(buf._data,N,nfile);
  50519. to_write-=N;
  50520. }
  50521. } break;
  50522. case 2 : { // RG image
  50523. CImg<floatT> buf(buf_size);
  50524. for (longT to_write = (longT)width()*height(); to_write>0; ) {
  50525. const unsigned int N = std::min((unsigned int)to_write,buf_size/3);
  50526. float *ptrd = buf._data;
  50527. for (ulongT i = N; i>0; --i) {
  50528. *(ptrd++) = (float)*(ptr_r++);
  50529. *(ptrd++) = (float)*(ptr_g++);
  50530. *(ptrd++) = 0;
  50531. }
  50532. if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
  50533. cimg::fwrite(buf._data,3*N,nfile);
  50534. to_write-=N;
  50535. }
  50536. } break;
  50537. default : { // RGB image
  50538. CImg<floatT> buf(buf_size);
  50539. for (longT to_write = (longT)width()*height(); to_write>0; ) {
  50540. const unsigned int N = std::min((unsigned int)to_write,buf_size/3);
  50541. float *ptrd = buf._data;
  50542. for (ulongT i = N; i>0; --i) {
  50543. *(ptrd++) = (float)*(ptr_r++);
  50544. *(ptrd++) = (float)*(ptr_g++);
  50545. *(ptrd++) = (float)*(ptr_b++);
  50546. }
  50547. if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
  50548. cimg::fwrite(buf._data,3*N,nfile);
  50549. to_write-=N;
  50550. }
  50551. }
  50552. }
  50553. if (!file) cimg::fclose(nfile);
  50554. return *this;
  50555. }
  50556. //! Save image as a RGB file.
  50557. /**
  50558. \param filename Filename, as a C-string.
  50559. **/
  50560. const CImg<T>& save_rgb(const char *const filename) const {
  50561. return _save_rgb(0,filename);
  50562. }
  50563. //! Save image as a RGB file \overloading.
  50564. const CImg<T>& save_rgb(std::FILE *const file) const {
  50565. return _save_rgb(file,0);
  50566. }
  50567. const CImg<T>& _save_rgb(std::FILE *const file, const char *const filename) const {
  50568. if (!file && !filename)
  50569. throw CImgArgumentException(_cimg_instance
  50570. "save_rgb(): Specified filename is (null).",
  50571. cimg_instance);
  50572. if (is_empty()) { cimg::fempty(file,filename); return *this; }
  50573. if (_spectrum!=3)
  50574. cimg::warn(_cimg_instance
  50575. "save_rgb(): image instance has not exactly 3 channels, for file '%s'.",
  50576. cimg_instance,
  50577. filename?filename:"(FILE*)");
  50578. std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
  50579. const ulongT wh = (ulongT)_width*_height;
  50580. unsigned char *const buffer = new unsigned char[3*wh], *nbuffer = buffer;
  50581. const T
  50582. *ptr1 = data(0,0,0,0),
  50583. *ptr2 = _spectrum>1?data(0,0,0,1):0,
  50584. *ptr3 = _spectrum>2?data(0,0,0,2):0;
  50585. switch (_spectrum) {
  50586. case 1 : { // Scalar image
  50587. for (ulongT k = 0; k<wh; ++k) {
  50588. const unsigned char val = (unsigned char)*(ptr1++);
  50589. *(nbuffer++) = val;
  50590. *(nbuffer++) = val;
  50591. *(nbuffer++) = val;
  50592. }
  50593. } break;
  50594. case 2 : { // RG image
  50595. for (ulongT k = 0; k<wh; ++k) {
  50596. *(nbuffer++) = (unsigned char)(*(ptr1++));
  50597. *(nbuffer++) = (unsigned char)(*(ptr2++));
  50598. *(nbuffer++) = 0;
  50599. }
  50600. } break;
  50601. default : { // RGB image
  50602. for (ulongT k = 0; k<wh; ++k) {
  50603. *(nbuffer++) = (unsigned char)(*(ptr1++));
  50604. *(nbuffer++) = (unsigned char)(*(ptr2++));
  50605. *(nbuffer++) = (unsigned char)(*(ptr3++));
  50606. }
  50607. }
  50608. }
  50609. cimg::fwrite(buffer,3*wh,nfile);
  50610. if (!file) cimg::fclose(nfile);
  50611. delete[] buffer;
  50612. return *this;
  50613. }
  50614. //! Save image as a RGBA file.
  50615. /**
  50616. \param filename Filename, as a C-string.
  50617. **/
  50618. const CImg<T>& save_rgba(const char *const filename) const {
  50619. return _save_rgba(0,filename);
  50620. }
  50621. //! Save image as a RGBA file \overloading.
  50622. const CImg<T>& save_rgba(std::FILE *const file) const {
  50623. return _save_rgba(file,0);
  50624. }
  50625. const CImg<T>& _save_rgba(std::FILE *const file, const char *const filename) const {
  50626. if (!file && !filename)
  50627. throw CImgArgumentException(_cimg_instance
  50628. "save_rgba(): Specified filename is (null).",
  50629. cimg_instance);
  50630. if (is_empty()) { cimg::fempty(file,filename); return *this; }
  50631. if (_spectrum!=4)
  50632. cimg::warn(_cimg_instance
  50633. "save_rgba(): image instance has not exactly 4 channels, for file '%s'.",
  50634. cimg_instance,
  50635. filename?filename:"(FILE*)");
  50636. std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
  50637. const ulongT wh = (ulongT)_width*_height;
  50638. unsigned char *const buffer = new unsigned char[4*wh], *nbuffer = buffer;
  50639. const T
  50640. *ptr1 = data(0,0,0,0),
  50641. *ptr2 = _spectrum>1?data(0,0,0,1):0,
  50642. *ptr3 = _spectrum>2?data(0,0,0,2):0,
  50643. *ptr4 = _spectrum>3?data(0,0,0,3):0;
  50644. switch (_spectrum) {
  50645. case 1 : { // Scalar images
  50646. for (ulongT k = 0; k<wh; ++k) {
  50647. const unsigned char val = (unsigned char)*(ptr1++);
  50648. *(nbuffer++) = val;
  50649. *(nbuffer++) = val;
  50650. *(nbuffer++) = val;
  50651. *(nbuffer++) = 255;
  50652. }
  50653. } break;
  50654. case 2 : { // RG images
  50655. for (ulongT k = 0; k<wh; ++k) {
  50656. *(nbuffer++) = (unsigned char)(*(ptr1++));
  50657. *(nbuffer++) = (unsigned char)(*(ptr2++));
  50658. *(nbuffer++) = 0;
  50659. *(nbuffer++) = 255;
  50660. }
  50661. } break;
  50662. case 3 : { // RGB images
  50663. for (ulongT k = 0; k<wh; ++k) {
  50664. *(nbuffer++) = (unsigned char)(*(ptr1++));
  50665. *(nbuffer++) = (unsigned char)(*(ptr2++));
  50666. *(nbuffer++) = (unsigned char)(*(ptr3++));
  50667. *(nbuffer++) = 255;
  50668. }
  50669. } break;
  50670. default : { // RGBA images
  50671. for (ulongT k = 0; k<wh; ++k) {
  50672. *(nbuffer++) = (unsigned char)(*(ptr1++));
  50673. *(nbuffer++) = (unsigned char)(*(ptr2++));
  50674. *(nbuffer++) = (unsigned char)(*(ptr3++));
  50675. *(nbuffer++) = (unsigned char)(*(ptr4++));
  50676. }
  50677. }
  50678. }
  50679. cimg::fwrite(buffer,4*wh,nfile);
  50680. if (!file) cimg::fclose(nfile);
  50681. delete[] buffer;
  50682. return *this;
  50683. }
  50684. //! Save image as a TIFF file.
  50685. /**
  50686. \param filename Filename, as a C-string.
  50687. \param compression_type Type of data compression. Can be <tt>{ 0=None | 1=LZW | 2=JPEG }</tt>.
  50688. \param voxel_size Voxel size, to be stored in the filename.
  50689. \param description Description, to be stored in the filename.
  50690. \param use_bigtiff Allow to save big tiff files (>4Gb).
  50691. \note
  50692. - libtiff support is enabled by defining the precompilation
  50693. directive \c cimg_use_tif.
  50694. - When libtiff is enabled, 2D and 3D (multipage) several
  50695. channel per pixel are supported for
  50696. <tt>char,uchar,short,ushort,float</tt> and \c double pixel types.
  50697. - If \c cimg_use_tif is not defined at compile time the
  50698. function uses CImg<T>&save_other(const char*).
  50699. **/
  50700. const CImg<T>& save_tiff(const char *const filename, const unsigned int compression_type=0,
  50701. const float *const voxel_size=0, const char *const description=0,
  50702. const bool use_bigtiff=true) const {
  50703. if (!filename)
  50704. throw CImgArgumentException(_cimg_instance
  50705. "save_tiff(): Specified filename is (null).",
  50706. cimg_instance);
  50707. if (is_empty()) { cimg::fempty(0,filename); return *this; }
  50708. #ifdef cimg_use_tiff
  50709. const bool
  50710. _use_bigtiff = use_bigtiff && sizeof(ulongT)>=8 && size()*sizeof(T)>=1UL<<31; // No bigtiff for small images.
  50711. TIFF *tif = TIFFOpen(filename,_use_bigtiff?"w8":"w4");
  50712. if (tif) {
  50713. cimg_forZ(*this,z) _save_tiff(tif,z,z,compression_type,voxel_size,description);
  50714. TIFFClose(tif);
  50715. } else throw CImgIOException(_cimg_instance
  50716. "save_tiff(): Failed to open file '%s' for writing.",
  50717. cimg_instance,
  50718. filename);
  50719. return *this;
  50720. #else
  50721. cimg::unused(compression_type,voxel_size,description,use_bigtiff);
  50722. return save_other(filename);
  50723. #endif
  50724. }
  50725. #ifdef cimg_use_tiff
  50726. #define _cimg_save_tiff(types,typed,compression_type) if (!std::strcmp(types,pixel_type())) { \
  50727. const typed foo = (typed)0; return _save_tiff(tif,directory,z,foo,compression_type,voxel_size,description); }
  50728. // [internal] Save a plane into a tiff file
  50729. template<typename t>
  50730. const CImg<T>& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int z, const t& pixel_t,
  50731. const unsigned int compression_type, const float *const voxel_size,
  50732. const char *const description) const {
  50733. if (is_empty() || !tif || pixel_t) return *this;
  50734. const char *const filename = TIFFFileName(tif);
  50735. uint32 rowsperstrip = (uint32)-1;
  50736. uint16 spp = _spectrum, bpp = sizeof(t)*8, photometric;
  50737. if (spp==3 || spp==4) photometric = PHOTOMETRIC_RGB;
  50738. else photometric = PHOTOMETRIC_MINISBLACK;
  50739. TIFFSetDirectory(tif,directory);
  50740. TIFFSetField(tif,TIFFTAG_IMAGEWIDTH,_width);
  50741. TIFFSetField(tif,TIFFTAG_IMAGELENGTH,_height);
  50742. if (voxel_size) {
  50743. const float vx = voxel_size[0], vy = voxel_size[1], vz = voxel_size[2];
  50744. TIFFSetField(tif,TIFFTAG_RESOLUTIONUNIT,RESUNIT_NONE);
  50745. TIFFSetField(tif,TIFFTAG_XRESOLUTION,1.0f/vx);
  50746. TIFFSetField(tif,TIFFTAG_YRESOLUTION,1.0f/vy);
  50747. CImg<charT> s_description(256);
  50748. cimg_snprintf(s_description,s_description._width,"VX=%g VY=%g VZ=%g spacing=%g",vx,vy,vz,vz);
  50749. TIFFSetField(tif,TIFFTAG_IMAGEDESCRIPTION,s_description.data());
  50750. }
  50751. if (description) TIFFSetField(tif,TIFFTAG_IMAGEDESCRIPTION,description);
  50752. TIFFSetField(tif,TIFFTAG_ORIENTATION,ORIENTATION_TOPLEFT);
  50753. TIFFSetField(tif,TIFFTAG_SAMPLESPERPIXEL,spp);
  50754. if (cimg::type<t>::is_float()) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,3);
  50755. else if (cimg::type<t>::min()==0) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,1);
  50756. else TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,2);
  50757. double valm, valM = max_min(valm);
  50758. TIFFSetField(tif,TIFFTAG_SMINSAMPLEVALUE,valm);
  50759. TIFFSetField(tif,TIFFTAG_SMAXSAMPLEVALUE,valM);
  50760. TIFFSetField(tif,TIFFTAG_BITSPERSAMPLE,bpp);
  50761. TIFFSetField(tif,TIFFTAG_PLANARCONFIG,PLANARCONFIG_CONTIG);
  50762. TIFFSetField(tif,TIFFTAG_PHOTOMETRIC,photometric);
  50763. TIFFSetField(tif,TIFFTAG_COMPRESSION,compression_type==2?COMPRESSION_JPEG:
  50764. compression_type==1?COMPRESSION_LZW:COMPRESSION_NONE);
  50765. rowsperstrip = TIFFDefaultStripSize(tif,rowsperstrip);
  50766. TIFFSetField(tif,TIFFTAG_ROWSPERSTRIP,rowsperstrip);
  50767. TIFFSetField(tif,TIFFTAG_FILLORDER,FILLORDER_MSB2LSB);
  50768. TIFFSetField(tif,TIFFTAG_SOFTWARE,"CImg");
  50769. t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
  50770. if (buf) {
  50771. for (unsigned int row = 0; row<_height; row+=rowsperstrip) {
  50772. uint32 nrow = (row + rowsperstrip>_height?_height - row:rowsperstrip);
  50773. tstrip_t strip = TIFFComputeStrip(tif,row,0);
  50774. tsize_t i = 0;
  50775. for (unsigned int rr = 0; rr<nrow; ++rr)
  50776. for (unsigned int cc = 0; cc<_width; ++cc)
  50777. for (unsigned int vv = 0; vv<spp; ++vv)
  50778. buf[i++] = (t)(*this)(cc,row + rr,z,vv);
  50779. if (TIFFWriteEncodedStrip(tif,strip,buf,i*sizeof(t))<0)
  50780. throw CImgIOException(_cimg_instance
  50781. "save_tiff(): Invalid strip writing when saving file '%s'.",
  50782. cimg_instance,
  50783. filename?filename:"(FILE*)");
  50784. }
  50785. _TIFFfree(buf);
  50786. }
  50787. TIFFWriteDirectory(tif);
  50788. return *this;
  50789. }
  50790. const CImg<T>& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int z,
  50791. const unsigned int compression_type, const float *const voxel_size,
  50792. const char *const description) const {
  50793. _cimg_save_tiff("bool",unsigned char,compression_type);
  50794. _cimg_save_tiff("unsigned char",unsigned char,compression_type);
  50795. _cimg_save_tiff("char",char,compression_type);
  50796. _cimg_save_tiff("unsigned short",unsigned short,compression_type);
  50797. _cimg_save_tiff("short",short,compression_type);
  50798. _cimg_save_tiff("unsigned int",unsigned int,compression_type);
  50799. _cimg_save_tiff("int",int,compression_type);
  50800. _cimg_save_tiff("unsigned int64",unsigned int,compression_type);
  50801. _cimg_save_tiff("int64",int,compression_type);
  50802. _cimg_save_tiff("float",float,compression_type);
  50803. _cimg_save_tiff("double",float,compression_type);
  50804. const char *const filename = TIFFFileName(tif);
  50805. throw CImgInstanceException(_cimg_instance
  50806. "save_tiff(): Unsupported pixel type '%s' for file '%s'.",
  50807. cimg_instance,
  50808. pixel_type(),filename?filename:"(FILE*)");
  50809. return *this;
  50810. }
  50811. #endif
  50812. //! Save image as a MINC2 file.
  50813. /**
  50814. \param filename Filename, as a C-string.
  50815. \param imitate_file If non-zero, reference filename, as a C-string, to borrow header from.
  50816. **/
  50817. const CImg<T>& save_minc2(const char *const filename,
  50818. const char *const imitate_file=0) const {
  50819. if (!filename)
  50820. throw CImgArgumentException(_cimg_instance
  50821. "save_minc2(): Specified filename is (null).",
  50822. cimg_instance);
  50823. if (is_empty()) { cimg::fempty(0,filename); return *this; }
  50824. #ifndef cimg_use_minc2
  50825. cimg::unused(imitate_file);
  50826. return save_other(filename);
  50827. #else
  50828. minc::minc_1_writer wtr;
  50829. if (imitate_file)
  50830. wtr.open(filename, imitate_file);
  50831. else {
  50832. minc::minc_info di;
  50833. if (width()) di.push_back(minc::dim_info(width(),width()*0.5,-1,minc::dim_info::DIM_X));
  50834. if (height()) di.push_back(minc::dim_info(height(),height()*0.5,-1,minc::dim_info::DIM_Y));
  50835. if (depth()) di.push_back(minc::dim_info(depth(),depth()*0.5,-1,minc::dim_info::DIM_Z));
  50836. if (spectrum()) di.push_back(minc::dim_info(spectrum(),spectrum()*0.5,-1,minc::dim_info::DIM_TIME));
  50837. wtr.open(filename,di,1,NC_FLOAT,0);
  50838. }
  50839. if (cimg::type<T>::string()==cimg::type<unsigned char>::string())
  50840. wtr.setup_write_byte();
  50841. else if (cimg::type<T>::string()==cimg::type<int>::string())
  50842. wtr.setup_write_int();
  50843. else if (cimg::type<T>::string()==cimg::type<double>::string())
  50844. wtr.setup_write_double();
  50845. else
  50846. wtr.setup_write_float();
  50847. minc::save_standard_volume(wtr, this->_data);
  50848. return *this;
  50849. #endif
  50850. }
  50851. //! Save image as an ANALYZE7.5 or NIFTI file.
  50852. /**
  50853. \param filename Filename, as a C-string.
  50854. \param voxel_size Pointer to 3 consecutive values that tell about the voxel sizes along the X,Y and Z dimensions.
  50855. **/
  50856. const CImg<T>& save_analyze(const char *const filename, const float *const voxel_size=0) const {
  50857. if (!filename)
  50858. throw CImgArgumentException(_cimg_instance
  50859. "save_analyze(): Specified filename is (null).",
  50860. cimg_instance);
  50861. if (is_empty()) { cimg::fempty(0,filename); return *this; }
  50862. std::FILE *file;
  50863. CImg<charT> hname(1024), iname(1024);
  50864. const char *const ext = cimg::split_filename(filename);
  50865. short datatype = -1;
  50866. if (!*ext) {
  50867. cimg_snprintf(hname,hname._width,"%s.hdr",filename);
  50868. cimg_snprintf(iname,iname._width,"%s.img",filename);
  50869. }
  50870. if (!cimg::strncasecmp(ext,"hdr",3)) {
  50871. std::strcpy(hname,filename);
  50872. std::strncpy(iname,filename,iname._width - 1);
  50873. cimg_sprintf(iname._data + std::strlen(iname) - 3,"img");
  50874. }
  50875. if (!cimg::strncasecmp(ext,"img",3)) {
  50876. std::strcpy(hname,filename);
  50877. std::strncpy(iname,filename,iname._width - 1);
  50878. cimg_sprintf(hname._data + std::strlen(iname) - 3,"hdr");
  50879. }
  50880. if (!cimg::strncasecmp(ext,"nii",3)) {
  50881. std::strncpy(hname,filename,hname._width - 1); *iname = 0;
  50882. }
  50883. CImg<charT> header(*iname?348:352,1,1,1,0);
  50884. int *const iheader = (int*)header._data;
  50885. *iheader = 348;
  50886. std::strcpy(header._data + 4,"CImg");
  50887. std::strcpy(header._data + 14," ");
  50888. ((short*)&(header[36]))[0] = 4096;
  50889. ((char*)&(header[38]))[0] = 114;
  50890. ((short*)&(header[40]))[0] = 4;
  50891. ((short*)&(header[40]))[1] = (short)_width;
  50892. ((short*)&(header[40]))[2] = (short)_height;
  50893. ((short*)&(header[40]))[3] = (short)_depth;
  50894. ((short*)&(header[40]))[4] = (short)_spectrum;
  50895. if (!cimg::strcasecmp(pixel_type(),"bool")) datatype = 2;
  50896. if (!cimg::strcasecmp(pixel_type(),"unsigned char")) datatype = 2;
  50897. if (!cimg::strcasecmp(pixel_type(),"char")) datatype = 2;
  50898. if (!cimg::strcasecmp(pixel_type(),"unsigned short")) datatype = 4;
  50899. if (!cimg::strcasecmp(pixel_type(),"short")) datatype = 4;
  50900. if (!cimg::strcasecmp(pixel_type(),"unsigned int")) datatype = 8;
  50901. if (!cimg::strcasecmp(pixel_type(),"int")) datatype = 8;
  50902. if (!cimg::strcasecmp(pixel_type(),"unsigned int64")) datatype = 8;
  50903. if (!cimg::strcasecmp(pixel_type(),"int64")) datatype = 8;
  50904. if (!cimg::strcasecmp(pixel_type(),"float")) datatype = 16;
  50905. if (!cimg::strcasecmp(pixel_type(),"double")) datatype = 64;
  50906. if (datatype<0)
  50907. throw CImgIOException(_cimg_instance
  50908. "save_analyze(): Unsupported pixel type '%s' for file '%s'.",
  50909. cimg_instance,
  50910. pixel_type(),filename);
  50911. ((short*)&(header[70]))[0] = datatype;
  50912. ((short*)&(header[72]))[0] = sizeof(T);
  50913. ((float*)&(header[108]))[0] = (float)(*iname?0:header.width());
  50914. ((float*)&(header[112]))[0] = 1;
  50915. ((float*)&(header[76]))[0] = 0;
  50916. if (voxel_size) {
  50917. ((float*)&(header[76]))[1] = voxel_size[0];
  50918. ((float*)&(header[76]))[2] = voxel_size[1];
  50919. ((float*)&(header[76]))[3] = voxel_size[2];
  50920. } else ((float*)&(header[76]))[1] = ((float*)&(header[76]))[2] = ((float*)&(header[76]))[3] = 1;
  50921. file = cimg::fopen(hname,"wb");
  50922. cimg::fwrite(header._data,header.width(),file);
  50923. if (*iname) { cimg::fclose(file); file = cimg::fopen(iname,"wb"); }
  50924. cimg::fwrite(_data,size(),file);
  50925. cimg::fclose(file);
  50926. return *this;
  50927. }
  50928. //! Save image as a .cimg file.
  50929. /**
  50930. \param filename Filename, as a C-string.
  50931. \param is_compressed Tells if the file contains compressed image data.
  50932. **/
  50933. const CImg<T>& save_cimg(const char *const filename, const bool is_compressed=false) const {
  50934. CImgList<T>(*this,true).save_cimg(filename,is_compressed);
  50935. return *this;
  50936. }
  50937. //! Save image as a .cimg file \overloading.
  50938. const CImg<T>& save_cimg(std::FILE *const file, const bool is_compressed=false) const {
  50939. CImgList<T>(*this,true).save_cimg(file,is_compressed);
  50940. return *this;
  50941. }
  50942. //! Save image as a sub-image into an existing .cimg file.
  50943. /**
  50944. \param filename Filename, as a C-string.
  50945. \param n0 Index of the image inside the file.
  50946. \param x0 X-coordinate of the sub-image location.
  50947. \param y0 Y-coordinate of the sub-image location.
  50948. \param z0 Z-coordinate of the sub-image location.
  50949. \param c0 C-coordinate of the sub-image location.
  50950. **/
  50951. const CImg<T>& save_cimg(const char *const filename,
  50952. const unsigned int n0,
  50953. const unsigned int x0, const unsigned int y0,
  50954. const unsigned int z0, const unsigned int c0) const {
  50955. CImgList<T>(*this,true).save_cimg(filename,n0,x0,y0,z0,c0);
  50956. return *this;
  50957. }
  50958. //! Save image as a sub-image into an existing .cimg file \overloading.
  50959. const CImg<T>& save_cimg(std::FILE *const file,
  50960. const unsigned int n0,
  50961. const unsigned int x0, const unsigned int y0,
  50962. const unsigned int z0, const unsigned int c0) const {
  50963. CImgList<T>(*this,true).save_cimg(file,n0,x0,y0,z0,c0);
  50964. return *this;
  50965. }
  50966. //! Save blank image as a .cimg file.
  50967. /**
  50968. \param filename Filename, as a C-string.
  50969. \param dx Width of the image.
  50970. \param dy Height of the image.
  50971. \param dz Depth of the image.
  50972. \param dc Number of channels of the image.
  50973. \note
  50974. - All pixel values of the saved image are set to \c 0.
  50975. - Use this method to save large images without having to instanciate and allocate them.
  50976. **/
  50977. static void save_empty_cimg(const char *const filename,
  50978. const unsigned int dx, const unsigned int dy=1,
  50979. const unsigned int dz=1, const unsigned int dc=1) {
  50980. return CImgList<T>::save_empty_cimg(filename,1,dx,dy,dz,dc);
  50981. }
  50982. //! Save blank image as a .cimg file \overloading.
  50983. /**
  50984. Same as save_empty_cimg(const char *,unsigned int,unsigned int,unsigned int,unsigned int)
  50985. with a file stream argument instead of a filename string.
  50986. **/
  50987. static void save_empty_cimg(std::FILE *const file,
  50988. const unsigned int dx, const unsigned int dy=1,
  50989. const unsigned int dz=1, const unsigned int dc=1) {
  50990. return CImgList<T>::save_empty_cimg(file,1,dx,dy,dz,dc);
  50991. }
  50992. //! Save image as an INRIMAGE-4 file.
  50993. /**
  50994. \param filename Filename, as a C-string.
  50995. \param voxel_size Pointer to 3 values specifying the voxel sizes along the X,Y and Z dimensions.
  50996. **/
  50997. const CImg<T>& save_inr(const char *const filename, const float *const voxel_size=0) const {
  50998. return _save_inr(0,filename,voxel_size);
  50999. }
  51000. //! Save image as an INRIMAGE-4 file \overloading.
  51001. const CImg<T>& save_inr(std::FILE *const file, const float *const voxel_size=0) const {
  51002. return _save_inr(file,0,voxel_size);
  51003. }
  51004. const CImg<T>& _save_inr(std::FILE *const file, const char *const filename, const float *const voxel_size) const {
  51005. if (!file && !filename)
  51006. throw CImgArgumentException(_cimg_instance
  51007. "save_inr(): Specified filename is (null).",
  51008. cimg_instance);
  51009. if (is_empty()) { cimg::fempty(file,filename); return *this; }
  51010. int inrpixsize = -1;
  51011. const char *inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0";
  51012. if (!cimg::strcasecmp(pixel_type(),"unsigned char")) {
  51013. inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1;
  51014. }
  51015. if (!cimg::strcasecmp(pixel_type(),"char")) {
  51016. inrtype = "fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1;
  51017. }
  51018. if (!cimg::strcasecmp(pixel_type(),"unsigned short")) {
  51019. inrtype = "unsigned fixed\nPIXSIZE=16 bits\nSCALE=2**0";inrpixsize = 2;
  51020. }
  51021. if (!cimg::strcasecmp(pixel_type(),"short")) {
  51022. inrtype = "fixed\nPIXSIZE=16 bits\nSCALE=2**0"; inrpixsize = 2;
  51023. }
  51024. if (!cimg::strcasecmp(pixel_type(),"unsigned int")) {
  51025. inrtype = "unsigned fixed\nPIXSIZE=32 bits\nSCALE=2**0";inrpixsize = 4;
  51026. }
  51027. if (!cimg::strcasecmp(pixel_type(),"int")) {
  51028. inrtype = "fixed\nPIXSIZE=32 bits\nSCALE=2**0"; inrpixsize = 4;
  51029. }
  51030. if (!cimg::strcasecmp(pixel_type(),"float")) {
  51031. inrtype = "float\nPIXSIZE=32 bits"; inrpixsize = 4;
  51032. }
  51033. if (!cimg::strcasecmp(pixel_type(),"double")) {
  51034. inrtype = "float\nPIXSIZE=64 bits"; inrpixsize = 8;
  51035. }
  51036. if (inrpixsize<=0)
  51037. throw CImgIOException(_cimg_instance
  51038. "save_inr(): Unsupported pixel type '%s' for file '%s'",
  51039. cimg_instance,
  51040. pixel_type(),filename?filename:"(FILE*)");
  51041. std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
  51042. CImg<charT> header(257);
  51043. int err = cimg_snprintf(header,header._width,"#INRIMAGE-4#{\nXDIM=%u\nYDIM=%u\nZDIM=%u\nVDIM=%u\n",
  51044. _width,_height,_depth,_spectrum);
  51045. if (voxel_size) err+=cimg_sprintf(header._data + err,"VX=%g\nVY=%g\nVZ=%g\n",
  51046. voxel_size[0],voxel_size[1],voxel_size[2]);
  51047. err+=cimg_sprintf(header._data + err,"TYPE=%s\nCPU=%s\n",inrtype,cimg::endianness()?"sun":"decm");
  51048. std::memset(header._data + err,'\n',252 - err);
  51049. std::memcpy(header._data + 252,"##}\n",4);
  51050. cimg::fwrite(header._data,256,nfile);
  51051. cimg_forXYZ(*this,x,y,z) cimg_forC(*this,c) cimg::fwrite(&((*this)(x,y,z,c)),1,nfile);
  51052. if (!file) cimg::fclose(nfile);
  51053. return *this;
  51054. }
  51055. //! Save image as an OpenEXR file.
  51056. /**
  51057. \param filename Filename, as a C-string.
  51058. \note The OpenEXR file format is <a href="http://en.wikipedia.org/wiki/OpenEXR">described here</a>.
  51059. **/
  51060. const CImg<T>& save_exr(const char *const filename) const {
  51061. if (!filename)
  51062. throw CImgArgumentException(_cimg_instance
  51063. "save_exr(): Specified filename is (null).",
  51064. cimg_instance);
  51065. if (is_empty()) { cimg::fempty(0,filename); return *this; }
  51066. if (_depth>1)
  51067. cimg::warn(_cimg_instance
  51068. "save_exr(): Instance is volumetric, only the first slice will be saved in file '%s'.",
  51069. cimg_instance,
  51070. filename);
  51071. #ifndef cimg_use_openexr
  51072. return save_other(filename);
  51073. #else
  51074. Imf::Rgba *const ptrd0 = new Imf::Rgba[(size_t)_width*_height], *ptrd = ptrd0, rgba;
  51075. switch (_spectrum) {
  51076. case 1 : { // Grayscale image.
  51077. for (const T *ptr_r = data(), *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e;) {
  51078. rgba.r = rgba.g = rgba.b = (half)(*(ptr_r++));
  51079. rgba.a = (half)1;
  51080. *(ptrd++) = rgba;
  51081. }
  51082. } break;
  51083. case 2 : { // RG image.
  51084. for (const T *ptr_r = data(), *ptr_g = data(0,0,0,1),
  51085. *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e; ) {
  51086. rgba.r = (half)(*(ptr_r++));
  51087. rgba.g = (half)(*(ptr_g++));
  51088. rgba.b = (half)0;
  51089. rgba.a = (half)1;
  51090. *(ptrd++) = rgba;
  51091. }
  51092. } break;
  51093. case 3 : { // RGB image.
  51094. for (const T *ptr_r = data(), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2),
  51095. *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e;) {
  51096. rgba.r = (half)(*(ptr_r++));
  51097. rgba.g = (half)(*(ptr_g++));
  51098. rgba.b = (half)(*(ptr_b++));
  51099. rgba.a = (half)1;
  51100. *(ptrd++) = rgba;
  51101. }
  51102. } break;
  51103. default : { // RGBA image.
  51104. 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),
  51105. *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e;) {
  51106. rgba.r = (half)(*(ptr_r++));
  51107. rgba.g = (half)(*(ptr_g++));
  51108. rgba.b = (half)(*(ptr_b++));
  51109. rgba.a = (half)(*(ptr_a++));
  51110. *(ptrd++) = rgba;
  51111. }
  51112. } break;
  51113. }
  51114. Imf::RgbaOutputFile outFile(filename,_width,_height,
  51115. _spectrum==1?Imf::WRITE_Y:_spectrum==2?Imf::WRITE_YA:_spectrum==3?
  51116. Imf::WRITE_RGB:Imf::WRITE_RGBA);
  51117. outFile.setFrameBuffer(ptrd0,1,_width);
  51118. outFile.writePixels(_height);
  51119. delete[] ptrd0;
  51120. return *this;
  51121. #endif
  51122. }
  51123. //! Save image as a Pandore-5 file.
  51124. /**
  51125. \param filename Filename, as a C-string.
  51126. \param colorspace Colorspace data field in output file
  51127. (see <a href="http://www.greyc.ensicaen.fr/~regis/Pandore">Pandore file specifications</a>
  51128. for more information).
  51129. **/
  51130. const CImg<T>& save_pandore(const char *const filename, const unsigned int colorspace=0) const {
  51131. return _save_pandore(0,filename,colorspace);
  51132. }
  51133. //! Save image as a Pandore-5 file \overloading.
  51134. /**
  51135. Same as save_pandore(const char *,unsigned int) const
  51136. with a file stream argument instead of a filename string.
  51137. **/
  51138. const CImg<T>& save_pandore(std::FILE *const file, const unsigned int colorspace=0) const {
  51139. return _save_pandore(file,0,colorspace);
  51140. }
  51141. unsigned int _save_pandore_header_length(unsigned int id, unsigned int *dims, const unsigned int colorspace) const {
  51142. unsigned int nbdims = 0;
  51143. if (id==2 || id==3 || id==4) {
  51144. dims[0] = 1; dims[1] = _width; nbdims = 2;
  51145. }
  51146. if (id==5 || id==6 || id==7) {
  51147. dims[0] = 1; dims[1] = _height; dims[2] = _width; nbdims=3;
  51148. }
  51149. if (id==8 || id==9 || id==10) {
  51150. dims[0] = _spectrum; dims[1] = _depth; dims[2] = _height; dims[3] = _width; nbdims = 4;
  51151. }
  51152. if (id==16 || id==17 || id==18) {
  51153. dims[0] = 3; dims[1] = _height; dims[2] = _width; dims[3] = colorspace; nbdims = 4;
  51154. }
  51155. if (id==19 || id==20 || id==21) {
  51156. dims[0] = 3; dims[1] = _depth; dims[2] = _height; dims[3] = _width; dims[4] = colorspace; nbdims = 5;
  51157. }
  51158. if (id==22 || id==23 || id==25) {
  51159. dims[0] = _spectrum; dims[1] = _width; nbdims = 2;
  51160. }
  51161. if (id==26 || id==27 || id==29) {
  51162. dims[0] = _spectrum; dims[1] = _height; dims[2] = _width; nbdims=3;
  51163. }
  51164. if (id==30 || id==31 || id==33) {
  51165. dims[0] = _spectrum; dims[1] = _depth; dims[2] = _height; dims[3] = _width; nbdims = 4;
  51166. }
  51167. return nbdims;
  51168. }
  51169. const CImg<T>& _save_pandore(std::FILE *const file, const char *const filename,
  51170. const unsigned int colorspace) const {
  51171. #define __cimg_save_pandore_case(dtype) \
  51172. dtype *buffer = new dtype[size()]; \
  51173. const T *ptrs = _data; \
  51174. cimg_foroff(*this,off) *(buffer++) = (dtype)(*(ptrs++)); \
  51175. buffer-=size(); \
  51176. cimg::fwrite(buffer,size(),nfile); \
  51177. delete[] buffer
  51178. #define _cimg_save_pandore_case(sy,sz,sv,stype,id) \
  51179. if (!saved && (sy?(sy==_height):true) && (sz?(sz==_depth):true) && \
  51180. (sv?(sv==_spectrum):true) && !std::strcmp(stype,pixel_type())) { \
  51181. unsigned int *iheader = (unsigned int*)(header + 12); \
  51182. nbdims = _save_pandore_header_length((*iheader=id),dims,colorspace); \
  51183. cimg::fwrite(header,36,nfile); \
  51184. if (sizeof(unsigned long)==4) { CImg<ulongT> ndims(5); \
  51185. for (int d = 0; d<5; ++d) ndims[d] = (unsigned long)dims[d]; cimg::fwrite(ndims._data,nbdims,nfile); } \
  51186. else if (sizeof(unsigned int)==4) { CImg<uintT> ndims(5); \
  51187. for (int d = 0; d<5; ++d) ndims[d] = (unsigned int)dims[d]; cimg::fwrite(ndims._data,nbdims,nfile); } \
  51188. else if (sizeof(unsigned short)==4) { CImg<ushortT> ndims(5); \
  51189. for (int d = 0; d<5; ++d) ndims[d] = (unsigned short)dims[d]; cimg::fwrite(ndims._data,nbdims,nfile); } \
  51190. else throw CImgIOException(_cimg_instance \
  51191. "save_pandore(): Unsupported datatype for file '%s'.",\
  51192. cimg_instance, \
  51193. filename?filename:"(FILE*)"); \
  51194. if (id==2 || id==5 || id==8 || id==16 || id==19 || id==22 || id==26 || id==30) { \
  51195. __cimg_save_pandore_case(unsigned char); \
  51196. } else if (id==3 || id==6 || id==9 || id==17 || id==20 || id==23 || id==27 || id==31) { \
  51197. if (sizeof(unsigned long)==4) { __cimg_save_pandore_case(unsigned long); } \
  51198. else if (sizeof(unsigned int)==4) { __cimg_save_pandore_case(unsigned int); } \
  51199. else if (sizeof(unsigned short)==4) { __cimg_save_pandore_case(unsigned short); } \
  51200. else throw CImgIOException(_cimg_instance \
  51201. "save_pandore(): Unsupported datatype for file '%s'.",\
  51202. cimg_instance, \
  51203. filename?filename:"(FILE*)"); \
  51204. } else if (id==4 || id==7 || id==10 || id==18 || id==21 || id==25 || id==29 || id==33) { \
  51205. if (sizeof(double)==4) { __cimg_save_pandore_case(double); } \
  51206. else if (sizeof(float)==4) { __cimg_save_pandore_case(float); } \
  51207. else throw CImgIOException(_cimg_instance \
  51208. "save_pandore(): Unsupported datatype for file '%s'.",\
  51209. cimg_instance, \
  51210. filename?filename:"(FILE*)"); \
  51211. } \
  51212. saved = true; \
  51213. }
  51214. if (!file && !filename)
  51215. throw CImgArgumentException(_cimg_instance
  51216. "save_pandore(): Specified filename is (null).",
  51217. cimg_instance);
  51218. if (is_empty()) { cimg::fempty(file,filename); return *this; }
  51219. std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
  51220. unsigned char header[36] = { 'P','A','N','D','O','R','E','0','4',0,0,0,
  51221. 0,0,0,0,'C','I','m','g',0,0,0,0,0,
  51222. 'N','o',' ','d','a','t','e',0,0,0,0 };
  51223. unsigned int nbdims, dims[5] = { 0 };
  51224. bool saved = false;
  51225. _cimg_save_pandore_case(1,1,1,"unsigned char",2);
  51226. _cimg_save_pandore_case(1,1,1,"char",3);
  51227. _cimg_save_pandore_case(1,1,1,"unsigned short",3);
  51228. _cimg_save_pandore_case(1,1,1,"short",3);
  51229. _cimg_save_pandore_case(1,1,1,"unsigned int",3);
  51230. _cimg_save_pandore_case(1,1,1,"int",3);
  51231. _cimg_save_pandore_case(1,1,1,"unsigned int64",3);
  51232. _cimg_save_pandore_case(1,1,1,"int64",3);
  51233. _cimg_save_pandore_case(1,1,1,"float",4);
  51234. _cimg_save_pandore_case(1,1,1,"double",4);
  51235. _cimg_save_pandore_case(0,1,1,"unsigned char",5);
  51236. _cimg_save_pandore_case(0,1,1,"char",6);
  51237. _cimg_save_pandore_case(0,1,1,"unsigned short",6);
  51238. _cimg_save_pandore_case(0,1,1,"short",6);
  51239. _cimg_save_pandore_case(0,1,1,"unsigned int",6);
  51240. _cimg_save_pandore_case(0,1,1,"int",6);
  51241. _cimg_save_pandore_case(0,1,1,"unsigned int64",6);
  51242. _cimg_save_pandore_case(0,1,1,"int64",6);
  51243. _cimg_save_pandore_case(0,1,1,"float",7);
  51244. _cimg_save_pandore_case(0,1,1,"double",7);
  51245. _cimg_save_pandore_case(0,0,1,"unsigned char",8);
  51246. _cimg_save_pandore_case(0,0,1,"char",9);
  51247. _cimg_save_pandore_case(0,0,1,"unsigned short",9);
  51248. _cimg_save_pandore_case(0,0,1,"short",9);
  51249. _cimg_save_pandore_case(0,0,1,"unsigned int",9);
  51250. _cimg_save_pandore_case(0,0,1,"int",9);
  51251. _cimg_save_pandore_case(0,0,1,"unsigned int64",9);
  51252. _cimg_save_pandore_case(0,0,1,"int64",9);
  51253. _cimg_save_pandore_case(0,0,1,"float",10);
  51254. _cimg_save_pandore_case(0,0,1,"double",10);
  51255. _cimg_save_pandore_case(0,1,3,"unsigned char",16);
  51256. _cimg_save_pandore_case(0,1,3,"char",17);
  51257. _cimg_save_pandore_case(0,1,3,"unsigned short",17);
  51258. _cimg_save_pandore_case(0,1,3,"short",17);
  51259. _cimg_save_pandore_case(0,1,3,"unsigned int",17);
  51260. _cimg_save_pandore_case(0,1,3,"int",17);
  51261. _cimg_save_pandore_case(0,1,3,"unsigned int64",17);
  51262. _cimg_save_pandore_case(0,1,3,"int64",17);
  51263. _cimg_save_pandore_case(0,1,3,"float",18);
  51264. _cimg_save_pandore_case(0,1,3,"double",18);
  51265. _cimg_save_pandore_case(0,0,3,"unsigned char",19);
  51266. _cimg_save_pandore_case(0,0,3,"char",20);
  51267. _cimg_save_pandore_case(0,0,3,"unsigned short",20);
  51268. _cimg_save_pandore_case(0,0,3,"short",20);
  51269. _cimg_save_pandore_case(0,0,3,"unsigned int",20);
  51270. _cimg_save_pandore_case(0,0,3,"int",20);
  51271. _cimg_save_pandore_case(0,0,3,"unsigned int64",20);
  51272. _cimg_save_pandore_case(0,0,3,"int64",20);
  51273. _cimg_save_pandore_case(0,0,3,"float",21);
  51274. _cimg_save_pandore_case(0,0,3,"double",21);
  51275. _cimg_save_pandore_case(1,1,0,"unsigned char",22);
  51276. _cimg_save_pandore_case(1,1,0,"char",23);
  51277. _cimg_save_pandore_case(1,1,0,"unsigned short",23);
  51278. _cimg_save_pandore_case(1,1,0,"short",23);
  51279. _cimg_save_pandore_case(1,1,0,"unsigned int",23);
  51280. _cimg_save_pandore_case(1,1,0,"int",23);
  51281. _cimg_save_pandore_case(1,1,0,"unsigned int64",23);
  51282. _cimg_save_pandore_case(1,1,0,"int64",23);
  51283. _cimg_save_pandore_case(1,1,0,"float",25);
  51284. _cimg_save_pandore_case(1,1,0,"double",25);
  51285. _cimg_save_pandore_case(0,1,0,"unsigned char",26);
  51286. _cimg_save_pandore_case(0,1,0,"char",27);
  51287. _cimg_save_pandore_case(0,1,0,"unsigned short",27);
  51288. _cimg_save_pandore_case(0,1,0,"short",27);
  51289. _cimg_save_pandore_case(0,1,0,"unsigned int",27);
  51290. _cimg_save_pandore_case(0,1,0,"int",27);
  51291. _cimg_save_pandore_case(0,1,0,"unsigned int64",27);
  51292. _cimg_save_pandore_case(0,1,0,"int64",27);
  51293. _cimg_save_pandore_case(0,1,0,"float",29);
  51294. _cimg_save_pandore_case(0,1,0,"double",29);
  51295. _cimg_save_pandore_case(0,0,0,"unsigned char",30);
  51296. _cimg_save_pandore_case(0,0,0,"char",31);
  51297. _cimg_save_pandore_case(0,0,0,"unsigned short",31);
  51298. _cimg_save_pandore_case(0,0,0,"short",31);
  51299. _cimg_save_pandore_case(0,0,0,"unsigned int",31);
  51300. _cimg_save_pandore_case(0,0,0,"int",31);
  51301. _cimg_save_pandore_case(0,0,0,"unsigned int64",31);
  51302. _cimg_save_pandore_case(0,0,0,"int64",31);
  51303. _cimg_save_pandore_case(0,0,0,"float",33);
  51304. _cimg_save_pandore_case(0,0,0,"double",33);
  51305. if (!file) cimg::fclose(nfile);
  51306. return *this;
  51307. }
  51308. //! Save image as a raw data file.
  51309. /**
  51310. \param filename Filename, as a C-string.
  51311. \param is_multiplexed Tells if the image channels are stored in a multiplexed way (\c true) or not (\c false).
  51312. \note The .raw format does not store the image dimensions in the output file,
  51313. so you have to keep track of them somewhere to be able to read the file correctly afterwards.
  51314. **/
  51315. const CImg<T>& save_raw(const char *const filename, const bool is_multiplexed=false) const {
  51316. return _save_raw(0,filename,is_multiplexed);
  51317. }
  51318. //! Save image as a raw data file \overloading.
  51319. /**
  51320. Same as save_raw(const char *,bool) const
  51321. with a file stream argument instead of a filename string.
  51322. **/
  51323. const CImg<T>& save_raw(std::FILE *const file, const bool is_multiplexed=false) const {
  51324. return _save_raw(file,0,is_multiplexed);
  51325. }
  51326. const CImg<T>& _save_raw(std::FILE *const file, const char *const filename, const bool is_multiplexed) const {
  51327. if (!file && !filename)
  51328. throw CImgArgumentException(_cimg_instance
  51329. "save_raw(): Specified filename is (null).",
  51330. cimg_instance);
  51331. if (is_empty()) { cimg::fempty(file,filename); return *this; }
  51332. std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
  51333. if (!is_multiplexed) cimg::fwrite(_data,size(),nfile);
  51334. else {
  51335. CImg<T> buf(_spectrum);
  51336. cimg_forXYZ(*this,x,y,z) {
  51337. cimg_forC(*this,c) buf[c] = (*this)(x,y,z,c);
  51338. cimg::fwrite(buf._data,_spectrum,nfile);
  51339. }
  51340. }
  51341. if (!file) cimg::fclose(nfile);
  51342. return *this;
  51343. }
  51344. //! Save image as a .yuv video file.
  51345. /**
  51346. \param filename Filename, as a C-string.
  51347. \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
  51348. \param is_rgb Tells if pixel values of the instance image are RGB-coded (\c true) or YUV-coded (\c false).
  51349. \note Each slice of the instance image is considered to be a single frame of the output video file.
  51350. **/
  51351. const CImg<T>& save_yuv(const char *const filename,
  51352. const unsigned int chroma_subsampling=444,
  51353. const bool is_rgb=true) const {
  51354. CImgList<T>(*this,true).save_yuv(filename,chroma_subsampling,is_rgb);
  51355. return *this;
  51356. }
  51357. //! Save image as a .yuv video file \overloading.
  51358. /**
  51359. Same as save_yuv(const char*,const unsigned int,const bool) const
  51360. with a file stream argument instead of a filename string.
  51361. **/
  51362. const CImg<T>& save_yuv(std::FILE *const file,
  51363. const unsigned int chroma_subsampling=444,
  51364. const bool is_rgb=true) const {
  51365. CImgList<T>(*this,true).save_yuv(file,chroma_subsampling,is_rgb);
  51366. return *this;
  51367. }
  51368. //! Save 3d object as an Object File Format (.off) file.
  51369. /**
  51370. \param filename Filename, as a C-string.
  51371. \param primitives List of 3d object primitives.
  51372. \param colors List of 3d object colors.
  51373. \note
  51374. - Instance image contains the vertices data of the 3d object.
  51375. - Textured, transparent or sphere-shaped primitives cannot be managed by the .off file format.
  51376. Such primitives will be lost or simplified during file saving.
  51377. - The .off file format is <a href="http://people.sc.fsu.edu/~jburkardt/html/off_format.html">described here</a>.
  51378. **/
  51379. template<typename tf, typename tc>
  51380. const CImg<T>& save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors,
  51381. const char *const filename) const {
  51382. return _save_off(primitives,colors,0,filename);
  51383. }
  51384. //! Save 3d object as an Object File Format (.off) file \overloading.
  51385. /**
  51386. Same as save_off(const CImgList<tf>&,const CImgList<tc>&,const char*) const
  51387. with a file stream argument instead of a filename string.
  51388. **/
  51389. template<typename tf, typename tc>
  51390. const CImg<T>& save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors,
  51391. std::FILE *const file) const {
  51392. return _save_off(primitives,colors,file,0);
  51393. }
  51394. template<typename tf, typename tc>
  51395. const CImg<T>& _save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors,
  51396. std::FILE *const file, const char *const filename) const {
  51397. if (!file && !filename)
  51398. throw CImgArgumentException(_cimg_instance
  51399. "save_off(): Specified filename is (null).",
  51400. cimg_instance);
  51401. if (is_empty())
  51402. throw CImgInstanceException(_cimg_instance
  51403. "save_off(): Empty instance, for file '%s'.",
  51404. cimg_instance,
  51405. filename?filename:"(FILE*)");
  51406. CImgList<T> opacities;
  51407. CImg<charT> error_message(1024);
  51408. if (!is_object3d(primitives,colors,opacities,true,error_message))
  51409. throw CImgInstanceException(_cimg_instance
  51410. "save_off(): Invalid specified 3d object, for file '%s' (%s).",
  51411. cimg_instance,
  51412. filename?filename:"(FILE*)",error_message.data());
  51413. const CImg<tc> default_color(1,3,1,1,200);
  51414. std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
  51415. unsigned int supported_primitives = 0;
  51416. cimglist_for(primitives,l) if (primitives[l].size()!=5) ++supported_primitives;
  51417. std::fprintf(nfile,"OFF\n%u %u %u\n",_width,supported_primitives,3*primitives._width);
  51418. cimg_forX(*this,i) std::fprintf(nfile,"%f %f %f\n",
  51419. (float)((*this)(i,0)),(float)((*this)(i,1)),(float)((*this)(i,2)));
  51420. cimglist_for(primitives,l) {
  51421. const CImg<tc>& color = l<colors.width()?colors[l]:default_color;
  51422. const unsigned int psiz = primitives[l].size(), csiz = color.size();
  51423. const float r = color[0]/255.0f, g = (csiz>1?color[1]:r)/255.0f, b = (csiz>2?color[2]:g)/255.0f;
  51424. switch (psiz) {
  51425. case 1 : std::fprintf(nfile,"1 %u %f %f %f\n",
  51426. (unsigned int)primitives(l,0),r,g,b); break;
  51427. case 2 : std::fprintf(nfile,"2 %u %u %f %f %f\n",
  51428. (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break;
  51429. case 3 : std::fprintf(nfile,"3 %u %u %u %f %f %f\n",
  51430. (unsigned int)primitives(l,0),(unsigned int)primitives(l,2),
  51431. (unsigned int)primitives(l,1),r,g,b); break;
  51432. case 4 : std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n",
  51433. (unsigned int)primitives(l,0),(unsigned int)primitives(l,3),
  51434. (unsigned int)primitives(l,2),(unsigned int)primitives(l,1),r,g,b); break;
  51435. case 5 : std::fprintf(nfile,"2 %u %u %f %f %f\n",
  51436. (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break;
  51437. case 6 : {
  51438. const unsigned int xt = (unsigned int)primitives(l,2), yt = (unsigned int)primitives(l,3);
  51439. const float
  51440. rt = color.atXY(xt,yt,0)/255.0f,
  51441. gt = (csiz>1?color.atXY(xt,yt,1):r)/255.0f,
  51442. bt = (csiz>2?color.atXY(xt,yt,2):g)/255.0f;
  51443. std::fprintf(nfile,"2 %u %u %f %f %f\n",
  51444. (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),rt,gt,bt);
  51445. } break;
  51446. case 9 : {
  51447. const unsigned int xt = (unsigned int)primitives(l,3), yt = (unsigned int)primitives(l,4);
  51448. const float
  51449. rt = color.atXY(xt,yt,0)/255.0f,
  51450. gt = (csiz>1?color.atXY(xt,yt,1):r)/255.0f,
  51451. bt = (csiz>2?color.atXY(xt,yt,2):g)/255.0f;
  51452. std::fprintf(nfile,"3 %u %u %u %f %f %f\n",
  51453. (unsigned int)primitives(l,0),(unsigned int)primitives(l,2),
  51454. (unsigned int)primitives(l,1),rt,gt,bt);
  51455. } break;
  51456. case 12 : {
  51457. const unsigned int xt = (unsigned int)primitives(l,4), yt = (unsigned int)primitives(l,5);
  51458. const float
  51459. rt = color.atXY(xt,yt,0)/255.0f,
  51460. gt = (csiz>1?color.atXY(xt,yt,1):r)/255.0f,
  51461. bt = (csiz>2?color.atXY(xt,yt,2):g)/255.0f;
  51462. std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n",
  51463. (unsigned int)primitives(l,0),(unsigned int)primitives(l,3),
  51464. (unsigned int)primitives(l,2),(unsigned int)primitives(l,1),rt,gt,bt);
  51465. } break;
  51466. }
  51467. }
  51468. if (!file) cimg::fclose(nfile);
  51469. return *this;
  51470. }
  51471. //! Save volumetric image as a video, using the OpenCV library.
  51472. /**
  51473. \param filename Filename to write data to.
  51474. \param fps Number of frames per second.
  51475. \param codec Type of compression (See http://www.fourcc.org/codecs.php to see available codecs).
  51476. \param keep_open Tells if the video writer associated to the specified filename
  51477. must be kept open or not (to allow frames to be added in the same file afterwards).
  51478. **/
  51479. const CImg<T>& save_video(const char *const filename, const unsigned int fps=25,
  51480. const char *codec=0, const bool keep_open=false) const {
  51481. if (is_empty()) { CImgList<T>().save_video(filename,fps,codec,keep_open); return *this; }
  51482. CImgList<T> list;
  51483. get_split('z').move_to(list);
  51484. list.save_video(filename,fps,codec,keep_open);
  51485. return *this;
  51486. }
  51487. //! Save volumetric image as a video, using ffmpeg external binary.
  51488. /**
  51489. \param filename Filename, as a C-string.
  51490. \param fps Video framerate.
  51491. \param codec Video codec, as a C-string.
  51492. \param bitrate Video bitrate.
  51493. \note
  51494. - Each slice of the instance image is considered to be a single frame of the output video file.
  51495. - This method uses \c ffmpeg, an external executable binary provided by
  51496. <a href="http://www.ffmpeg.org">FFmpeg</a>.
  51497. It must be installed for the method to succeed.
  51498. **/
  51499. const CImg<T>& save_ffmpeg_external(const char *const filename, const unsigned int fps=25,
  51500. const char *const codec=0, const unsigned int bitrate=2048) const {
  51501. if (!filename)
  51502. throw CImgArgumentException(_cimg_instance
  51503. "save_ffmpeg_external(): Specified filename is (null).",
  51504. cimg_instance);
  51505. if (is_empty()) { cimg::fempty(0,filename); return *this; }
  51506. CImgList<T> list;
  51507. get_split('z').move_to(list);
  51508. list.save_ffmpeg_external(filename,fps,codec,bitrate);
  51509. return *this;
  51510. }
  51511. //! Save image using gzip external binary.
  51512. /**
  51513. \param filename Filename, as a C-string.
  51514. \note This method uses \c gzip, an external executable binary provided by
  51515. <a href="//http://www.gzip.org">gzip</a>.
  51516. It must be installed for the method to succeed.
  51517. **/
  51518. const CImg<T>& save_gzip_external(const char *const filename) const {
  51519. if (!filename)
  51520. throw CImgArgumentException(_cimg_instance
  51521. "save_gzip_external(): Specified filename is (null).",
  51522. cimg_instance);
  51523. if (is_empty()) { cimg::fempty(0,filename); return *this; }
  51524. CImg<charT> command(1024), filename_tmp(256), body(256);
  51525. const char
  51526. *ext = cimg::split_filename(filename,body),
  51527. *ext2 = cimg::split_filename(body,0);
  51528. std::FILE *file;
  51529. do {
  51530. if (!cimg::strcasecmp(ext,"gz")) {
  51531. if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
  51532. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
  51533. else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg",
  51534. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
  51535. } else {
  51536. if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
  51537. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
  51538. else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg",
  51539. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
  51540. }
  51541. if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
  51542. } while (file);
  51543. save(filename_tmp);
  51544. cimg_snprintf(command,command._width,"%s -c \"%s\" > \"%s\"",
  51545. cimg::gzip_path(),
  51546. CImg<charT>::string(filename_tmp)._system_strescape().data(),
  51547. CImg<charT>::string(filename)._system_strescape().data());
  51548. cimg::system(command);
  51549. file = std_fopen(filename,"rb");
  51550. if (!file)
  51551. throw CImgIOException(_cimg_instance
  51552. "save_gzip_external(): Failed to save file '%s' with external command 'gzip'.",
  51553. cimg_instance,
  51554. filename);
  51555. else cimg::fclose(file);
  51556. std::remove(filename_tmp);
  51557. return *this;
  51558. }
  51559. //! Save image using GraphicsMagick's external binary.
  51560. /**
  51561. \param filename Filename, as a C-string.
  51562. \param quality Image quality (expressed in percent), when the file format supports it.
  51563. \note This method uses \c gm, an external executable binary provided by
  51564. <a href="http://www.graphicsmagick.org">GraphicsMagick</a>.
  51565. It must be installed for the method to succeed.
  51566. **/
  51567. const CImg<T>& save_graphicsmagick_external(const char *const filename, const unsigned int quality=100) const {
  51568. if (!filename)
  51569. throw CImgArgumentException(_cimg_instance
  51570. "save_graphicsmagick_external(): Specified filename is (null).",
  51571. cimg_instance);
  51572. if (is_empty()) { cimg::fempty(0,filename); return *this; }
  51573. if (_depth>1)
  51574. cimg::warn(_cimg_instance
  51575. "save_other(): File '%s', saving a volumetric image with an external call to "
  51576. "GraphicsMagick only writes the first image slice.",
  51577. cimg_instance,filename);
  51578. #ifdef cimg_use_png
  51579. #define _cimg_sge_ext1 "png"
  51580. #define _cimg_sge_ext2 "png"
  51581. #else
  51582. #define _cimg_sge_ext1 "pgm"
  51583. #define _cimg_sge_ext2 "ppm"
  51584. #endif
  51585. CImg<charT> command(1024), filename_tmp(256);
  51586. std::FILE *file;
  51587. do {
  51588. cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
  51589. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),
  51590. _spectrum==1?_cimg_sge_ext1:_cimg_sge_ext2);
  51591. if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
  51592. } while (file);
  51593. #ifdef cimg_use_png
  51594. save_png(filename_tmp);
  51595. #else
  51596. save_pnm(filename_tmp);
  51597. #endif
  51598. cimg_snprintf(command,command._width,"%s convert -quality %u \"%s\" \"%s\"",
  51599. cimg::graphicsmagick_path(),quality,
  51600. CImg<charT>::string(filename_tmp)._system_strescape().data(),
  51601. CImg<charT>::string(filename)._system_strescape().data());
  51602. cimg::system(command);
  51603. file = std_fopen(filename,"rb");
  51604. if (!file)
  51605. throw CImgIOException(_cimg_instance
  51606. "save_graphicsmagick_external(): Failed to save file '%s' with external command 'gm'.",
  51607. cimg_instance,
  51608. filename);
  51609. if (file) cimg::fclose(file);
  51610. std::remove(filename_tmp);
  51611. return *this;
  51612. }
  51613. //! Save image using ImageMagick's external binary.
  51614. /**
  51615. \param filename Filename, as a C-string.
  51616. \param quality Image quality (expressed in percent), when the file format supports it.
  51617. \note This method uses \c convert, an external executable binary provided by
  51618. <a href="http://www.imagemagick.org">ImageMagick</a>.
  51619. It must be installed for the method to succeed.
  51620. **/
  51621. const CImg<T>& save_imagemagick_external(const char *const filename, const unsigned int quality=100) const {
  51622. if (!filename)
  51623. throw CImgArgumentException(_cimg_instance
  51624. "save_imagemagick_external(): Specified filename is (null).",
  51625. cimg_instance);
  51626. if (is_empty()) { cimg::fempty(0,filename); return *this; }
  51627. if (_depth>1)
  51628. cimg::warn(_cimg_instance
  51629. "save_other(): File '%s', saving a volumetric image with an external call to "
  51630. "ImageMagick only writes the first image slice.",
  51631. cimg_instance,filename);
  51632. #ifdef cimg_use_png
  51633. #define _cimg_sie_ext1 "png"
  51634. #define _cimg_sie_ext2 "png"
  51635. #else
  51636. #define _cimg_sie_ext1 "pgm"
  51637. #define _cimg_sie_ext2 "ppm"
  51638. #endif
  51639. CImg<charT> command(1024), filename_tmp(256);
  51640. std::FILE *file;
  51641. do {
  51642. cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",cimg::temporary_path(),
  51643. cimg_file_separator,cimg::filenamerand(),_spectrum==1?_cimg_sie_ext1:_cimg_sie_ext2);
  51644. if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
  51645. } while (file);
  51646. #ifdef cimg_use_png
  51647. save_png(filename_tmp);
  51648. #else
  51649. save_pnm(filename_tmp);
  51650. #endif
  51651. cimg_snprintf(command,command._width,"%s -quality %u \"%s\" \"%s\"",
  51652. cimg::imagemagick_path(),quality,
  51653. CImg<charT>::string(filename_tmp)._system_strescape().data(),
  51654. CImg<charT>::string(filename)._system_strescape().data());
  51655. cimg::system(command);
  51656. file = std_fopen(filename,"rb");
  51657. if (!file)
  51658. throw CImgIOException(_cimg_instance
  51659. "save_imagemagick_external(): Failed to save file '%s' with "
  51660. "external command 'magick/convert'.",
  51661. cimg_instance,
  51662. filename);
  51663. if (file) cimg::fclose(file);
  51664. std::remove(filename_tmp);
  51665. return *this;
  51666. }
  51667. //! Save image as a Dicom file.
  51668. /**
  51669. \param filename Filename, as a C-string.
  51670. \note This method uses \c medcon, an external executable binary provided by
  51671. <a href="http://xmedcon.sourceforge.net">(X)Medcon</a>.
  51672. It must be installed for the method to succeed.
  51673. **/
  51674. const CImg<T>& save_medcon_external(const char *const filename) const {
  51675. if (!filename)
  51676. throw CImgArgumentException(_cimg_instance
  51677. "save_medcon_external(): Specified filename is (null).",
  51678. cimg_instance);
  51679. if (is_empty()) { cimg::fempty(0,filename); return *this; }
  51680. CImg<charT> command(1024), filename_tmp(256), body(256);
  51681. std::FILE *file;
  51682. do {
  51683. cimg_snprintf(filename_tmp,filename_tmp._width,"%s.hdr",cimg::filenamerand());
  51684. if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
  51685. } while (file);
  51686. save_analyze(filename_tmp);
  51687. cimg_snprintf(command,command._width,"%s -w -c dicom -o \"%s\" -f \"%s\"",
  51688. cimg::medcon_path(),
  51689. CImg<charT>::string(filename)._system_strescape().data(),
  51690. CImg<charT>::string(filename_tmp)._system_strescape().data());
  51691. cimg::system(command);
  51692. std::remove(filename_tmp);
  51693. cimg::split_filename(filename_tmp,body);
  51694. cimg_snprintf(filename_tmp,filename_tmp._width,"%s.img",body._data);
  51695. std::remove(filename_tmp);
  51696. file = std_fopen(filename,"rb");
  51697. if (!file) {
  51698. cimg_snprintf(command,command._width,"m000-%s",filename);
  51699. file = std_fopen(command,"rb");
  51700. if (!file) {
  51701. cimg::fclose(cimg::fopen(filename,"r"));
  51702. throw CImgIOException(_cimg_instance
  51703. "save_medcon_external(): Failed to save file '%s' with external command 'medcon'.",
  51704. cimg_instance,
  51705. filename);
  51706. }
  51707. }
  51708. cimg::fclose(file);
  51709. std::rename(command,filename);
  51710. return *this;
  51711. }
  51712. // Save image for non natively supported formats.
  51713. /**
  51714. \param filename Filename, as a C-string.
  51715. \param quality Image quality (expressed in percent), when the file format supports it.
  51716. \note
  51717. - The filename extension tells about the desired file format.
  51718. - This method tries to save the instance image as a file, using external tools from
  51719. <a href="http://www.imagemagick.org">ImageMagick</a> or
  51720. <a href="http://www.graphicsmagick.org">GraphicsMagick</a>.
  51721. At least one of these tool must be installed for the method to succeed.
  51722. - It is recommended to use the generic method save(const char*, int) const instead,
  51723. as it can handle some file formats natively.
  51724. **/
  51725. const CImg<T>& save_other(const char *const filename, const unsigned int quality=100) const {
  51726. if (!filename)
  51727. throw CImgArgumentException(_cimg_instance
  51728. "save_other(): Specified filename is (null).",
  51729. cimg_instance);
  51730. if (is_empty()) { cimg::fempty(0,filename); return *this; }
  51731. if (_depth>1)
  51732. cimg::warn(_cimg_instance
  51733. "save_other(): File '%s', saving a volumetric image with an external call to "
  51734. "ImageMagick or GraphicsMagick only writes the first image slice.",
  51735. cimg_instance,filename);
  51736. const unsigned int omode = cimg::exception_mode();
  51737. bool is_saved = true;
  51738. cimg::exception_mode(0);
  51739. try { save_magick(filename); }
  51740. catch (CImgException&) {
  51741. try { save_imagemagick_external(filename,quality); }
  51742. catch (CImgException&) {
  51743. try { save_graphicsmagick_external(filename,quality); }
  51744. catch (CImgException&) {
  51745. is_saved = false;
  51746. }
  51747. }
  51748. }
  51749. cimg::exception_mode(omode);
  51750. if (!is_saved)
  51751. throw CImgIOException(_cimg_instance
  51752. "save_other(): Failed to save file '%s'. Format is not natively supported, "
  51753. "and no external commands succeeded.",
  51754. cimg_instance,
  51755. filename);
  51756. return *this;
  51757. }
  51758. //! Serialize a CImg<T> instance into a raw CImg<unsigned char> buffer.
  51759. /**
  51760. \param is_compressed tells if zlib compression must be used for serialization
  51761. (this requires 'cimg_use_zlib' been enabled).
  51762. **/
  51763. CImg<ucharT> get_serialize(const bool is_compressed=false) const {
  51764. return CImgList<T>(*this,true).get_serialize(is_compressed);
  51765. }
  51766. // [internal] Return a 40x38 color logo of a 'danger' item.
  51767. static CImg<T> _logo40x38() {
  51768. CImg<T> res(40,38,1,3);
  51769. const unsigned char *ptrs = cimg::logo40x38;
  51770. T *ptr1 = res.data(0,0,0,0), *ptr2 = res.data(0,0,0,1), *ptr3 = res.data(0,0,0,2);
  51771. for (ulongT off = 0; off<(ulongT)res._width*res._height;) {
  51772. const unsigned char n = *(ptrs++), r = *(ptrs++), g = *(ptrs++), b = *(ptrs++);
  51773. for (unsigned int l = 0; l<n; ++off, ++l) { *(ptr1++) = (T)r; *(ptr2++) = (T)g; *(ptr3++) = (T)b; }
  51774. }
  51775. return res;
  51776. }
  51777. //@}
  51778. };
  51779. /*
  51780. #-----------------------------------------
  51781. #
  51782. #
  51783. #
  51784. # Definition of the CImgList<T> structure
  51785. #
  51786. #
  51787. #
  51788. #------------------------------------------
  51789. */
  51790. //! Represent a list of images CImg<T>.
  51791. template<typename T>
  51792. struct CImgList {
  51793. unsigned int _width, _allocated_width;
  51794. CImg<T> *_data;
  51795. //! Simple iterator type, to loop through each image of a list.
  51796. /**
  51797. \note
  51798. - The \c CImgList<T>::iterator type is defined as a <tt>CImg<T>*</tt>.
  51799. - You may use it like this:
  51800. \code
  51801. CImgList<> list; // Assuming this image list is not empty.
  51802. for (CImgList<>::iterator it = list.begin(); it<list.end(); ++it) (*it).mirror('x');
  51803. \endcode
  51804. - Using the loop macro \c cimglist_for is another (more concise) alternative:
  51805. \code
  51806. cimglist_for(list,l) list[l].mirror('x');
  51807. \endcode
  51808. **/
  51809. typedef CImg<T>* iterator;
  51810. //! Simple const iterator type, to loop through each image of a \c const list instance.
  51811. /**
  51812. \note
  51813. - The \c CImgList<T>::const_iterator type is defined to be a <tt>const CImg<T>*</tt>.
  51814. - Similar to CImgList<T>::iterator, but for constant list instances.
  51815. **/
  51816. typedef const CImg<T>* const_iterator;
  51817. //! Pixel value type.
  51818. /**
  51819. Refer to the pixels value type of the images in the list.
  51820. \note
  51821. - The \c CImgList<T>::value_type type of a \c CImgList<T> is defined to be a \c T.
  51822. It is then similar to CImg<T>::value_type.
  51823. - \c CImgList<T>::value_type is actually not used in %CImg methods. It has been mainly defined for
  51824. compatibility with STL naming conventions.
  51825. **/
  51826. typedef T value_type;
  51827. // Define common types related to template type T.
  51828. typedef typename cimg::superset<T,bool>::type Tbool;
  51829. typedef typename cimg::superset<T,unsigned char>::type Tuchar;
  51830. typedef typename cimg::superset<T,char>::type Tchar;
  51831. typedef typename cimg::superset<T,unsigned short>::type Tushort;
  51832. typedef typename cimg::superset<T,short>::type Tshort;
  51833. typedef typename cimg::superset<T,unsigned int>::type Tuint;
  51834. typedef typename cimg::superset<T,int>::type Tint;
  51835. typedef typename cimg::superset<T,cimg_ulong>::type Tulong;
  51836. typedef typename cimg::superset<T,cimg_long>::type Tlong;
  51837. typedef typename cimg::superset<T,float>::type Tfloat;
  51838. typedef typename cimg::superset<T,double>::type Tdouble;
  51839. typedef typename cimg::last<T,bool>::type boolT;
  51840. typedef typename cimg::last<T,unsigned char>::type ucharT;
  51841. typedef typename cimg::last<T,char>::type charT;
  51842. typedef typename cimg::last<T,unsigned short>::type ushortT;
  51843. typedef typename cimg::last<T,short>::type shortT;
  51844. typedef typename cimg::last<T,unsigned int>::type uintT;
  51845. typedef typename cimg::last<T,int>::type intT;
  51846. typedef typename cimg::last<T,cimg_ulong>::type ulongT;
  51847. typedef typename cimg::last<T,cimg_long>::type longT;
  51848. typedef typename cimg::last<T,cimg_uint64>::type uint64T;
  51849. typedef typename cimg::last<T,cimg_int64>::type int64T;
  51850. typedef typename cimg::last<T,float>::type floatT;
  51851. typedef typename cimg::last<T,double>::type doubleT;
  51852. //@}
  51853. //---------------------------
  51854. //
  51855. //! \name Plugins
  51856. //@{
  51857. //---------------------------
  51858. #ifdef cimglist_plugin
  51859. #include cimglist_plugin
  51860. #endif
  51861. #ifdef cimglist_plugin1
  51862. #include cimglist_plugin1
  51863. #endif
  51864. #ifdef cimglist_plugin2
  51865. #include cimglist_plugin2
  51866. #endif
  51867. #ifdef cimglist_plugin3
  51868. #include cimglist_plugin3
  51869. #endif
  51870. #ifdef cimglist_plugin4
  51871. #include cimglist_plugin4
  51872. #endif
  51873. #ifdef cimglist_plugin5
  51874. #include cimglist_plugin5
  51875. #endif
  51876. #ifdef cimglist_plugin6
  51877. #include cimglist_plugin6
  51878. #endif
  51879. #ifdef cimglist_plugin7
  51880. #include cimglist_plugin7
  51881. #endif
  51882. #ifdef cimglist_plugin8
  51883. #include cimglist_plugin8
  51884. #endif
  51885. //@}
  51886. //--------------------------------------------------------
  51887. //
  51888. //! \name Constructors / Destructor / Instance Management
  51889. //@{
  51890. //--------------------------------------------------------
  51891. //! Destructor.
  51892. /**
  51893. Destroy current list instance.
  51894. \note
  51895. - Any allocated buffer is deallocated.
  51896. - Destroying an empty list does nothing actually.
  51897. **/
  51898. ~CImgList() {
  51899. delete[] _data;
  51900. }
  51901. //! Default constructor.
  51902. /**
  51903. Construct a new empty list instance.
  51904. \note
  51905. - An empty list has no pixel data and its dimension width() is set to \c 0, as well as its
  51906. image buffer pointer data().
  51907. - An empty list may be reassigned afterwards, with the family of the assign() methods.
  51908. In all cases, the type of pixels stays \c T.
  51909. **/
  51910. CImgList():
  51911. _width(0),_allocated_width(0),_data(0) {}
  51912. //! Construct list containing empty images.
  51913. /**
  51914. \param n Number of empty images.
  51915. \note Useful when you know by advance the number of images you want to manage, as
  51916. it will allocate the right amount of memory for the list, without needs for reallocation
  51917. (that may occur when starting from an empty list and inserting several images in it).
  51918. **/
  51919. explicit CImgList(const unsigned int n):_width(n) {
  51920. if (n) _data = new CImg<T>[_allocated_width = std::max(16U,(unsigned int)cimg::nearest_pow2(n))];
  51921. else { _allocated_width = 0; _data = 0; }
  51922. }
  51923. //! Construct list containing images of specified size.
  51924. /**
  51925. \param n Number of images.
  51926. \param width Width of images.
  51927. \param height Height of images.
  51928. \param depth Depth of images.
  51929. \param spectrum Number of channels of images.
  51930. \note Pixel values are not initialized and may probably contain garbage.
  51931. **/
  51932. CImgList(const unsigned int n, const unsigned int width, const unsigned int height=1,
  51933. const unsigned int depth=1, const unsigned int spectrum=1):
  51934. _width(0),_allocated_width(0),_data(0) {
  51935. assign(n);
  51936. cimglist_apply(*this,assign)(width,height,depth,spectrum);
  51937. }
  51938. //! Construct list containing images of specified size, and initialize pixel values.
  51939. /**
  51940. \param n Number of images.
  51941. \param width Width of images.
  51942. \param height Height of images.
  51943. \param depth Depth of images.
  51944. \param spectrum Number of channels of images.
  51945. \param val Initialization value for images pixels.
  51946. **/
  51947. CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
  51948. const unsigned int depth, const unsigned int spectrum, const T& val):
  51949. _width(0),_allocated_width(0),_data(0) {
  51950. assign(n);
  51951. cimglist_apply(*this,assign)(width,height,depth,spectrum,val);
  51952. }
  51953. //! Construct list containing images of specified size, and initialize pixel values from a sequence of integers.
  51954. /**
  51955. \param n Number of images.
  51956. \param width Width of images.
  51957. \param height Height of images.
  51958. \param depth Depth of images.
  51959. \param spectrum Number of channels of images.
  51960. \param val0 First value of the initializing integers sequence.
  51961. \param val1 Second value of the initializing integers sequence.
  51962. \warning You must specify at least <tt>width*height*depth*spectrum</tt> values in your argument list,
  51963. or you will probably segfault.
  51964. **/
  51965. CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
  51966. const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...):
  51967. _width(0),_allocated_width(0),_data(0) {
  51968. #define _CImgList_stdarg(t) { \
  51969. assign(n,width,height,depth,spectrum); \
  51970. const ulongT siz = (ulongT)width*height*depth*spectrum, nsiz = siz*n; \
  51971. T *ptrd = _data->_data; \
  51972. va_list ap; \
  51973. va_start(ap,val1); \
  51974. for (ulongT l = 0, s = 0, i = 0; i<nsiz; ++i) { \
  51975. *(ptrd++) = (T)(i==0?val0:(i==1?val1:va_arg(ap,t))); \
  51976. if ((++s)==siz) { ptrd = _data[++l]._data; s = 0; } \
  51977. } \
  51978. va_end(ap); \
  51979. }
  51980. _CImgList_stdarg(int);
  51981. }
  51982. //! Construct list containing images of specified size, and initialize pixel values from a sequence of doubles.
  51983. /**
  51984. \param n Number of images.
  51985. \param width Width of images.
  51986. \param height Height of images.
  51987. \param depth Depth of images.
  51988. \param spectrum Number of channels of images.
  51989. \param val0 First value of the initializing doubles sequence.
  51990. \param val1 Second value of the initializing doubles sequence.
  51991. \warning You must specify at least <tt>width*height*depth*spectrum</tt> values in your argument list,
  51992. or you will probably segfault.
  51993. **/
  51994. CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
  51995. const unsigned int depth, const unsigned int spectrum, const double val0, const double val1, ...):
  51996. _width(0),_allocated_width(0),_data(0) {
  51997. _CImgList_stdarg(double);
  51998. }
  51999. //! Construct list containing copies of an input image.
  52000. /**
  52001. \param n Number of images.
  52002. \param img Input image to copy in the constructed list.
  52003. \param is_shared Tells if the elements of the list are shared or non-shared copies of \c img.
  52004. **/
  52005. template<typename t>
  52006. CImgList(const unsigned int n, const CImg<t>& img, const bool is_shared=false):
  52007. _width(0),_allocated_width(0),_data(0) {
  52008. assign(n);
  52009. cimglist_apply(*this,assign)(img,is_shared);
  52010. }
  52011. //! Construct list from one image.
  52012. /**
  52013. \param img Input image to copy in the constructed list.
  52014. \param is_shared Tells if the element of the list is a shared or non-shared copy of \c img.
  52015. **/
  52016. template<typename t>
  52017. explicit CImgList(const CImg<t>& img, const bool is_shared=false):
  52018. _width(0),_allocated_width(0),_data(0) {
  52019. assign(1);
  52020. _data[0].assign(img,is_shared);
  52021. }
  52022. //! Construct list from two images.
  52023. /**
  52024. \param img1 First input image to copy in the constructed list.
  52025. \param img2 Second input image to copy in the constructed list.
  52026. \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
  52027. **/
  52028. template<typename t1, typename t2>
  52029. CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const bool is_shared=false):
  52030. _width(0),_allocated_width(0),_data(0) {
  52031. assign(2);
  52032. _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared);
  52033. }
  52034. //! Construct list from three images.
  52035. /**
  52036. \param img1 First input image to copy in the constructed list.
  52037. \param img2 Second input image to copy in the constructed list.
  52038. \param img3 Third input image to copy in the constructed list.
  52039. \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
  52040. **/
  52041. template<typename t1, typename t2, typename t3>
  52042. CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const bool is_shared=false):
  52043. _width(0),_allocated_width(0),_data(0) {
  52044. assign(3);
  52045. _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
  52046. }
  52047. //! Construct list from four images.
  52048. /**
  52049. \param img1 First input image to copy in the constructed list.
  52050. \param img2 Second input image to copy in the constructed list.
  52051. \param img3 Third input image to copy in the constructed list.
  52052. \param img4 Fourth input image to copy in the constructed list.
  52053. \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
  52054. **/
  52055. template<typename t1, typename t2, typename t3, typename t4>
  52056. CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
  52057. const bool is_shared=false):
  52058. _width(0),_allocated_width(0),_data(0) {
  52059. assign(4);
  52060. _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
  52061. _data[3].assign(img4,is_shared);
  52062. }
  52063. //! Construct list from five images.
  52064. /**
  52065. \param img1 First input image to copy in the constructed list.
  52066. \param img2 Second input image to copy in the constructed list.
  52067. \param img3 Third input image to copy in the constructed list.
  52068. \param img4 Fourth input image to copy in the constructed list.
  52069. \param img5 Fifth input image to copy in the constructed list.
  52070. \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
  52071. **/
  52072. template<typename t1, typename t2, typename t3, typename t4, typename t5>
  52073. CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
  52074. const CImg<t5>& img5, const bool is_shared=false):
  52075. _width(0),_allocated_width(0),_data(0) {
  52076. assign(5);
  52077. _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
  52078. _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared);
  52079. }
  52080. //! Construct list from six images.
  52081. /**
  52082. \param img1 First input image to copy in the constructed list.
  52083. \param img2 Second input image to copy in the constructed list.
  52084. \param img3 Third input image to copy in the constructed list.
  52085. \param img4 Fourth input image to copy in the constructed list.
  52086. \param img5 Fifth input image to copy in the constructed list.
  52087. \param img6 Sixth input image to copy in the constructed list.
  52088. \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
  52089. **/
  52090. template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6>
  52091. CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
  52092. const CImg<t5>& img5, const CImg<t6>& img6, const bool is_shared=false):
  52093. _width(0),_allocated_width(0),_data(0) {
  52094. assign(6);
  52095. _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
  52096. _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
  52097. }
  52098. //! Construct list from seven images.
  52099. /**
  52100. \param img1 First input image to copy in the constructed list.
  52101. \param img2 Second input image to copy in the constructed list.
  52102. \param img3 Third input image to copy in the constructed list.
  52103. \param img4 Fourth input image to copy in the constructed list.
  52104. \param img5 Fifth input image to copy in the constructed list.
  52105. \param img6 Sixth input image to copy in the constructed list.
  52106. \param img7 Seventh input image to copy in the constructed list.
  52107. \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
  52108. **/
  52109. template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7>
  52110. CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
  52111. const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const bool is_shared=false):
  52112. _width(0),_allocated_width(0),_data(0) {
  52113. assign(7);
  52114. _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
  52115. _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
  52116. _data[6].assign(img7,is_shared);
  52117. }
  52118. //! Construct list from eight images.
  52119. /**
  52120. \param img1 First input image to copy in the constructed list.
  52121. \param img2 Second input image to copy in the constructed list.
  52122. \param img3 Third input image to copy in the constructed list.
  52123. \param img4 Fourth input image to copy in the constructed list.
  52124. \param img5 Fifth input image to copy in the constructed list.
  52125. \param img6 Sixth input image to copy in the constructed list.
  52126. \param img7 Seventh input image to copy in the constructed list.
  52127. \param img8 Eighth input image to copy in the constructed list.
  52128. \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
  52129. **/
  52130. template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7, typename t8>
  52131. CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
  52132. const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const CImg<t8>& img8,
  52133. const bool is_shared=false):
  52134. _width(0),_allocated_width(0),_data(0) {
  52135. assign(8);
  52136. _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
  52137. _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
  52138. _data[6].assign(img7,is_shared); _data[7].assign(img8,is_shared);
  52139. }
  52140. //! Construct list copy.
  52141. /**
  52142. \param list Input list to copy.
  52143. \note The shared state of each element of the constructed list is kept the same as in \c list.
  52144. **/
  52145. template<typename t>
  52146. CImgList(const CImgList<t>& list):_width(0),_allocated_width(0),_data(0) {
  52147. assign(list._width);
  52148. cimglist_for(*this,l) _data[l].assign(list[l],false);
  52149. }
  52150. //! Construct list copy \specialization.
  52151. CImgList(const CImgList<T>& list):_width(0),_allocated_width(0),_data(0) {
  52152. assign(list._width);
  52153. cimglist_for(*this,l) _data[l].assign(list[l],list[l]._is_shared);
  52154. }
  52155. //! Construct list copy, and force the shared state of the list elements.
  52156. /**
  52157. \param list Input list to copy.
  52158. \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
  52159. **/
  52160. template<typename t>
  52161. CImgList(const CImgList<t>& list, const bool is_shared):_width(0),_allocated_width(0),_data(0) {
  52162. assign(list._width);
  52163. cimglist_for(*this,l) _data[l].assign(list[l],is_shared);
  52164. }
  52165. //! Construct list by reading the content of a file.
  52166. /**
  52167. \param filename Filename, as a C-string.
  52168. **/
  52169. explicit CImgList(const char *const filename):_width(0),_allocated_width(0),_data(0) {
  52170. assign(filename);
  52171. }
  52172. //! Construct list from the content of a display window.
  52173. /**
  52174. \param disp Display window to get content from.
  52175. \note Constructed list contains a single image only.
  52176. **/
  52177. explicit CImgList(const CImgDisplay& disp):_width(0),_allocated_width(0),_data(0) {
  52178. assign(disp);
  52179. }
  52180. //! Return a list with elements being shared copies of images in the list instance.
  52181. /**
  52182. \note <tt>list2 = list1.get_shared()</tt> is equivalent to <tt>list2.assign(list1,true)</tt>.
  52183. **/
  52184. CImgList<T> get_shared() {
  52185. CImgList<T> res(_width);
  52186. cimglist_for(*this,l) res[l].assign(_data[l],true);
  52187. return res;
  52188. }
  52189. //! Return a list with elements being shared copies of images in the list instance \const.
  52190. const CImgList<T> get_shared() const {
  52191. CImgList<T> res(_width);
  52192. cimglist_for(*this,l) res[l].assign(_data[l],true);
  52193. return res;
  52194. }
  52195. //! Destructor \inplace.
  52196. /**
  52197. \see CImgList().
  52198. **/
  52199. CImgList<T>& assign() {
  52200. delete[] _data;
  52201. _width = _allocated_width = 0;
  52202. _data = 0;
  52203. return *this;
  52204. }
  52205. //! Destructor \inplace.
  52206. /**
  52207. Equivalent to assign().
  52208. \note Only here for compatibility with STL naming conventions.
  52209. **/
  52210. CImgList<T>& clear() {
  52211. return assign();
  52212. }
  52213. //! Construct list containing empty images \inplace.
  52214. /**
  52215. \see CImgList(unsigned int).
  52216. **/
  52217. CImgList<T>& assign(const unsigned int n) {
  52218. if (!n) return assign();
  52219. if (_allocated_width<n || _allocated_width>(n<<2)) {
  52220. delete[] _data;
  52221. _data = new CImg<T>[_allocated_width = std::max(16U,(unsigned int)cimg::nearest_pow2(n))];
  52222. }
  52223. _width = n;
  52224. return *this;
  52225. }
  52226. //! Construct list containing images of specified size \inplace.
  52227. /**
  52228. \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int).
  52229. **/
  52230. CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height=1,
  52231. const unsigned int depth=1, const unsigned int spectrum=1) {
  52232. assign(n);
  52233. cimglist_apply(*this,assign)(width,height,depth,spectrum);
  52234. return *this;
  52235. }
  52236. //! Construct list containing images of specified size, and initialize pixel values \inplace.
  52237. /**
  52238. \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, const T).
  52239. **/
  52240. CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
  52241. const unsigned int depth, const unsigned int spectrum, const T& val) {
  52242. assign(n);
  52243. cimglist_apply(*this,assign)(width,height,depth,spectrum,val);
  52244. return *this;
  52245. }
  52246. //! Construct list with images of specified size, and initialize pixel values from a sequence of integers \inplace.
  52247. /**
  52248. \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, const int, const int, ...).
  52249. **/
  52250. CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
  52251. const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...) {
  52252. _CImgList_stdarg(int);
  52253. return *this;
  52254. }
  52255. //! Construct list with images of specified size, and initialize pixel values from a sequence of doubles \inplace.
  52256. /**
  52257. \see CImgList(unsigned int,unsigned int,unsigned int,unsigned int,unsigned int,const double,const double,...).
  52258. **/
  52259. CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
  52260. const unsigned int depth, const unsigned int spectrum,
  52261. const double val0, const double val1, ...) {
  52262. _CImgList_stdarg(double);
  52263. return *this;
  52264. }
  52265. //! Construct list containing copies of an input image \inplace.
  52266. /**
  52267. \see CImgList(unsigned int, const CImg<t>&, bool).
  52268. **/
  52269. template<typename t>
  52270. CImgList<T>& assign(const unsigned int n, const CImg<t>& img, const bool is_shared=false) {
  52271. assign(n);
  52272. cimglist_apply(*this,assign)(img,is_shared);
  52273. return *this;
  52274. }
  52275. //! Construct list from one image \inplace.
  52276. /**
  52277. \see CImgList(const CImg<t>&, bool).
  52278. **/
  52279. template<typename t>
  52280. CImgList<T>& assign(const CImg<t>& img, const bool is_shared=false) {
  52281. assign(1);
  52282. _data[0].assign(img,is_shared);
  52283. return *this;
  52284. }
  52285. //! Construct list from two images \inplace.
  52286. /**
  52287. \see CImgList(const CImg<t>&, const CImg<t>&, bool).
  52288. **/
  52289. template<typename t1, typename t2>
  52290. CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const bool is_shared=false) {
  52291. assign(2);
  52292. _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared);
  52293. return *this;
  52294. }
  52295. //! Construct list from three images \inplace.
  52296. /**
  52297. \see CImgList(const CImg<t>&, const CImg<t>&, const CImg<t>&, bool).
  52298. **/
  52299. template<typename t1, typename t2, typename t3>
  52300. CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const bool is_shared=false) {
  52301. assign(3);
  52302. _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
  52303. return *this;
  52304. }
  52305. //! Construct list from four images \inplace.
  52306. /**
  52307. \see CImgList(const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, bool).
  52308. **/
  52309. template<typename t1, typename t2, typename t3, typename t4>
  52310. CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
  52311. const bool is_shared=false) {
  52312. assign(4);
  52313. _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
  52314. _data[3].assign(img4,is_shared);
  52315. return *this;
  52316. }
  52317. //! Construct list from five images \inplace.
  52318. /**
  52319. \see CImgList(const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, bool).
  52320. **/
  52321. template<typename t1, typename t2, typename t3, typename t4, typename t5>
  52322. CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
  52323. const CImg<t5>& img5, const bool is_shared=false) {
  52324. assign(5);
  52325. _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
  52326. _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared);
  52327. return *this;
  52328. }
  52329. //! Construct list from six images \inplace.
  52330. /**
  52331. \see CImgList(const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&, bool).
  52332. **/
  52333. template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6>
  52334. CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
  52335. const CImg<t5>& img5, const CImg<t6>& img6, const bool is_shared=false) {
  52336. assign(6);
  52337. _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
  52338. _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
  52339. return *this;
  52340. }
  52341. //! Construct list from seven images \inplace.
  52342. /**
  52343. \see CImgList(const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,
  52344. const CImg<t>&, bool).
  52345. **/
  52346. template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7>
  52347. CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
  52348. const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const bool is_shared=false) {
  52349. assign(7);
  52350. _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
  52351. _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
  52352. _data[6].assign(img7,is_shared);
  52353. return *this;
  52354. }
  52355. //! Construct list from eight images \inplace.
  52356. /**
  52357. \see CImgList(const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,
  52358. const CImg<t>&, const CImg<t>&, bool).
  52359. **/
  52360. template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7, typename t8>
  52361. CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
  52362. const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const CImg<t8>& img8,
  52363. const bool is_shared=false) {
  52364. assign(8);
  52365. _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
  52366. _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
  52367. _data[6].assign(img7,is_shared); _data[7].assign(img8,is_shared);
  52368. return *this;
  52369. }
  52370. //! Construct list as a copy of an existing list and force the shared state of the list elements \inplace.
  52371. /**
  52372. \see CImgList(const CImgList<t>&, bool is_shared).
  52373. **/
  52374. template<typename t>
  52375. CImgList<T>& assign(const CImgList<t>& list, const bool is_shared=false) {
  52376. cimg::unused(is_shared);
  52377. assign(list._width);
  52378. cimglist_for(*this,l) _data[l].assign(list[l],false);
  52379. return *this;
  52380. }
  52381. //! Construct list as a copy of an existing list and force shared state of elements \inplace \specialization.
  52382. CImgList<T>& assign(const CImgList<T>& list, const bool is_shared=false) {
  52383. if (this==&list) return *this;
  52384. CImgList<T> res(list._width);
  52385. cimglist_for(res,l) res[l].assign(list[l],is_shared);
  52386. return res.move_to(*this);
  52387. }
  52388. //! Construct list by reading the content of a file \inplace.
  52389. /**
  52390. \see CImgList(const char *const).
  52391. **/
  52392. CImgList<T>& assign(const char *const filename) {
  52393. return load(filename);
  52394. }
  52395. //! Construct list from the content of a display window \inplace.
  52396. /**
  52397. \see CImgList(const CImgDisplay&).
  52398. **/
  52399. CImgList<T>& assign(const CImgDisplay &disp) {
  52400. return assign(CImg<T>(disp));
  52401. }
  52402. //! Transfer the content of the list instance to another list.
  52403. /**
  52404. \param list Destination list.
  52405. \note When returning, the current list instance is empty and the initial content of \c list is destroyed.
  52406. **/
  52407. template<typename t>
  52408. CImgList<t>& move_to(CImgList<t>& list) {
  52409. list.assign(_width);
  52410. bool is_one_shared_element = false;
  52411. cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared;
  52412. if (is_one_shared_element) cimglist_for(*this,l) list[l].assign(_data[l]);
  52413. else cimglist_for(*this,l) _data[l].move_to(list[l]);
  52414. assign();
  52415. return list;
  52416. }
  52417. //! Transfer the content of the list instance at a specified position in another list.
  52418. /**
  52419. \param list Destination list.
  52420. \param pos Index of the insertion in the list.
  52421. \note When returning, the list instance is empty and the initial content of \c list is preserved
  52422. (only images indexes may be modified).
  52423. **/
  52424. template<typename t>
  52425. CImgList<t>& move_to(CImgList<t>& list, const unsigned int pos) {
  52426. if (is_empty()) return list;
  52427. const unsigned int npos = pos>list._width?list._width:pos;
  52428. list.insert(_width,npos);
  52429. bool is_one_shared_element = false;
  52430. cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared;
  52431. if (is_one_shared_element) cimglist_for(*this,l) list[npos + l].assign(_data[l]);
  52432. else cimglist_for(*this,l) _data[l].move_to(list[npos + l]);
  52433. assign();
  52434. return list;
  52435. }
  52436. //! Swap all fields between two list instances.
  52437. /**
  52438. \param list List to swap fields with.
  52439. \note Can be used to exchange the content of two lists in a fast way.
  52440. **/
  52441. CImgList<T>& swap(CImgList<T>& list) {
  52442. cimg::swap(_width,list._width,_allocated_width,list._allocated_width);
  52443. cimg::swap(_data,list._data);
  52444. return list;
  52445. }
  52446. //! Return a reference to an empty list.
  52447. /**
  52448. \note Can be used to define default values in a function taking a CImgList<T> as an argument.
  52449. \code
  52450. void f(const CImgList<char>& list=CImgList<char>::empty());
  52451. \endcode
  52452. **/
  52453. static CImgList<T>& empty() {
  52454. static CImgList<T> _empty;
  52455. return _empty.assign();
  52456. }
  52457. //! Return a reference to an empty list \const.
  52458. static const CImgList<T>& const_empty() {
  52459. static const CImgList<T> _empty;
  52460. return _empty;
  52461. }
  52462. //@}
  52463. //------------------------------------------
  52464. //
  52465. //! \name Overloaded Operators
  52466. //@{
  52467. //------------------------------------------
  52468. //! Return a reference to one image element of the list.
  52469. /**
  52470. \param pos Indice of the image element.
  52471. **/
  52472. CImg<T>& operator()(const unsigned int pos) {
  52473. #if cimg_verbosity>=3
  52474. if (pos>=_width) {
  52475. cimg::warn(_cimglist_instance
  52476. "operator(): Invalid image request, at position [%u].",
  52477. cimglist_instance,
  52478. pos);
  52479. return *_data;
  52480. }
  52481. #endif
  52482. return _data[pos];
  52483. }
  52484. //! Return a reference to one image of the list.
  52485. /**
  52486. \param pos Indice of the image element.
  52487. **/
  52488. const CImg<T>& operator()(const unsigned int pos) const {
  52489. return const_cast<CImgList<T>*>(this)->operator()(pos);
  52490. }
  52491. //! Return a reference to one pixel value of one image of the list.
  52492. /**
  52493. \param pos Indice of the image element.
  52494. \param x X-coordinate of the pixel value.
  52495. \param y Y-coordinate of the pixel value.
  52496. \param z Z-coordinate of the pixel value.
  52497. \param c C-coordinate of the pixel value.
  52498. \note <tt>list(n,x,y,z,c)</tt> is equivalent to <tt>list[n](x,y,z,c)</tt>.
  52499. **/
  52500. T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0,
  52501. const unsigned int z=0, const unsigned int c=0) {
  52502. return (*this)[pos](x,y,z,c);
  52503. }
  52504. //! Return a reference to one pixel value of one image of the list \const.
  52505. const T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0,
  52506. const unsigned int z=0, const unsigned int c=0) const {
  52507. return (*this)[pos](x,y,z,c);
  52508. }
  52509. //! Return pointer to the first image of the list.
  52510. /**
  52511. \note Images in a list are stored as a buffer of \c CImg<T>.
  52512. **/
  52513. operator CImg<T>*() {
  52514. return _data;
  52515. }
  52516. //! Return pointer to the first image of the list \const.
  52517. operator const CImg<T>*() const {
  52518. return _data;
  52519. }
  52520. //! Construct list from one image \inplace.
  52521. /**
  52522. \param img Input image to copy in the constructed list.
  52523. \note <tt>list = img;</tt> is equivalent to <tt>list.assign(img);</tt>.
  52524. **/
  52525. template<typename t>
  52526. CImgList<T>& operator=(const CImg<t>& img) {
  52527. return assign(img);
  52528. }
  52529. //! Construct list from another list.
  52530. /**
  52531. \param list Input list to copy.
  52532. \note <tt>list1 = list2</tt> is equivalent to <tt>list1.assign(list2);</tt>.
  52533. **/
  52534. template<typename t>
  52535. CImgList<T>& operator=(const CImgList<t>& list) {
  52536. return assign(list);
  52537. }
  52538. //! Construct list from another list \specialization.
  52539. CImgList<T>& operator=(const CImgList<T>& list) {
  52540. return assign(list);
  52541. }
  52542. //! Construct list by reading the content of a file \inplace.
  52543. /**
  52544. \see CImgList(const char *const).
  52545. **/
  52546. CImgList<T>& operator=(const char *const filename) {
  52547. return assign(filename);
  52548. }
  52549. //! Construct list from the content of a display window \inplace.
  52550. /**
  52551. \see CImgList(const CImgDisplay&).
  52552. **/
  52553. CImgList<T>& operator=(const CImgDisplay& disp) {
  52554. return assign(disp);
  52555. }
  52556. //! Return a non-shared copy of a list.
  52557. /**
  52558. \note <tt>+list</tt> is equivalent to <tt>CImgList<T>(list,false)</tt>.
  52559. It forces the copy to have non-shared elements.
  52560. **/
  52561. CImgList<T> operator+() const {
  52562. return CImgList<T>(*this,false);
  52563. }
  52564. //! Return a copy of the list instance, where image \c img has been inserted at the end.
  52565. /**
  52566. \param img Image inserted at the end of the instance copy.
  52567. \note Define a convenient way to create temporary lists of images, as in the following code:
  52568. \code
  52569. (img1,img2,img3,img4).display("My four images");
  52570. \endcode
  52571. **/
  52572. template<typename t>
  52573. CImgList<T>& operator,(const CImg<t>& img) {
  52574. return insert(img);
  52575. }
  52576. //! Return a copy of the list instance, where image \c img has been inserted at the end \const.
  52577. template<typename t>
  52578. CImgList<T> operator,(const CImg<t>& img) const {
  52579. return (+*this).insert(img);
  52580. }
  52581. //! Return a copy of the list instance, where all elements of input list \c list have been inserted at the end.
  52582. /**
  52583. \param list List inserted at the end of the instance copy.
  52584. **/
  52585. template<typename t>
  52586. CImgList<T>& operator,(const CImgList<t>& list) {
  52587. return insert(list);
  52588. }
  52589. //! Return a copy of the list instance, where all elements of input \c list have been inserted at the end \const.
  52590. template<typename t>
  52591. CImgList<T>& operator,(const CImgList<t>& list) const {
  52592. return (+*this).insert(list);
  52593. }
  52594. //! Return image corresponding to the appending of all images of the instance list along specified axis.
  52595. /**
  52596. \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
  52597. \note <tt>list>'x'</tt> is equivalent to <tt>list.get_append('x')</tt>.
  52598. **/
  52599. CImg<T> operator>(const char axis) const {
  52600. return get_append(axis,0);
  52601. }
  52602. //! Return list corresponding to the splitting of all images of the instance list along specified axis.
  52603. /**
  52604. \param axis Axis used for image splitting.
  52605. \note <tt>list<'x'</tt> is equivalent to <tt>list.get_split('x')</tt>.
  52606. **/
  52607. CImgList<T> operator<(const char axis) const {
  52608. return get_split(axis);
  52609. }
  52610. //@}
  52611. //-------------------------------------
  52612. //
  52613. //! \name Instance Characteristics
  52614. //@{
  52615. //-------------------------------------
  52616. //! Return the type of image pixel values as a C string.
  52617. /**
  52618. Return a \c char* string containing the usual type name of the image pixel values
  52619. (i.e. a stringified version of the template parameter \c T).
  52620. \note
  52621. - The returned string may contain spaces (as in \c "unsigned char").
  52622. - If the pixel type \c T does not correspond to a registered type, the string <tt>"unknown"</tt> is returned.
  52623. **/
  52624. static const char* pixel_type() {
  52625. return cimg::type<T>::string();
  52626. }
  52627. //! Return the size of the list, i.e. the number of images contained in it.
  52628. /**
  52629. \note Similar to size() but returns result as a (signed) integer.
  52630. **/
  52631. int width() const {
  52632. return (int)_width;
  52633. }
  52634. //! Return the size of the list, i.e. the number of images contained in it.
  52635. /**
  52636. \note Similar to width() but returns result as an unsigned integer.
  52637. **/
  52638. unsigned int size() const {
  52639. return _width;
  52640. }
  52641. //! Return pointer to the first image of the list.
  52642. /**
  52643. \note Images in a list are stored as a buffer of \c CImg<T>.
  52644. **/
  52645. CImg<T> *data() {
  52646. return _data;
  52647. }
  52648. //! Return pointer to the first image of the list \const.
  52649. const CImg<T> *data() const {
  52650. return _data;
  52651. }
  52652. //! Return pointer to the pos-th image of the list.
  52653. /**
  52654. \param pos Indice of the image element to access.
  52655. \note <tt>list.data(n);</tt> is equivalent to <tt>list.data + n;</tt>.
  52656. **/
  52657. #if cimg_verbosity>=3
  52658. CImg<T> *data(const unsigned int pos) {
  52659. if (pos>=size())
  52660. cimg::warn(_cimglist_instance
  52661. "data(): Invalid pointer request, at position [%u].",
  52662. cimglist_instance,
  52663. pos);
  52664. return _data + pos;
  52665. }
  52666. const CImg<T> *data(const unsigned int l) const {
  52667. return const_cast<CImgList<T>*>(this)->data(l);
  52668. }
  52669. #else
  52670. CImg<T> *data(const unsigned int l) {
  52671. return _data + l;
  52672. }
  52673. //! Return pointer to the pos-th image of the list \const.
  52674. const CImg<T> *data(const unsigned int l) const {
  52675. return _data + l;
  52676. }
  52677. #endif
  52678. //! Return iterator to the first image of the list.
  52679. /**
  52680. **/
  52681. iterator begin() {
  52682. return _data;
  52683. }
  52684. //! Return iterator to the first image of the list \const.
  52685. const_iterator begin() const {
  52686. return _data;
  52687. }
  52688. //! Return iterator to one position after the last image of the list.
  52689. /**
  52690. **/
  52691. iterator end() {
  52692. return _data + _width;
  52693. }
  52694. //! Return iterator to one position after the last image of the list \const.
  52695. const_iterator end() const {
  52696. return _data + _width;
  52697. }
  52698. //! Return reference to the first image of the list.
  52699. /**
  52700. **/
  52701. CImg<T>& front() {
  52702. return *_data;
  52703. }
  52704. //! Return reference to the first image of the list \const.
  52705. const CImg<T>& front() const {
  52706. return *_data;
  52707. }
  52708. //! Return a reference to the last image of the list.
  52709. /**
  52710. **/
  52711. const CImg<T>& back() const {
  52712. return *(_data + _width - 1);
  52713. }
  52714. //! Return a reference to the last image of the list \const.
  52715. CImg<T>& back() {
  52716. return *(_data + _width - 1);
  52717. }
  52718. //! Return pos-th image of the list.
  52719. /**
  52720. \param pos Indice of the image element to access.
  52721. **/
  52722. CImg<T>& at(const int pos) {
  52723. if (is_empty())
  52724. throw CImgInstanceException(_cimglist_instance
  52725. "at(): Empty instance.",
  52726. cimglist_instance);
  52727. return _data[cimg::cut(pos,0,width() - 1)];
  52728. }
  52729. //! Access to pixel value with Dirichlet boundary conditions.
  52730. /**
  52731. \param pos Indice of the image element to access.
  52732. \param x X-coordinate of the pixel value.
  52733. \param y Y-coordinate of the pixel value.
  52734. \param z Z-coordinate of the pixel value.
  52735. \param c C-coordinate of the pixel value.
  52736. \param out_value Default value returned if \c offset is outside image bounds.
  52737. \note <tt>list.atNXYZC(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZC(x,y,z,c);</tt>.
  52738. **/
  52739. T& atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
  52740. return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atXYZC(x,y,z,c,out_value);
  52741. }
  52742. //! Access to pixel value with Dirichlet boundary conditions \const.
  52743. T atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
  52744. return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atXYZC(x,y,z,c,out_value);
  52745. }
  52746. //! Access to pixel value with Neumann boundary conditions.
  52747. /**
  52748. \param pos Indice of the image element to access.
  52749. \param x X-coordinate of the pixel value.
  52750. \param y Y-coordinate of the pixel value.
  52751. \param z Z-coordinate of the pixel value.
  52752. \param c C-coordinate of the pixel value.
  52753. \note <tt>list.atNXYZC(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZC(x,y,z,c);</tt>.
  52754. **/
  52755. T& atNXYZC(const int pos, const int x, const int y, const int z, const int c) {
  52756. if (is_empty())
  52757. throw CImgInstanceException(_cimglist_instance
  52758. "atNXYZC(): Empty instance.",
  52759. cimglist_instance);
  52760. return _atNXYZC(pos,x,y,z,c);
  52761. }
  52762. //! Access to pixel value with Neumann boundary conditions \const.
  52763. T atNXYZC(const int pos, const int x, const int y, const int z, const int c) const {
  52764. if (is_empty())
  52765. throw CImgInstanceException(_cimglist_instance
  52766. "atNXYZC(): Empty instance.",
  52767. cimglist_instance);
  52768. return _atNXYZC(pos,x,y,z,c);
  52769. }
  52770. T& _atNXYZC(const int pos, const int x, const int y, const int z, const int c) {
  52771. return _data[cimg::cut(pos,0,width() - 1)].atXYZC(x,y,z,c);
  52772. }
  52773. T _atNXYZC(const int pos, const int x, const int y, const int z, const int c) const {
  52774. return _data[cimg::cut(pos,0,width() - 1)].atXYZC(x,y,z,c);
  52775. }
  52776. //! Access pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y,\c z).
  52777. /**
  52778. \param pos Indice of the image element to access.
  52779. \param x X-coordinate of the pixel value.
  52780. \param y Y-coordinate of the pixel value.
  52781. \param z Z-coordinate of the pixel value.
  52782. \param c C-coordinate of the pixel value.
  52783. \param out_value Default value returned if \c offset is outside image bounds.
  52784. \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
  52785. **/
  52786. T& atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
  52787. return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atXYZ(x,y,z,c,out_value);
  52788. }
  52789. //! Access pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y,\c z) \const.
  52790. T atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
  52791. return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atXYZ(x,y,z,c,out_value);
  52792. }
  52793. //! Access to pixel value with Neumann boundary conditions for the 4 coordinates (\c pos, \c x,\c y,\c z).
  52794. /**
  52795. \param pos Indice of the image element to access.
  52796. \param x X-coordinate of the pixel value.
  52797. \param y Y-coordinate of the pixel value.
  52798. \param z Z-coordinate of the pixel value.
  52799. \param c C-coordinate of the pixel value.
  52800. \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
  52801. **/
  52802. T& atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) {
  52803. if (is_empty())
  52804. throw CImgInstanceException(_cimglist_instance
  52805. "atNXYZ(): Empty instance.",
  52806. cimglist_instance);
  52807. return _atNXYZ(pos,x,y,z,c);
  52808. }
  52809. //! Access to pixel value with Neumann boundary conditions for the 4 coordinates (\c pos, \c x,\c y,\c z) \const.
  52810. T atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const {
  52811. if (is_empty())
  52812. throw CImgInstanceException(_cimglist_instance
  52813. "atNXYZ(): Empty instance.",
  52814. cimglist_instance);
  52815. return _atNXYZ(pos,x,y,z,c);
  52816. }
  52817. T& _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) {
  52818. return _data[cimg::cut(pos,0,width() - 1)].atXYZ(x,y,z,c);
  52819. }
  52820. T _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const {
  52821. return _data[cimg::cut(pos,0,width() - 1)].atXYZ(x,y,z,c);
  52822. }
  52823. //! Access to pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y).
  52824. /**
  52825. \param pos Indice of the image element to access.
  52826. \param x X-coordinate of the pixel value.
  52827. \param y Y-coordinate of the pixel value.
  52828. \param z Z-coordinate of the pixel value.
  52829. \param c C-coordinate of the pixel value.
  52830. \param out_value Default value returned if \c offset is outside image bounds.
  52831. \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
  52832. **/
  52833. T& atNXY(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
  52834. return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atXY(x,y,z,c,out_value);
  52835. }
  52836. //! Access to pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y) \const.
  52837. T atNXY(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
  52838. return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atXY(x,y,z,c,out_value);
  52839. }
  52840. //! Access to pixel value with Neumann boundary conditions for the 3 coordinates (\c pos, \c x,\c y).
  52841. /**
  52842. \param pos Indice of the image element to access.
  52843. \param x X-coordinate of the pixel value.
  52844. \param y Y-coordinate of the pixel value.
  52845. \param z Z-coordinate of the pixel value.
  52846. \param c C-coordinate of the pixel value.
  52847. \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
  52848. **/
  52849. T& atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) {
  52850. if (is_empty())
  52851. throw CImgInstanceException(_cimglist_instance
  52852. "atNXY(): Empty instance.",
  52853. cimglist_instance);
  52854. return _atNXY(pos,x,y,z,c);
  52855. }
  52856. //! Access to pixel value with Neumann boundary conditions for the 3 coordinates (\c pos, \c x,\c y) \const.
  52857. T atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const {
  52858. if (is_empty())
  52859. throw CImgInstanceException(_cimglist_instance
  52860. "atNXY(): Empty instance.",
  52861. cimglist_instance);
  52862. return _atNXY(pos,x,y,z,c);
  52863. }
  52864. T& _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) {
  52865. return _data[cimg::cut(pos,0,width() - 1)].atXY(x,y,z,c);
  52866. }
  52867. T _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const {
  52868. return _data[cimg::cut(pos,0,width() - 1)].atXY(x,y,z,c);
  52869. }
  52870. //! Access to pixel value with Dirichlet boundary conditions for the 2 coordinates (\c pos,\c x).
  52871. /**
  52872. \param pos Indice of the image element to access.
  52873. \param x X-coordinate of the pixel value.
  52874. \param y Y-coordinate of the pixel value.
  52875. \param z Z-coordinate of the pixel value.
  52876. \param c C-coordinate of the pixel value.
  52877. \param out_value Default value returned if \c offset is outside image bounds.
  52878. \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
  52879. **/
  52880. T& atNX(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
  52881. return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atX(x,y,z,c,out_value);
  52882. }
  52883. //! Access to pixel value with Dirichlet boundary conditions for the 2 coordinates (\c pos,\c x) \const.
  52884. T atNX(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
  52885. return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atX(x,y,z,c,out_value);
  52886. }
  52887. //! Access to pixel value with Neumann boundary conditions for the 2 coordinates (\c pos, \c x).
  52888. /**
  52889. \param pos Indice of the image element to access.
  52890. \param x X-coordinate of the pixel value.
  52891. \param y Y-coordinate of the pixel value.
  52892. \param z Z-coordinate of the pixel value.
  52893. \param c C-coordinate of the pixel value.
  52894. \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
  52895. **/
  52896. T& atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) {
  52897. if (is_empty())
  52898. throw CImgInstanceException(_cimglist_instance
  52899. "atNX(): Empty instance.",
  52900. cimglist_instance);
  52901. return _atNX(pos,x,y,z,c);
  52902. }
  52903. //! Access to pixel value with Neumann boundary conditions for the 2 coordinates (\c pos, \c x) \const.
  52904. T atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const {
  52905. if (is_empty())
  52906. throw CImgInstanceException(_cimglist_instance
  52907. "atNX(): Empty instance.",
  52908. cimglist_instance);
  52909. return _atNX(pos,x,y,z,c);
  52910. }
  52911. T& _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) {
  52912. return _data[cimg::cut(pos,0,width() - 1)].atX(x,y,z,c);
  52913. }
  52914. T _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const {
  52915. return _data[cimg::cut(pos,0,width() - 1)].atX(x,y,z,c);
  52916. }
  52917. //! Access to pixel value with Dirichlet boundary conditions for the coordinate (\c pos).
  52918. /**
  52919. \param pos Indice of the image element to access.
  52920. \param x X-coordinate of the pixel value.
  52921. \param y Y-coordinate of the pixel value.
  52922. \param z Z-coordinate of the pixel value.
  52923. \param c C-coordinate of the pixel value.
  52924. \param out_value Default value returned if \c offset is outside image bounds.
  52925. \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
  52926. **/
  52927. T& atN(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
  52928. return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):(*this)(pos,x,y,z,c);
  52929. }
  52930. //! Access to pixel value with Dirichlet boundary conditions for the coordinate (\c pos) \const.
  52931. T atN(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
  52932. return (pos<0 || pos>=(int)_width)?out_value:(*this)(pos,x,y,z,c);
  52933. }
  52934. //! Return pixel value with Neumann boundary conditions for the coordinate (\c pos).
  52935. /**
  52936. \param pos Indice of the image element to access.
  52937. \param x X-coordinate of the pixel value.
  52938. \param y Y-coordinate of the pixel value.
  52939. \param z Z-coordinate of the pixel value.
  52940. \param c C-coordinate of the pixel value.
  52941. \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
  52942. **/
  52943. T& atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) {
  52944. if (is_empty())
  52945. throw CImgInstanceException(_cimglist_instance
  52946. "atN(): Empty instance.",
  52947. cimglist_instance);
  52948. return _atN(pos,x,y,z,c);
  52949. }
  52950. //! Return pixel value with Neumann boundary conditions for the coordinate (\c pos) \const.
  52951. T atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const {
  52952. if (is_empty())
  52953. throw CImgInstanceException(_cimglist_instance
  52954. "atN(): Empty instance.",
  52955. cimglist_instance);
  52956. return _atN(pos,x,y,z,c);
  52957. }
  52958. T& _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) {
  52959. return _data[cimg::cut(pos,0,width() - 1)](x,y,z,c);
  52960. }
  52961. T _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const {
  52962. return _data[cimg::cut(pos,0,width() - 1)](x,y,z,c);
  52963. }
  52964. //@}
  52965. //-------------------------------------
  52966. //
  52967. //! \name Instance Checking
  52968. //@{
  52969. //-------------------------------------
  52970. //! Return \c true if list is empty.
  52971. /**
  52972. **/
  52973. bool is_empty() const {
  52974. return (!_data || !_width);
  52975. }
  52976. //! Test if number of image elements is equal to specified value.
  52977. /**
  52978. \param size_n Number of image elements to test.
  52979. **/
  52980. bool is_sameN(const unsigned int size_n) const {
  52981. return _width==size_n;
  52982. }
  52983. //! Test if number of image elements is equal between two images lists.
  52984. /**
  52985. \param list Input list to compare with.
  52986. **/
  52987. template<typename t>
  52988. bool is_sameN(const CImgList<t>& list) const {
  52989. return is_sameN(list._width);
  52990. }
  52991. // Define useful functions to check list dimensions.
  52992. // (cannot be documented because macro-generated).
  52993. #define _cimglist_def_is_same1(axis) \
  52994. bool is_same##axis(const unsigned int val) const { \
  52995. bool res = true; \
  52996. for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(val); return res; \
  52997. } \
  52998. bool is_sameN##axis(const unsigned int n, const unsigned int val) const { \
  52999. return is_sameN(n) && is_same##axis(val); \
  53000. } \
  53001. #define _cimglist_def_is_same2(axis1,axis2) \
  53002. bool is_same##axis1##axis2(const unsigned int val1, const unsigned int val2) const { \
  53003. bool res = true; \
  53004. for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis1##axis2(val1,val2); return res; \
  53005. } \
  53006. bool is_sameN##axis1##axis2(const unsigned int n, const unsigned int val1, const unsigned int val2) const { \
  53007. return is_sameN(n) && is_same##axis1##axis2(val1,val2); \
  53008. } \
  53009. #define _cimglist_def_is_same3(axis1,axis2,axis3) \
  53010. bool is_same##axis1##axis2##axis3(const unsigned int val1, const unsigned int val2, \
  53011. const unsigned int val3) const { \
  53012. bool res = true; \
  53013. for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis1##axis2##axis3(val1,val2,val3); \
  53014. return res; \
  53015. } \
  53016. bool is_sameN##axis1##axis2##axis3(const unsigned int n, const unsigned int val1, \
  53017. const unsigned int val2, const unsigned int val3) const { \
  53018. return is_sameN(n) && is_same##axis1##axis2##axis3(val1,val2,val3); \
  53019. } \
  53020. #define _cimglist_def_is_same(axis) \
  53021. template<typename t> bool is_same##axis(const CImg<t>& img) const { \
  53022. bool res = true; for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(img); return res; \
  53023. } \
  53024. template<typename t> bool is_same##axis(const CImgList<t>& list) const { \
  53025. const unsigned int lmin = std::min(_width,list._width); \
  53026. bool res = true; for (unsigned int l = 0; l<lmin && res; ++l) res = _data[l].is_same##axis(list[l]); return res; \
  53027. } \
  53028. template<typename t> bool is_sameN##axis(const unsigned int n, const CImg<t>& img) const { \
  53029. return (is_sameN(n) && is_same##axis(img)); \
  53030. } \
  53031. template<typename t> bool is_sameN##axis(const CImgList<t>& list) const { \
  53032. return (is_sameN(list) && is_same##axis(list)); \
  53033. }
  53034. _cimglist_def_is_same(XY)
  53035. _cimglist_def_is_same(XZ)
  53036. _cimglist_def_is_same(XC)
  53037. _cimglist_def_is_same(YZ)
  53038. _cimglist_def_is_same(YC)
  53039. _cimglist_def_is_same(XYZ)
  53040. _cimglist_def_is_same(XYC)
  53041. _cimglist_def_is_same(YZC)
  53042. _cimglist_def_is_same(XYZC)
  53043. _cimglist_def_is_same1(X)
  53044. _cimglist_def_is_same1(Y)
  53045. _cimglist_def_is_same1(Z)
  53046. _cimglist_def_is_same1(C)
  53047. _cimglist_def_is_same2(X,Y)
  53048. _cimglist_def_is_same2(X,Z)
  53049. _cimglist_def_is_same2(X,C)
  53050. _cimglist_def_is_same2(Y,Z)
  53051. _cimglist_def_is_same2(Y,C)
  53052. _cimglist_def_is_same2(Z,C)
  53053. _cimglist_def_is_same3(X,Y,Z)
  53054. _cimglist_def_is_same3(X,Y,C)
  53055. _cimglist_def_is_same3(X,Z,C)
  53056. _cimglist_def_is_same3(Y,Z,C)
  53057. //! Test if dimensions of each image of the list match specified arguments.
  53058. /**
  53059. \param dx Checked image width.
  53060. \param dy Checked image height.
  53061. \param dz Checked image depth.
  53062. \param dc Checked image spectrum.
  53063. **/
  53064. bool is_sameXYZC(const unsigned int dx, const unsigned int dy,
  53065. const unsigned int dz, const unsigned int dc) const {
  53066. bool res = true;
  53067. for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_sameXYZC(dx,dy,dz,dc);
  53068. return res;
  53069. }
  53070. //! Test if list dimensions match specified arguments.
  53071. /**
  53072. \param n Number of images in the list.
  53073. \param dx Checked image width.
  53074. \param dy Checked image height.
  53075. \param dz Checked image depth.
  53076. \param dc Checked image spectrum.
  53077. **/
  53078. bool is_sameNXYZC(const unsigned int n,
  53079. const unsigned int dx, const unsigned int dy,
  53080. const unsigned int dz, const unsigned int dc) const {
  53081. return is_sameN(n) && is_sameXYZC(dx,dy,dz,dc);
  53082. }
  53083. //! Test if list contains one particular pixel location.
  53084. /**
  53085. \param n Index of the image whom checked pixel value belong to.
  53086. \param x X-coordinate of the checked pixel value.
  53087. \param y Y-coordinate of the checked pixel value.
  53088. \param z Z-coordinate of the checked pixel value.
  53089. \param c C-coordinate of the checked pixel value.
  53090. **/
  53091. bool containsNXYZC(const int n, const int x=0, const int y=0, const int z=0, const int c=0) const {
  53092. if (is_empty()) return false;
  53093. return n>=0 && n<(int)_width && x>=0 && x<_data[n].width() && y>=0 && y<_data[n].height() &&
  53094. z>=0 && z<_data[n].depth() && c>=0 && c<_data[n].spectrum();
  53095. }
  53096. //! Test if list contains image with specified indice.
  53097. /**
  53098. \param n Index of the checked image.
  53099. **/
  53100. bool containsN(const int n) const {
  53101. if (is_empty()) return false;
  53102. return n>=0 && n<(int)_width;
  53103. }
  53104. //! Test if one image of the list contains the specified referenced value.
  53105. /**
  53106. \param pixel Reference to pixel value to test.
  53107. \param[out] n Index of image containing the pixel value, if test succeeds.
  53108. \param[out] x X-coordinate of the pixel value, if test succeeds.
  53109. \param[out] y Y-coordinate of the pixel value, if test succeeds.
  53110. \param[out] z Z-coordinate of the pixel value, if test succeeds.
  53111. \param[out] c C-coordinate of the pixel value, if test succeeds.
  53112. \note If true, set coordinates (n,x,y,z,c).
  53113. **/
  53114. template<typename t>
  53115. bool contains(const T& pixel, t& n, t& x, t&y, t& z, t& c) const {
  53116. if (is_empty()) return false;
  53117. cimglist_for(*this,l) if (_data[l].contains(pixel,x,y,z,c)) { n = (t)l; return true; }
  53118. return false;
  53119. }
  53120. //! Test if one of the image list contains the specified referenced value.
  53121. /**
  53122. \param pixel Reference to pixel value to test.
  53123. \param[out] n Index of image containing the pixel value, if test succeeds.
  53124. \param[out] x X-coordinate of the pixel value, if test succeeds.
  53125. \param[out] y Y-coordinate of the pixel value, if test succeeds.
  53126. \param[out] z Z-coordinate of the pixel value, if test succeeds.
  53127. \note If true, set coordinates (n,x,y,z).
  53128. **/
  53129. template<typename t>
  53130. bool contains(const T& pixel, t& n, t& x, t&y, t& z) const {
  53131. t c;
  53132. return contains(pixel,n,x,y,z,c);
  53133. }
  53134. //! Test if one of the image list contains the specified referenced value.
  53135. /**
  53136. \param pixel Reference to pixel value to test.
  53137. \param[out] n Index of image containing the pixel value, if test succeeds.
  53138. \param[out] x X-coordinate of the pixel value, if test succeeds.
  53139. \param[out] y Y-coordinate of the pixel value, if test succeeds.
  53140. \note If true, set coordinates (n,x,y).
  53141. **/
  53142. template<typename t>
  53143. bool contains(const T& pixel, t& n, t& x, t&y) const {
  53144. t z, c;
  53145. return contains(pixel,n,x,y,z,c);
  53146. }
  53147. //! Test if one of the image list contains the specified referenced value.
  53148. /**
  53149. \param pixel Reference to pixel value to test.
  53150. \param[out] n Index of image containing the pixel value, if test succeeds.
  53151. \param[out] x X-coordinate of the pixel value, if test succeeds.
  53152. \note If true, set coordinates (n,x).
  53153. **/
  53154. template<typename t>
  53155. bool contains(const T& pixel, t& n, t& x) const {
  53156. t y, z, c;
  53157. return contains(pixel,n,x,y,z,c);
  53158. }
  53159. //! Test if one of the image list contains the specified referenced value.
  53160. /**
  53161. \param pixel Reference to pixel value to test.
  53162. \param[out] n Index of image containing the pixel value, if test succeeds.
  53163. \note If true, set coordinates (n).
  53164. **/
  53165. template<typename t>
  53166. bool contains(const T& pixel, t& n) const {
  53167. t x, y, z, c;
  53168. return contains(pixel,n,x,y,z,c);
  53169. }
  53170. //! Test if one of the image list contains the specified referenced value.
  53171. /**
  53172. \param pixel Reference to pixel value to test.
  53173. **/
  53174. bool contains(const T& pixel) const {
  53175. unsigned int n, x, y, z, c;
  53176. return contains(pixel,n,x,y,z,c);
  53177. }
  53178. //! Test if the list contains the image 'img'.
  53179. /**
  53180. \param img Reference to image to test.
  53181. \param[out] n Index of image in the list, if test succeeds.
  53182. \note If true, returns the position (n) of the image in the list.
  53183. **/
  53184. template<typename t>
  53185. bool contains(const CImg<T>& img, t& n) const {
  53186. if (is_empty()) return false;
  53187. const CImg<T> *const ptr = &img;
  53188. cimglist_for(*this,i) if (_data + i==ptr) { n = (t)i; return true; }
  53189. return false;
  53190. }
  53191. //! Test if the list contains the image img.
  53192. /**
  53193. \param img Reference to image to test.
  53194. **/
  53195. bool contains(const CImg<T>& img) const {
  53196. unsigned int n;
  53197. return contains(img,n);
  53198. }
  53199. //@}
  53200. //-------------------------------------
  53201. //
  53202. //! \name Mathematical Functions
  53203. //@{
  53204. //-------------------------------------
  53205. //! Return a reference to the minimum pixel value of the instance list.
  53206. /**
  53207. **/
  53208. T& min() {
  53209. if (is_empty())
  53210. throw CImgInstanceException(_cimglist_instance
  53211. "min(): Empty instance.",
  53212. cimglist_instance);
  53213. T *ptr_min = _data->_data;
  53214. T min_value = *ptr_min;
  53215. cimglist_for(*this,l) {
  53216. const CImg<T>& img = _data[l];
  53217. cimg_for(img,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
  53218. }
  53219. return *ptr_min;
  53220. }
  53221. //! Return a reference to the minimum pixel value of the instance list \const.
  53222. const T& min() const {
  53223. if (is_empty())
  53224. throw CImgInstanceException(_cimglist_instance
  53225. "min(): Empty instance.",
  53226. cimglist_instance);
  53227. const T *ptr_min = _data->_data;
  53228. T min_value = *ptr_min;
  53229. cimglist_for(*this,l) {
  53230. const CImg<T>& img = _data[l];
  53231. cimg_for(img,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
  53232. }
  53233. return *ptr_min;
  53234. }
  53235. //! Return a reference to the maximum pixel value of the instance list.
  53236. /**
  53237. **/
  53238. T& max() {
  53239. if (is_empty())
  53240. throw CImgInstanceException(_cimglist_instance
  53241. "max(): Empty instance.",
  53242. cimglist_instance);
  53243. T *ptr_max = _data->_data;
  53244. T max_value = *ptr_max;
  53245. cimglist_for(*this,l) {
  53246. const CImg<T>& img = _data[l];
  53247. cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
  53248. }
  53249. return *ptr_max;
  53250. }
  53251. //! Return a reference to the maximum pixel value of the instance list \const.
  53252. const T& max() const {
  53253. if (is_empty())
  53254. throw CImgInstanceException(_cimglist_instance
  53255. "max(): Empty instance.",
  53256. cimglist_instance);
  53257. const T *ptr_max = _data->_data;
  53258. T max_value = *ptr_max;
  53259. cimglist_for(*this,l) {
  53260. const CImg<T>& img = _data[l];
  53261. cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
  53262. }
  53263. return *ptr_max;
  53264. }
  53265. //! Return a reference to the minimum pixel value of the instance list and return the maximum vvalue as well.
  53266. /**
  53267. \param[out] max_val Value of the maximum value found.
  53268. **/
  53269. template<typename t>
  53270. T& min_max(t& max_val) {
  53271. if (is_empty())
  53272. throw CImgInstanceException(_cimglist_instance
  53273. "min_max(): Empty instance.",
  53274. cimglist_instance);
  53275. T *ptr_min = _data->_data;
  53276. T min_value = *ptr_min, max_value = min_value;
  53277. cimglist_for(*this,l) {
  53278. const CImg<T>& img = _data[l];
  53279. cimg_for(img,ptrs,T) {
  53280. const T val = *ptrs;
  53281. if (val<min_value) { min_value = val; ptr_min = ptrs; }
  53282. if (val>max_value) max_value = val;
  53283. }
  53284. }
  53285. max_val = (t)max_value;
  53286. return *ptr_min;
  53287. }
  53288. //! Return a reference to the minimum pixel value of the instance list and return the maximum vvalue as well \const.
  53289. /**
  53290. \param[out] max_val Value of the maximum value found.
  53291. **/
  53292. template<typename t>
  53293. const T& min_max(t& max_val) const {
  53294. if (is_empty())
  53295. throw CImgInstanceException(_cimglist_instance
  53296. "min_max(): Empty instance.",
  53297. cimglist_instance);
  53298. const T *ptr_min = _data->_data;
  53299. T min_value = *ptr_min, max_value = min_value;
  53300. cimglist_for(*this,l) {
  53301. const CImg<T>& img = _data[l];
  53302. cimg_for(img,ptrs,T) {
  53303. const T val = *ptrs;
  53304. if (val<min_value) { min_value = val; ptr_min = ptrs; }
  53305. if (val>max_value) max_value = val;
  53306. }
  53307. }
  53308. max_val = (t)max_value;
  53309. return *ptr_min;
  53310. }
  53311. //! Return a reference to the minimum pixel value of the instance list and return the minimum value as well.
  53312. /**
  53313. \param[out] min_val Value of the minimum value found.
  53314. **/
  53315. template<typename t>
  53316. T& max_min(t& min_val) {
  53317. if (is_empty())
  53318. throw CImgInstanceException(_cimglist_instance
  53319. "max_min(): Empty instance.",
  53320. cimglist_instance);
  53321. T *ptr_max = _data->_data;
  53322. T min_value = *ptr_max, max_value = min_value;
  53323. cimglist_for(*this,l) {
  53324. const CImg<T>& img = _data[l];
  53325. cimg_for(img,ptrs,T) {
  53326. const T val = *ptrs;
  53327. if (val>max_value) { max_value = val; ptr_max = ptrs; }
  53328. if (val<min_value) min_value = val;
  53329. }
  53330. }
  53331. min_val = (t)min_value;
  53332. return *ptr_max;
  53333. }
  53334. //! Return a reference to the minimum pixel value of the instance list and return the minimum value as well \const.
  53335. template<typename t>
  53336. const T& max_min(t& min_val) const {
  53337. if (is_empty())
  53338. throw CImgInstanceException(_cimglist_instance
  53339. "max_min(): Empty instance.",
  53340. cimglist_instance);
  53341. const T *ptr_max = _data->_data;
  53342. T min_value = *ptr_max, max_value = min_value;
  53343. cimglist_for(*this,l) {
  53344. const CImg<T>& img = _data[l];
  53345. cimg_for(img,ptrs,T) {
  53346. const T val = *ptrs;
  53347. if (val>max_value) { max_value = val; ptr_max = ptrs; }
  53348. if (val<min_value) min_value = val;
  53349. }
  53350. }
  53351. min_val = (t)min_value;
  53352. return *ptr_max;
  53353. }
  53354. //@}
  53355. //---------------------------
  53356. //
  53357. //! \name List Manipulation
  53358. //@{
  53359. //---------------------------
  53360. //! Insert a copy of the image \c img into the current image list, at position \c pos.
  53361. /**
  53362. \param img Image to insert a copy to the list.
  53363. \param pos Index of the insertion.
  53364. \param is_shared Tells if the inserted image is a shared copy of \c img or not.
  53365. **/
  53366. template<typename t>
  53367. CImgList<T>& insert(const CImg<t>& img, const unsigned int pos=~0U, const bool is_shared=false) {
  53368. const unsigned int npos = pos==~0U?_width:pos;
  53369. if (npos>_width)
  53370. throw CImgArgumentException(_cimglist_instance
  53371. "insert(): Invalid insertion request of specified image (%u,%u,%u,%u,%p) "
  53372. "at position %u.",
  53373. cimglist_instance,
  53374. img._width,img._height,img._depth,img._spectrum,img._data,npos);
  53375. if (is_shared)
  53376. throw CImgArgumentException(_cimglist_instance
  53377. "insert(): Invalid insertion request of specified shared image "
  53378. "CImg<%s>(%u,%u,%u,%u,%p) at position %u (pixel types are different).",
  53379. cimglist_instance,
  53380. img.pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data,npos);
  53381. CImg<T> *const new_data = (++_width>_allocated_width)?new CImg<T>[_allocated_width?(_allocated_width<<=1):
  53382. (_allocated_width=16)]:0;
  53383. if (!_data) { // Insert new element into empty list.
  53384. _data = new_data;
  53385. *_data = img;
  53386. } else {
  53387. if (new_data) { // Insert with re-allocation.
  53388. if (npos) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg<T>)*npos);
  53389. if (npos!=_width - 1)
  53390. std::memcpy((void*)(new_data + npos + 1),(void*)(_data + npos),sizeof(CImg<T>)*(_width - 1 - npos));
  53391. std::memset((void*)_data,0,sizeof(CImg<T>)*(_width - 1));
  53392. delete[] _data;
  53393. _data = new_data;
  53394. } else if (npos!=_width - 1) // Insert without re-allocation.
  53395. std::memmove((void*)(_data + npos + 1),(void*)(_data + npos),sizeof(CImg<T>)*(_width - 1 - npos));
  53396. _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0;
  53397. _data[npos]._data = 0;
  53398. _data[npos] = img;
  53399. }
  53400. return *this;
  53401. }
  53402. //! Insert a copy of the image \c img into the current image list, at position \c pos \specialization.
  53403. CImgList<T>& insert(const CImg<T>& img, const unsigned int pos=~0U, const bool is_shared=false) {
  53404. const unsigned int npos = pos==~0U?_width:pos;
  53405. if (npos>_width)
  53406. throw CImgArgumentException(_cimglist_instance
  53407. "insert(): Invalid insertion request of specified image (%u,%u,%u,%u,%p) "
  53408. "at position %u.",
  53409. cimglist_instance,
  53410. img._width,img._height,img._depth,img._spectrum,img._data,npos);
  53411. CImg<T> *const new_data = (++_width>_allocated_width)?new CImg<T>[_allocated_width?(_allocated_width<<=1):
  53412. (_allocated_width=16)]:0;
  53413. if (!_data) { // Insert new element into empty list.
  53414. _data = new_data;
  53415. if (is_shared && img) {
  53416. _data->_width = img._width;
  53417. _data->_height = img._height;
  53418. _data->_depth = img._depth;
  53419. _data->_spectrum = img._spectrum;
  53420. _data->_is_shared = true;
  53421. _data->_data = img._data;
  53422. } else *_data = img;
  53423. }
  53424. else {
  53425. if (new_data) { // Insert with re-allocation.
  53426. if (npos) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg<T>)*npos);
  53427. if (npos!=_width - 1)
  53428. std::memcpy((void*)(new_data + npos + 1),(void*)(_data + npos),sizeof(CImg<T>)*(_width - 1 - npos));
  53429. if (is_shared && img) {
  53430. new_data[npos]._width = img._width;
  53431. new_data[npos]._height = img._height;
  53432. new_data[npos]._depth = img._depth;
  53433. new_data[npos]._spectrum = img._spectrum;
  53434. new_data[npos]._is_shared = true;
  53435. new_data[npos]._data = img._data;
  53436. } else {
  53437. new_data[npos]._width = new_data[npos]._height = new_data[npos]._depth = new_data[npos]._spectrum = 0;
  53438. new_data[npos]._data = 0;
  53439. new_data[npos] = img;
  53440. }
  53441. std::memset((void*)_data,0,sizeof(CImg<T>)*(_width - 1));
  53442. delete[] _data;
  53443. _data = new_data;
  53444. } else { // Insert without re-allocation.
  53445. if (npos!=_width - 1)
  53446. std::memmove((void*)(_data + npos + 1),(void*)(_data + npos),sizeof(CImg<T>)*(_width - 1 - npos));
  53447. if (is_shared && img) {
  53448. _data[npos]._width = img._width;
  53449. _data[npos]._height = img._height;
  53450. _data[npos]._depth = img._depth;
  53451. _data[npos]._spectrum = img._spectrum;
  53452. _data[npos]._is_shared = true;
  53453. _data[npos]._data = img._data;
  53454. } else {
  53455. _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0;
  53456. _data[npos]._data = 0;
  53457. _data[npos] = img;
  53458. }
  53459. }
  53460. }
  53461. return *this;
  53462. }
  53463. //! Insert a copy of the image \c img into the current image list, at position \c pos \newinstance.
  53464. template<typename t>
  53465. CImgList<T> get_insert(const CImg<t>& img, const unsigned int pos=~0U, const bool is_shared=false) const {
  53466. return (+*this).insert(img,pos,is_shared);
  53467. }
  53468. //! Insert n empty images img into the current image list, at position \p pos.
  53469. /**
  53470. \param n Number of empty images to insert.
  53471. \param pos Index of the insertion.
  53472. **/
  53473. CImgList<T>& insert(const unsigned int n, const unsigned int pos=~0U) {
  53474. CImg<T> empty;
  53475. if (!n) return *this;
  53476. const unsigned int npos = pos==~0U?_width:pos;
  53477. for (unsigned int i = 0; i<n; ++i) insert(empty,npos+i);
  53478. return *this;
  53479. }
  53480. //! Insert n empty images img into the current image list, at position \p pos \newinstance.
  53481. CImgList<T> get_insert(const unsigned int n, const unsigned int pos=~0U) const {
  53482. return (+*this).insert(n,pos);
  53483. }
  53484. //! Insert \c n copies of the image \c img into the current image list, at position \c pos.
  53485. /**
  53486. \param n Number of image copies to insert.
  53487. \param img Image to insert by copy.
  53488. \param pos Index of the insertion.
  53489. \param is_shared Tells if inserted images are shared copies of \c img or not.
  53490. **/
  53491. template<typename t>
  53492. CImgList<T>& insert(const unsigned int n, const CImg<t>& img, const unsigned int pos=~0U,
  53493. const bool is_shared=false) {
  53494. if (!n) return *this;
  53495. const unsigned int npos = pos==~0U?_width:pos;
  53496. insert(img,npos,is_shared);
  53497. for (unsigned int i = 1; i<n; ++i) insert(_data[npos],npos + i,is_shared);
  53498. return *this;
  53499. }
  53500. //! Insert \c n copies of the image \c img into the current image list, at position \c pos \newinstance.
  53501. template<typename t>
  53502. CImgList<T> get_insert(const unsigned int n, const CImg<t>& img, const unsigned int pos=~0U,
  53503. const bool is_shared=false) const {
  53504. return (+*this).insert(n,img,pos,is_shared);
  53505. }
  53506. //! Insert a copy of the image list \c list into the current image list, starting from position \c pos.
  53507. /**
  53508. \param list Image list to insert.
  53509. \param pos Index of the insertion.
  53510. \param is_shared Tells if inserted images are shared copies of images of \c list or not.
  53511. **/
  53512. template<typename t>
  53513. CImgList<T>& insert(const CImgList<t>& list, const unsigned int pos=~0U, const bool is_shared=false) {
  53514. const unsigned int npos = pos==~0U?_width:pos;
  53515. if ((void*)this!=(void*)&list) cimglist_for(list,l) insert(list[l],npos + l,is_shared);
  53516. else insert(CImgList<T>(list),npos,is_shared);
  53517. return *this;
  53518. }
  53519. //! Insert a copy of the image list \c list into the current image list, starting from position \c pos \newinstance.
  53520. template<typename t>
  53521. CImgList<T> get_insert(const CImgList<t>& list, const unsigned int pos=~0U, const bool is_shared=false) const {
  53522. return (+*this).insert(list,pos,is_shared);
  53523. }
  53524. //! Insert n copies of the list \c list at position \c pos of the current list.
  53525. /**
  53526. \param n Number of list copies to insert.
  53527. \param list Image list to insert.
  53528. \param pos Index of the insertion.
  53529. \param is_shared Tells if inserted images are shared copies of images of \c list or not.
  53530. **/
  53531. template<typename t>
  53532. CImgList<T>& insert(const unsigned int n, const CImgList<t>& list, const unsigned int pos=~0U,
  53533. const bool is_shared=false) {
  53534. if (!n) return *this;
  53535. const unsigned int npos = pos==~0U?_width:pos;
  53536. for (unsigned int i = 0; i<n; ++i) insert(list,npos,is_shared);
  53537. return *this;
  53538. }
  53539. //! Insert n copies of the list \c list at position \c pos of the current list \newinstance.
  53540. template<typename t>
  53541. CImgList<T> get_insert(const unsigned int n, const CImgList<t>& list, const unsigned int pos=~0U,
  53542. const bool is_shared=false) const {
  53543. return (+*this).insert(n,list,pos,is_shared);
  53544. }
  53545. //! Remove all images between from indexes.
  53546. /**
  53547. \param pos1 Starting index of the removal.
  53548. \param pos2 Ending index of the removal.
  53549. **/
  53550. CImgList<T>& remove(const unsigned int pos1, const unsigned int pos2) {
  53551. const unsigned int
  53552. npos1 = pos1<pos2?pos1:pos2,
  53553. tpos2 = pos1<pos2?pos2:pos1,
  53554. npos2 = tpos2<_width?tpos2:_width - 1;
  53555. if (npos1>=_width)
  53556. throw CImgArgumentException(_cimglist_instance
  53557. "remove(): Invalid remove request at positions %u->%u.",
  53558. cimglist_instance,
  53559. npos1,tpos2);
  53560. else {
  53561. if (tpos2>=_width)
  53562. throw CImgArgumentException(_cimglist_instance
  53563. "remove(): Invalid remove request at positions %u->%u.",
  53564. cimglist_instance,
  53565. npos1,tpos2);
  53566. for (unsigned int k = npos1; k<=npos2; ++k) _data[k].assign();
  53567. const unsigned int nb = 1 + npos2 - npos1;
  53568. if (!(_width-=nb)) return assign();
  53569. if (_width>(_allocated_width>>2) || _allocated_width<=16) { // Removing items without reallocation.
  53570. if (npos1!=_width)
  53571. std::memmove((void*)(_data + npos1),(void*)(_data + npos2 + 1),sizeof(CImg<T>)*(_width - npos1));
  53572. std::memset((void*)(_data + _width),0,sizeof(CImg<T>)*nb);
  53573. } else { // Removing items with reallocation.
  53574. _allocated_width>>=2;
  53575. while (_allocated_width>16 && _width<(_allocated_width>>1)) _allocated_width>>=1;
  53576. CImg<T> *const new_data = new CImg<T>[_allocated_width];
  53577. if (npos1) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg<T>)*npos1);
  53578. if (npos1!=_width)
  53579. std::memcpy((void*)(new_data + npos1),(void*)(_data + npos2 + 1),sizeof(CImg<T>)*(_width - npos1));
  53580. if (_width!=_allocated_width)
  53581. std::memset((void*)(new_data + _width),0,sizeof(CImg<T>)*(_allocated_width - _width));
  53582. std::memset((void*)_data,0,sizeof(CImg<T>)*(_width + nb));
  53583. delete[] _data;
  53584. _data = new_data;
  53585. }
  53586. }
  53587. return *this;
  53588. }
  53589. //! Remove all images between from indexes \newinstance.
  53590. CImgList<T> get_remove(const unsigned int pos1, const unsigned int pos2) const {
  53591. return (+*this).remove(pos1,pos2);
  53592. }
  53593. //! Remove image at index \c pos from the image list.
  53594. /**
  53595. \param pos Index of the image to remove.
  53596. **/
  53597. CImgList<T>& remove(const unsigned int pos) {
  53598. return remove(pos,pos);
  53599. }
  53600. //! Remove image at index \c pos from the image list \newinstance.
  53601. CImgList<T> get_remove(const unsigned int pos) const {
  53602. return (+*this).remove(pos);
  53603. }
  53604. //! Remove last image.
  53605. /**
  53606. **/
  53607. CImgList<T>& remove() {
  53608. return remove(_width - 1);
  53609. }
  53610. //! Remove last image \newinstance.
  53611. CImgList<T> get_remove() const {
  53612. return (+*this).remove();
  53613. }
  53614. //! Reverse list order.
  53615. CImgList<T>& reverse() {
  53616. for (unsigned int l = 0; l<_width/2; ++l) (*this)[l].swap((*this)[_width - 1 - l]);
  53617. return *this;
  53618. }
  53619. //! Reverse list order \newinstance.
  53620. CImgList<T> get_reverse() const {
  53621. return (+*this).reverse();
  53622. }
  53623. //! Return a sublist.
  53624. /**
  53625. \param pos0 Starting index of the sublist.
  53626. \param pos1 Ending index of the sublist.
  53627. **/
  53628. CImgList<T>& images(const unsigned int pos0, const unsigned int pos1) {
  53629. return get_images(pos0,pos1).move_to(*this);
  53630. }
  53631. //! Return a sublist \newinstance.
  53632. CImgList<T> get_images(const unsigned int pos0, const unsigned int pos1) const {
  53633. if (pos0>pos1 || pos1>=_width)
  53634. throw CImgArgumentException(_cimglist_instance
  53635. "images(): Specified sub-list indices (%u->%u) are out of bounds.",
  53636. cimglist_instance,
  53637. pos0,pos1);
  53638. CImgList<T> res(pos1 - pos0 + 1);
  53639. cimglist_for(res,l) res[l].assign(_data[pos0 + l]);
  53640. return res;
  53641. }
  53642. //! Return a shared sublist.
  53643. /**
  53644. \param pos0 Starting index of the sublist.
  53645. \param pos1 Ending index of the sublist.
  53646. **/
  53647. CImgList<T> get_shared_images(const unsigned int pos0, const unsigned int pos1) {
  53648. if (pos0>pos1 || pos1>=_width)
  53649. throw CImgArgumentException(_cimglist_instance
  53650. "get_shared_images(): Specified sub-list indices (%u->%u) are out of bounds.",
  53651. cimglist_instance,
  53652. pos0,pos1);
  53653. CImgList<T> res(pos1 - pos0 + 1);
  53654. cimglist_for(res,l) res[l].assign(_data[pos0 + l],_data[pos0 + l]?true:false);
  53655. return res;
  53656. }
  53657. //! Return a shared sublist \newinstance.
  53658. const CImgList<T> get_shared_images(const unsigned int pos0, const unsigned int pos1) const {
  53659. if (pos0>pos1 || pos1>=_width)
  53660. throw CImgArgumentException(_cimglist_instance
  53661. "get_shared_images(): Specified sub-list indices (%u->%u) are out of bounds.",
  53662. cimglist_instance,
  53663. pos0,pos1);
  53664. CImgList<T> res(pos1 - pos0 + 1);
  53665. cimglist_for(res,l) res[l].assign(_data[pos0 + l],_data[pos0 + l]?true:false);
  53666. return res;
  53667. }
  53668. //! Return a single image which is the appending of all images of the current CImgList instance.
  53669. /**
  53670. \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
  53671. \param align Appending alignment.
  53672. **/
  53673. CImg<T> get_append(const char axis, const float align=0) const {
  53674. if (is_empty()) return CImg<T>();
  53675. if (_width==1) return +((*this)[0]);
  53676. unsigned int dx = 0, dy = 0, dz = 0, dc = 0, pos = 0;
  53677. CImg<T> res;
  53678. switch (cimg::lowercase(axis)) {
  53679. case 'x' : { // Along the X-axis.
  53680. cimglist_for(*this,l) {
  53681. const CImg<T>& img = (*this)[l];
  53682. if (img) {
  53683. dx+=img._width;
  53684. dy = std::max(dy,img._height);
  53685. dz = std::max(dz,img._depth);
  53686. dc = std::max(dc,img._spectrum);
  53687. }
  53688. }
  53689. res.assign(dx,dy,dz,dc,(T)0);
  53690. if (res) cimglist_for(*this,l) {
  53691. const CImg<T>& img = (*this)[l];
  53692. if (img) res.draw_image(pos,
  53693. (int)(align*(dy - img._height)),
  53694. (int)(align*(dz - img._depth)),
  53695. (int)(align*(dc - img._spectrum)),
  53696. img);
  53697. pos+=img._width;
  53698. }
  53699. } break;
  53700. case 'y' : { // Along the Y-axis.
  53701. cimglist_for(*this,l) {
  53702. const CImg<T>& img = (*this)[l];
  53703. if (img) {
  53704. dx = std::max(dx,img._width);
  53705. dy+=img._height;
  53706. dz = std::max(dz,img._depth);
  53707. dc = std::max(dc,img._spectrum);
  53708. }
  53709. }
  53710. res.assign(dx,dy,dz,dc,(T)0);
  53711. if (res) cimglist_for(*this,l) {
  53712. const CImg<T>& img = (*this)[l];
  53713. if (img) res.draw_image((int)(align*(dx - img._width)),
  53714. pos,
  53715. (int)(align*(dz - img._depth)),
  53716. (int)(align*(dc - img._spectrum)),
  53717. img);
  53718. pos+=img._height;
  53719. }
  53720. } break;
  53721. case 'z' : { // Along the Z-axis.
  53722. cimglist_for(*this,l) {
  53723. const CImg<T>& img = (*this)[l];
  53724. if (img) {
  53725. dx = std::max(dx,img._width);
  53726. dy = std::max(dy,img._height);
  53727. dz+=img._depth;
  53728. dc = std::max(dc,img._spectrum);
  53729. }
  53730. }
  53731. res.assign(dx,dy,dz,dc,(T)0);
  53732. if (res) cimglist_for(*this,l) {
  53733. const CImg<T>& img = (*this)[l];
  53734. if (img) res.draw_image((int)(align*(dx - img._width)),
  53735. (int)(align*(dy - img._height)),
  53736. pos,
  53737. (int)(align*(dc - img._spectrum)),
  53738. img);
  53739. pos+=img._depth;
  53740. }
  53741. } break;
  53742. default : { // Along the C-axis.
  53743. cimglist_for(*this,l) {
  53744. const CImg<T>& img = (*this)[l];
  53745. if (img) {
  53746. dx = std::max(dx,img._width);
  53747. dy = std::max(dy,img._height);
  53748. dz = std::max(dz,img._depth);
  53749. dc+=img._spectrum;
  53750. }
  53751. }
  53752. res.assign(dx,dy,dz,dc,(T)0);
  53753. if (res) cimglist_for(*this,l) {
  53754. const CImg<T>& img = (*this)[l];
  53755. if (img) res.draw_image((int)(align*(dx - img._width)),
  53756. (int)(align*(dy - img._height)),
  53757. (int)(align*(dz - img._depth)),
  53758. pos,
  53759. img);
  53760. pos+=img._spectrum;
  53761. }
  53762. }
  53763. }
  53764. return res;
  53765. }
  53766. //! Return a list where each image has been split along the specified axis.
  53767. /**
  53768. \param axis Axis to split images along.
  53769. \param nb Number of spliting parts for each image.
  53770. **/
  53771. CImgList<T>& split(const char axis, const int nb=-1) {
  53772. return get_split(axis,nb).move_to(*this);
  53773. }
  53774. //! Return a list where each image has been split along the specified axis \newinstance.
  53775. CImgList<T> get_split(const char axis, const int nb=-1) const {
  53776. CImgList<T> res;
  53777. cimglist_for(*this,l) _data[l].get_split(axis,nb).move_to(res,~0U);
  53778. return res;
  53779. }
  53780. //! Insert image at the end of the list.
  53781. /**
  53782. \param img Image to insert.
  53783. **/
  53784. template<typename t>
  53785. CImgList<T>& push_back(const CImg<t>& img) {
  53786. return insert(img);
  53787. }
  53788. //! Insert image at the front of the list.
  53789. /**
  53790. \param img Image to insert.
  53791. **/
  53792. template<typename t>
  53793. CImgList<T>& push_front(const CImg<t>& img) {
  53794. return insert(img,0);
  53795. }
  53796. //! Insert list at the end of the current list.
  53797. /**
  53798. \param list List to insert.
  53799. **/
  53800. template<typename t>
  53801. CImgList<T>& push_back(const CImgList<t>& list) {
  53802. return insert(list);
  53803. }
  53804. //! Insert list at the front of the current list.
  53805. /**
  53806. \param list List to insert.
  53807. **/
  53808. template<typename t>
  53809. CImgList<T>& push_front(const CImgList<t>& list) {
  53810. return insert(list,0);
  53811. }
  53812. //! Remove last image.
  53813. /**
  53814. **/
  53815. CImgList<T>& pop_back() {
  53816. return remove(_width - 1);
  53817. }
  53818. //! Remove first image.
  53819. /**
  53820. **/
  53821. CImgList<T>& pop_front() {
  53822. return remove(0);
  53823. }
  53824. //! Remove image pointed by iterator.
  53825. /**
  53826. \param iter Iterator pointing to the image to remove.
  53827. **/
  53828. CImgList<T>& erase(const iterator iter) {
  53829. return remove(iter - _data);
  53830. }
  53831. //@}
  53832. //----------------------------------
  53833. //
  53834. //! \name Data Input
  53835. //@{
  53836. //----------------------------------
  53837. //! Display a simple interactive interface to select images or sublists.
  53838. /**
  53839. \param disp Window instance to display selection and user interface.
  53840. \param feature_type Can be \c false to select a single image, or \c true to select a sublist.
  53841. \param axis Axis along whom images are appended for visualization.
  53842. \param align Alignment setting when images have not all the same size.
  53843. \param exit_on_anykey Exit function when any key is pressed.
  53844. \return A one-column vector containing the selected image indexes.
  53845. **/
  53846. CImg<intT> get_select(CImgDisplay &disp, const bool feature_type=true,
  53847. const char axis='x', const float align=0,
  53848. const bool exit_on_anykey=false) const {
  53849. return _select(disp,0,feature_type,axis,align,exit_on_anykey,0,false,false,false);
  53850. }
  53851. //! Display a simple interactive interface to select images or sublists.
  53852. /**
  53853. \param title Title of a new window used to display selection and user interface.
  53854. \param feature_type Can be \c false to select a single image, or \c true to select a sublist.
  53855. \param axis Axis along whom images are appended for visualization.
  53856. \param align Alignment setting when images have not all the same size.
  53857. \param exit_on_anykey Exit function when any key is pressed.
  53858. \return A one-column vector containing the selected image indexes.
  53859. **/
  53860. CImg<intT> get_select(const char *const title, const bool feature_type=true,
  53861. const char axis='x', const float align=0,
  53862. const bool exit_on_anykey=false) const {
  53863. CImgDisplay disp;
  53864. return _select(disp,title,feature_type,axis,align,exit_on_anykey,0,false,false,false);
  53865. }
  53866. CImg<intT> _select(CImgDisplay &disp, const char *const title, const bool feature_type,
  53867. const char axis, const float align, const bool exit_on_anykey,
  53868. const unsigned int orig, const bool resize_disp,
  53869. const bool exit_on_rightbutton, const bool exit_on_wheel) const {
  53870. if (is_empty())
  53871. throw CImgInstanceException(_cimglist_instance
  53872. "select(): Empty instance.",
  53873. cimglist_instance);
  53874. // Create image correspondence table and get list dimensions for visualization.
  53875. CImgList<uintT> _indices;
  53876. unsigned int max_width = 0, max_height = 0, sum_width = 0, sum_height = 0;
  53877. cimglist_for(*this,l) {
  53878. const CImg<T>& img = _data[l];
  53879. const unsigned int
  53880. w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
  53881. h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
  53882. if (w>max_width) max_width = w;
  53883. if (h>max_height) max_height = h;
  53884. sum_width+=w; sum_height+=h;
  53885. if (axis=='x') CImg<uintT>(w,1,1,1,(unsigned int)l).move_to(_indices);
  53886. else CImg<uintT>(h,1,1,1,(unsigned int)l).move_to(_indices);
  53887. }
  53888. const CImg<uintT> indices0 = _indices>'x';
  53889. // Create display window.
  53890. if (!disp) {
  53891. if (axis=='x') disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:0,1);
  53892. else disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:0,1);
  53893. if (!title) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width);
  53894. } else if (title) disp.set_title("%s",title);
  53895. if (resize_disp) {
  53896. if (axis=='x') disp.resize(cimg_fitscreen(sum_width,max_height,1),false);
  53897. else disp.resize(cimg_fitscreen(max_width,sum_height,1),false);
  53898. }
  53899. const unsigned int old_normalization = disp.normalization();
  53900. bool old_is_resized = disp.is_resized();
  53901. disp._normalization = 0;
  53902. disp.show().set_key(0);
  53903. static const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 };
  53904. // Enter event loop.
  53905. CImg<ucharT> visu0, visu;
  53906. CImg<uintT> indices;
  53907. CImg<intT> positions(_width,4,1,1,-1);
  53908. int oindice0 = -1, oindice1 = -1, indice0 = -1, indice1 = -1;
  53909. bool is_clicked = false, is_selected = false, text_down = false, update_display = true;
  53910. unsigned int key = 0;
  53911. while (!is_selected && !disp.is_closed() && !key) {
  53912. // Create background image.
  53913. if (!visu0) {
  53914. visu0.assign(disp._width,disp._height,1,3,0); visu.assign();
  53915. (indices0.get_resize(axis=='x'?visu0._width:visu0._height,1)).move_to(indices);
  53916. unsigned int ind = 0;
  53917. const CImg<T> onexone(1,1,1,1,(T)0);
  53918. if (axis=='x')
  53919. cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=4))
  53920. cimglist_for(*this,ind) {
  53921. unsigned int x0 = 0;
  53922. while (x0<visu0._width && indices[x0++]!=(unsigned int)ind) {}
  53923. unsigned int x1 = x0;
  53924. while (x1<visu0._width && indices[x1++]==(unsigned int)ind) {}
  53925. const CImg<T> &src = _data[ind]?_data[ind]:onexone;
  53926. CImg<ucharT> res;
  53927. src.__get_select(disp,old_normalization,(src._width - 1)/2,(src._height - 1)/2,(src._depth - 1)/2).
  53928. move_to(res);
  53929. const unsigned int h = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,true);
  53930. res.resize(x1 - x0,std::max(32U,h*disp._height/max_height),1,res._spectrum==1?3:-100);
  53931. positions(ind,0) = positions(ind,2) = (int)x0;
  53932. positions(ind,1) = positions(ind,3) = (int)(align*(visu0.height() - res.height()));
  53933. positions(ind,2)+=res._width;
  53934. positions(ind,3)+=res._height - 1;
  53935. visu0.draw_image(positions(ind,0),positions(ind,1),res);
  53936. }
  53937. else
  53938. cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=4))
  53939. cimglist_for(*this,ind) {
  53940. unsigned int y0 = 0;
  53941. while (y0<visu0._height && indices[y0++]!=(unsigned int)ind) {}
  53942. unsigned int y1 = y0;
  53943. while (y1<visu0._height && indices[y1++]==(unsigned int)ind) {}
  53944. const CImg<T> &src = _data[ind]?_data[ind]:onexone;
  53945. CImg<ucharT> res;
  53946. src.__get_select(disp,old_normalization,(src._width - 1)/2,(src._height - 1)/2,(src._depth - 1)/2).
  53947. move_to(res);
  53948. const unsigned int w = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,false);
  53949. res.resize(std::max(32U,w*disp._width/max_width),y1 - y0,1,res._spectrum==1?3:-100);
  53950. positions(ind,0) = positions(ind,2) = (int)(align*(visu0.width() - res.width()));
  53951. positions(ind,1) = positions(ind,3) = (int)y0;
  53952. positions(ind,2)+=res._width - 1;
  53953. positions(ind,3)+=res._height;
  53954. visu0.draw_image(positions(ind,0),positions(ind,1),res);
  53955. }
  53956. if (axis=='x') --positions(ind,2); else --positions(ind,3);
  53957. update_display = true;
  53958. }
  53959. if (!visu || oindice0!=indice0 || oindice1!=indice1) {
  53960. if (indice0>=0 && indice1>=0) {
  53961. visu.assign(visu0,false);
  53962. const int indm = std::min(indice0,indice1), indM = std::max(indice0,indice1);
  53963. for (int ind = indm; ind<=indM; ++ind) if (positions(ind,0)>=0) {
  53964. visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3),
  53965. background_color,0.2f);
  53966. if ((axis=='x' && positions(ind,2) - positions(ind,0)>=8) ||
  53967. (axis!='x' && positions(ind,3) - positions(ind,1)>=8))
  53968. visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3),
  53969. foreground_color,0.9f,0xAAAAAAAA);
  53970. }
  53971. const int yt = (int)text_down?visu.height() - 13:0;
  53972. if (is_clicked) visu.draw_text(0,yt," Images #%u - #%u, Size = %u",
  53973. foreground_color,background_color,0.7f,13,
  53974. orig + indm,orig + indM,indM - indm + 1);
  53975. else visu.draw_text(0,yt," Image #%u (%u,%u,%u,%u)",foreground_color,background_color,0.7f,13,
  53976. orig + indice0,
  53977. _data[indice0]._width,
  53978. _data[indice0]._height,
  53979. _data[indice0]._depth,
  53980. _data[indice0]._spectrum);
  53981. update_display = true;
  53982. } else visu.assign();
  53983. }
  53984. if (!visu) { visu.assign(visu0,true); update_display = true; }
  53985. if (update_display) { visu.display(disp); update_display = false; }
  53986. disp.wait();
  53987. // Manage user events.
  53988. const int xm = disp.mouse_x(), ym = disp.mouse_y();
  53989. int indice = -1;
  53990. if (xm>=0) {
  53991. indice = (int)indices(axis=='x'?xm:ym);
  53992. if (disp.button()&1) {
  53993. if (!is_clicked) { is_clicked = true; oindice0 = indice0; indice0 = indice; }
  53994. oindice1 = indice1; indice1 = indice;
  53995. if (!feature_type) is_selected = true;
  53996. } else {
  53997. if (!is_clicked) { oindice0 = oindice1 = indice0; indice0 = indice1 = indice; }
  53998. else is_selected = true;
  53999. }
  54000. } else {
  54001. if (is_clicked) {
  54002. if (!(disp.button()&1)) { is_clicked = is_selected = false; indice0 = indice1 = -1; }
  54003. else indice1 = -1;
  54004. } else indice0 = indice1 = -1;
  54005. }
  54006. if (disp.button()&4) { is_clicked = is_selected = false; indice0 = indice1 = -1; }
  54007. if (disp.button()&2 && exit_on_rightbutton) { is_selected = true; indice1 = indice0 = -1; }
  54008. if (disp.wheel() && exit_on_wheel) is_selected = true;
  54009. CImg<charT> filename(32);
  54010. switch (key = disp.key()) {
  54011. #if cimg_OS!=2
  54012. case cimg::keyCTRLRIGHT :
  54013. #endif
  54014. case 0 : case cimg::keyCTRLLEFT : key = 0; break;
  54015. case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  54016. disp.set_fullscreen(false).
  54017. resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
  54018. CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
  54019. _is_resized = true;
  54020. disp.set_key(key,false); key = 0; visu0.assign();
  54021. } break;
  54022. case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  54023. disp.set_fullscreen(false).
  54024. resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
  54025. disp.set_key(key,false); key = 0; visu0.assign();
  54026. } break;
  54027. case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  54028. disp.set_fullscreen(false).
  54029. resize(cimg_fitscreen(axis=='x'?sum_width:max_width,axis=='x'?max_height:sum_height,1),false).
  54030. _is_resized = true;
  54031. disp.set_key(key,false); key = 0; visu0.assign();
  54032. } break;
  54033. case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  54034. disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
  54035. disp.set_key(key,false); key = 0; visu0.assign();
  54036. } break;
  54037. case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  54038. static unsigned int snap_number = 0;
  54039. std::FILE *file;
  54040. do {
  54041. cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++);
  54042. if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
  54043. } while (file);
  54044. if (visu0) {
  54045. (+visu0).draw_text(0,0," Saving snapshot... ",
  54046. foreground_color,background_color,0.7f,13).display(disp);
  54047. visu0.save(filename);
  54048. (+visu0).draw_text(0,0," Snapshot '%s' saved. ",
  54049. foreground_color,background_color,0.7f,13,filename._data).display(disp);
  54050. }
  54051. disp.set_key(key,false).wait(); key = 0;
  54052. } break;
  54053. case cimg::keyO :
  54054. if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  54055. static unsigned int snap_number = 0;
  54056. std::FILE *file;
  54057. do {
  54058. #ifdef cimg_use_zlib
  54059. cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++);
  54060. #else
  54061. cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++);
  54062. #endif
  54063. if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
  54064. } while (file);
  54065. (+visu0).draw_text(0,0," Saving instance... ",
  54066. foreground_color,background_color,0.7f,13).display(disp);
  54067. save(filename);
  54068. (+visu0).draw_text(0,0," Instance '%s' saved. ",
  54069. foreground_color,background_color,0.7f,13,filename._data).display(disp);
  54070. disp.set_key(key,false).wait(); key = 0;
  54071. } break;
  54072. }
  54073. if (disp.is_resized()) { disp.resize(false); visu0.assign(); }
  54074. if (ym>=0 && ym<13) { if (!text_down) { visu.assign(); text_down = true; }}
  54075. else if (ym>=visu.height() - 13) { if (text_down) { visu.assign(); text_down = false; }}
  54076. if (!exit_on_anykey && key && key!=cimg::keyESC &&
  54077. (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
  54078. key = 0;
  54079. }
  54080. }
  54081. CImg<intT> res(1,2,1,1,-1);
  54082. if (is_selected) {
  54083. if (feature_type) res.fill(std::min(indice0,indice1),std::max(indice0,indice1));
  54084. else res.fill(indice0);
  54085. }
  54086. if (!(disp.button()&2)) disp.set_button();
  54087. disp._normalization = old_normalization;
  54088. disp._is_resized = old_is_resized;
  54089. disp.set_key(key);
  54090. return res;
  54091. }
  54092. //! Load a list from a file.
  54093. /**
  54094. \param filename Filename to read data from.
  54095. **/
  54096. CImgList<T>& load(const char *const filename) {
  54097. if (!filename)
  54098. throw CImgArgumentException(_cimglist_instance
  54099. "load(): Specified filename is (null).",
  54100. cimglist_instance);
  54101. if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) {
  54102. CImg<charT> filename_local(256);
  54103. load(cimg::load_network(filename,filename_local));
  54104. std::remove(filename_local);
  54105. return *this;
  54106. }
  54107. const bool is_stdin = *filename=='-' && (!filename[1] || filename[1]=='.');
  54108. const char *const ext = cimg::split_filename(filename);
  54109. const unsigned int omode = cimg::exception_mode();
  54110. cimg::exception_mode(0);
  54111. bool is_loaded = true;
  54112. try {
  54113. #ifdef cimglist_load_plugin
  54114. cimglist_load_plugin(filename);
  54115. #endif
  54116. #ifdef cimglist_load_plugin1
  54117. cimglist_load_plugin1(filename);
  54118. #endif
  54119. #ifdef cimglist_load_plugin2
  54120. cimglist_load_plugin2(filename);
  54121. #endif
  54122. #ifdef cimglist_load_plugin3
  54123. cimglist_load_plugin3(filename);
  54124. #endif
  54125. #ifdef cimglist_load_plugin4
  54126. cimglist_load_plugin4(filename);
  54127. #endif
  54128. #ifdef cimglist_load_plugin5
  54129. cimglist_load_plugin5(filename);
  54130. #endif
  54131. #ifdef cimglist_load_plugin6
  54132. cimglist_load_plugin6(filename);
  54133. #endif
  54134. #ifdef cimglist_load_plugin7
  54135. cimglist_load_plugin7(filename);
  54136. #endif
  54137. #ifdef cimglist_load_plugin8
  54138. cimglist_load_plugin8(filename);
  54139. #endif
  54140. if (!cimg::strcasecmp(ext,"tif") ||
  54141. !cimg::strcasecmp(ext,"tiff")) load_tiff(filename);
  54142. else if (!cimg::strcasecmp(ext,"gif")) load_gif_external(filename);
  54143. else if (!cimg::strcasecmp(ext,"cimg") ||
  54144. !cimg::strcasecmp(ext,"cimgz") ||
  54145. !*ext) load_cimg(filename);
  54146. else if (!cimg::strcasecmp(ext,"rec") ||
  54147. !cimg::strcasecmp(ext,"par")) load_parrec(filename);
  54148. else if (!cimg::strcasecmp(ext,"avi") ||
  54149. !cimg::strcasecmp(ext,"mov") ||
  54150. !cimg::strcasecmp(ext,"asf") ||
  54151. !cimg::strcasecmp(ext,"divx") ||
  54152. !cimg::strcasecmp(ext,"flv") ||
  54153. !cimg::strcasecmp(ext,"mpg") ||
  54154. !cimg::strcasecmp(ext,"m1v") ||
  54155. !cimg::strcasecmp(ext,"m2v") ||
  54156. !cimg::strcasecmp(ext,"m4v") ||
  54157. !cimg::strcasecmp(ext,"mjp") ||
  54158. !cimg::strcasecmp(ext,"mp4") ||
  54159. !cimg::strcasecmp(ext,"mkv") ||
  54160. !cimg::strcasecmp(ext,"mpe") ||
  54161. !cimg::strcasecmp(ext,"movie") ||
  54162. !cimg::strcasecmp(ext,"ogm") ||
  54163. !cimg::strcasecmp(ext,"ogg") ||
  54164. !cimg::strcasecmp(ext,"ogv") ||
  54165. !cimg::strcasecmp(ext,"qt") ||
  54166. !cimg::strcasecmp(ext,"rm") ||
  54167. !cimg::strcasecmp(ext,"vob") ||
  54168. !cimg::strcasecmp(ext,"wmv") ||
  54169. !cimg::strcasecmp(ext,"xvid") ||
  54170. !cimg::strcasecmp(ext,"mpeg")) load_video(filename);
  54171. else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename);
  54172. else is_loaded = false;
  54173. } catch (CImgIOException&) { is_loaded = false; }
  54174. // If nothing loaded, try to guess file format from magic number in file.
  54175. if (!is_loaded && !is_stdin) {
  54176. std::FILE *const file = std_fopen(filename,"rb");
  54177. if (!file) {
  54178. cimg::exception_mode(omode);
  54179. throw CImgIOException(_cimglist_instance
  54180. "load(): Failed to open file '%s'.",
  54181. cimglist_instance,
  54182. filename);
  54183. }
  54184. const char *const f_type = cimg::ftype(file,filename);
  54185. std::fclose(file);
  54186. is_loaded = true;
  54187. try {
  54188. if (!cimg::strcasecmp(f_type,"gif")) load_gif_external(filename);
  54189. else if (!cimg::strcasecmp(f_type,"tif")) load_tiff(filename);
  54190. else is_loaded = false;
  54191. } catch (CImgIOException&) { is_loaded = false; }
  54192. }
  54193. // If nothing loaded, try to load file as a single image.
  54194. if (!is_loaded) {
  54195. assign(1);
  54196. try {
  54197. _data->load(filename);
  54198. } catch (CImgIOException&) {
  54199. cimg::exception_mode(omode);
  54200. throw CImgIOException(_cimglist_instance
  54201. "load(): Failed to recognize format of file '%s'.",
  54202. cimglist_instance,
  54203. filename);
  54204. }
  54205. }
  54206. cimg::exception_mode(omode);
  54207. return *this;
  54208. }
  54209. //! Load a list from a file \newinstance.
  54210. static CImgList<T> get_load(const char *const filename) {
  54211. return CImgList<T>().load(filename);
  54212. }
  54213. //! Load a list from a .cimg file.
  54214. /**
  54215. \param filename Filename to read data from.
  54216. **/
  54217. CImgList<T>& load_cimg(const char *const filename) {
  54218. return _load_cimg(0,filename);
  54219. }
  54220. //! Load a list from a .cimg file \newinstance.
  54221. static CImgList<T> get_load_cimg(const char *const filename) {
  54222. return CImgList<T>().load_cimg(filename);
  54223. }
  54224. //! Load a list from a .cimg file.
  54225. /**
  54226. \param file File to read data from.
  54227. **/
  54228. CImgList<T>& load_cimg(std::FILE *const file) {
  54229. return _load_cimg(file,0);
  54230. }
  54231. //! Load a list from a .cimg file \newinstance.
  54232. static CImgList<T> get_load_cimg(std::FILE *const file) {
  54233. return CImgList<T>().load_cimg(file);
  54234. }
  54235. CImgList<T>& _load_cimg(std::FILE *const file, const char *const filename) {
  54236. #ifdef cimg_use_zlib
  54237. #define _cimgz_load_cimg_case(Tss) { \
  54238. Bytef *const cbuf = new Bytef[csiz]; \
  54239. cimg::fread(cbuf,csiz,nfile); \
  54240. raw.assign(W,H,D,C); \
  54241. uLongf destlen = (ulongT)raw.size()*sizeof(Tss); \
  54242. uncompress((Bytef*)raw._data,&destlen,cbuf,csiz); \
  54243. delete[] cbuf; \
  54244. if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \
  54245. raw.move_to(img); \
  54246. }
  54247. #else
  54248. #define _cimgz_load_cimg_case(Tss) \
  54249. throw CImgIOException(_cimglist_instance \
  54250. "load_cimg(): Unable to load compressed data from file '%s' unless zlib is enabled.", \
  54251. cimglist_instance, \
  54252. filename?filename:"(FILE*)");
  54253. #endif
  54254. #define _cimg_load_cimg_case(Ts,Tss) \
  54255. if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \
  54256. for (unsigned int l = 0; l<N; ++l) { \
  54257. j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0 && j<255) tmp[j++] = (char)i; tmp[j] = 0; \
  54258. W = H = D = C = 0; csiz = 0; \
  54259. if ((err = cimg_sscanf(tmp,"%u %u %u %u #%lu",&W,&H,&D,&C,&csiz))<4) \
  54260. throw CImgIOException(_cimglist_instance \
  54261. "load_cimg(): Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'.", \
  54262. cimglist_instance, \
  54263. W,H,D,C,l,filename?filename:("(FILE*)")); \
  54264. if (W*H*D*C>0) { \
  54265. CImg<Tss> raw; \
  54266. CImg<T> &img = _data[l]; \
  54267. if (err==5) _cimgz_load_cimg_case(Tss) \
  54268. else { \
  54269. img.assign(W,H,D,C); \
  54270. T *ptrd = img._data; \
  54271. for (ulongT to_read = img.size(); to_read; ) { \
  54272. raw.assign((unsigned int)std::min(to_read,cimg_iobuffer)); \
  54273. cimg::fread(raw._data,raw._width,nfile); \
  54274. if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \
  54275. const Tss *ptrs = raw._data; \
  54276. for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \
  54277. to_read-=raw._width; \
  54278. } \
  54279. } \
  54280. } \
  54281. } \
  54282. loaded = true; \
  54283. }
  54284. if (!filename && !file)
  54285. throw CImgArgumentException(_cimglist_instance
  54286. "load_cimg(): Specified filename is (null).",
  54287. cimglist_instance);
  54288. const ulongT cimg_iobuffer = (ulongT)24*1024*1024;
  54289. std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
  54290. bool loaded = false, endian = cimg::endianness();
  54291. CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
  54292. *tmp = *str_pixeltype = *str_endian = 0;
  54293. unsigned int j, N = 0, W, H, D, C;
  54294. unsigned long csiz;
  54295. int i, err;
  54296. do {
  54297. j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0 && j<255) tmp[j++] = (char)i; tmp[j] = 0;
  54298. } while (*tmp=='#' && i>=0);
  54299. err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]",
  54300. &N,str_pixeltype._data,str_endian._data);
  54301. if (err<2) {
  54302. if (!file) cimg::fclose(nfile);
  54303. throw CImgIOException(_cimglist_instance
  54304. "load_cimg(): CImg header not found in file '%s'.",
  54305. cimglist_instance,
  54306. filename?filename:"(FILE*)");
  54307. }
  54308. if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
  54309. else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
  54310. assign(N);
  54311. _cimg_load_cimg_case("bool",bool);
  54312. _cimg_load_cimg_case("unsigned_char",unsigned char);
  54313. _cimg_load_cimg_case("uchar",unsigned char);
  54314. _cimg_load_cimg_case("char",char);
  54315. _cimg_load_cimg_case("unsigned_short",unsigned short);
  54316. _cimg_load_cimg_case("ushort",unsigned short);
  54317. _cimg_load_cimg_case("short",short);
  54318. _cimg_load_cimg_case("unsigned_int",unsigned int);
  54319. _cimg_load_cimg_case("uint",unsigned int);
  54320. _cimg_load_cimg_case("int",int);
  54321. _cimg_load_cimg_case("unsigned_long",ulongT);
  54322. _cimg_load_cimg_case("ulong",ulongT);
  54323. _cimg_load_cimg_case("long",longT);
  54324. _cimg_load_cimg_case("unsigned_int64",uint64T);
  54325. _cimg_load_cimg_case("uint64",uint64T);
  54326. _cimg_load_cimg_case("int64",int64T);
  54327. _cimg_load_cimg_case("float",float);
  54328. _cimg_load_cimg_case("double",double);
  54329. if (!loaded) {
  54330. if (!file) cimg::fclose(nfile);
  54331. throw CImgIOException(_cimglist_instance
  54332. "load_cimg(): Unsupported pixel type '%s' for file '%s'.",
  54333. cimglist_instance,
  54334. str_pixeltype._data,filename?filename:"(FILE*)");
  54335. }
  54336. if (!file) cimg::fclose(nfile);
  54337. return *this;
  54338. }
  54339. //! Load a sublist list from a (non compressed) .cimg file.
  54340. /**
  54341. \param filename Filename to read data from.
  54342. \param n0 Starting index of images to read (~0U for max).
  54343. \param n1 Ending index of images to read (~0U for max).
  54344. \param x0 Starting X-coordinates of image regions to read.
  54345. \param y0 Starting Y-coordinates of image regions to read.
  54346. \param z0 Starting Z-coordinates of image regions to read.
  54347. \param c0 Starting C-coordinates of image regions to read.
  54348. \param x1 Ending X-coordinates of image regions to read (~0U for max).
  54349. \param y1 Ending Y-coordinates of image regions to read (~0U for max).
  54350. \param z1 Ending Z-coordinates of image regions to read (~0U for max).
  54351. \param c1 Ending C-coordinates of image regions to read (~0U for max).
  54352. **/
  54353. CImgList<T>& load_cimg(const char *const filename,
  54354. const unsigned int n0, const unsigned int n1,
  54355. const unsigned int x0, const unsigned int y0,
  54356. const unsigned int z0, const unsigned int c0,
  54357. const unsigned int x1, const unsigned int y1,
  54358. const unsigned int z1, const unsigned int c1) {
  54359. return _load_cimg(0,filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
  54360. }
  54361. //! Load a sublist list from a (non compressed) .cimg file \newinstance.
  54362. static CImgList<T> get_load_cimg(const char *const filename,
  54363. const unsigned int n0, const unsigned int n1,
  54364. const unsigned int x0, const unsigned int y0,
  54365. const unsigned int z0, const unsigned int c0,
  54366. const unsigned int x1, const unsigned int y1,
  54367. const unsigned int z1, const unsigned int c1) {
  54368. return CImgList<T>().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
  54369. }
  54370. //! Load a sub-image list from a (non compressed) .cimg file \overloading.
  54371. CImgList<T>& load_cimg(std::FILE *const file,
  54372. const unsigned int n0, const unsigned int n1,
  54373. const unsigned int x0, const unsigned int y0,
  54374. const unsigned int z0, const unsigned int c0,
  54375. const unsigned int x1, const unsigned int y1,
  54376. const unsigned int z1, const unsigned int c1) {
  54377. return _load_cimg(file,0,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
  54378. }
  54379. //! Load a sub-image list from a (non compressed) .cimg file \newinstance.
  54380. static CImgList<T> get_load_cimg(std::FILE *const file,
  54381. const unsigned int n0, const unsigned int n1,
  54382. const unsigned int x0, const unsigned int y0,
  54383. const unsigned int z0, const unsigned int c0,
  54384. const unsigned int x1, const unsigned int y1,
  54385. const unsigned int z1, const unsigned int c1) {
  54386. return CImgList<T>().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
  54387. }
  54388. CImgList<T>& _load_cimg(std::FILE *const file, const char *const filename,
  54389. const unsigned int n0, const unsigned int n1,
  54390. const unsigned int x0, const unsigned int y0,
  54391. const unsigned int z0, const unsigned int c0,
  54392. const unsigned int x1, const unsigned int y1,
  54393. const unsigned int z1, const unsigned int c1) {
  54394. #define _cimg_load_cimg_case2(Ts,Tss) \
  54395. if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \
  54396. for (unsigned int l = 0; l<=nn1; ++l) { \
  54397. j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0) tmp[j++] = (char)i; tmp[j] = 0; \
  54398. W = H = D = C = 0; \
  54399. if (cimg_sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \
  54400. throw CImgIOException(_cimglist_instance \
  54401. "load_cimg(): Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'", \
  54402. cimglist_instance, \
  54403. W,H,D,C,l,filename?filename:"(FILE*)"); \
  54404. if (W*H*D*C>0) { \
  54405. if (l<nn0 || nx0>=W || ny0>=H || nz0>=D || nc0>=C) cimg::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \
  54406. else { \
  54407. const unsigned int \
  54408. _nx1 = nx1==~0U?W - 1:nx1, \
  54409. _ny1 = ny1==~0U?H - 1:ny1, \
  54410. _nz1 = nz1==~0U?D - 1:nz1, \
  54411. _nc1 = nc1==~0U?C - 1:nc1; \
  54412. if (_nx1>=W || _ny1>=H || _nz1>=D || _nc1>=C) \
  54413. throw CImgArgumentException(_cimglist_instance \
  54414. "load_cimg(): Invalid specified coordinates " \
  54415. "[%u](%u,%u,%u,%u) -> [%u](%u,%u,%u,%u) " \
  54416. "because image [%u] in file '%s' has size (%u,%u,%u,%u).", \
  54417. cimglist_instance, \
  54418. n0,x0,y0,z0,c0,n1,x1,y1,z1,c1,l,filename?filename:"(FILE*)",W,H,D,C); \
  54419. CImg<Tss> raw(1 + _nx1 - nx0); \
  54420. CImg<T> &img = _data[l - nn0]; \
  54421. img.assign(1 + _nx1 - nx0,1 + _ny1 - ny0,1 + _nz1 - nz0,1 + _nc1 - nc0); \
  54422. T *ptrd = img._data; \
  54423. ulongT skipvb = nc0*W*H*D*sizeof(Tss); \
  54424. if (skipvb) cimg::fseek(nfile,skipvb,SEEK_CUR); \
  54425. for (unsigned int c = 1 + _nc1 - nc0; c; --c) { \
  54426. const ulongT skipzb = nz0*W*H*sizeof(Tss); \
  54427. if (skipzb) cimg::fseek(nfile,skipzb,SEEK_CUR); \
  54428. for (unsigned int z = 1 + _nz1 - nz0; z; --z) { \
  54429. const ulongT skipyb = ny0*W*sizeof(Tss); \
  54430. if (skipyb) cimg::fseek(nfile,skipyb,SEEK_CUR); \
  54431. for (unsigned int y = 1 + _ny1 - ny0; y; --y) { \
  54432. const ulongT skipxb = nx0*sizeof(Tss); \
  54433. if (skipxb) cimg::fseek(nfile,skipxb,SEEK_CUR); \
  54434. cimg::fread(raw._data,raw._width,nfile); \
  54435. if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); \
  54436. const Tss *ptrs = raw._data; \
  54437. for (unsigned int off = raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \
  54438. const ulongT skipxe = (W - 1 - _nx1)*sizeof(Tss); \
  54439. if (skipxe) cimg::fseek(nfile,skipxe,SEEK_CUR); \
  54440. } \
  54441. const ulongT skipye = (H - 1 - _ny1)*W*sizeof(Tss); \
  54442. if (skipye) cimg::fseek(nfile,skipye,SEEK_CUR); \
  54443. } \
  54444. const ulongT skipze = (D - 1 - _nz1)*W*H*sizeof(Tss); \
  54445. if (skipze) cimg::fseek(nfile,skipze,SEEK_CUR); \
  54446. } \
  54447. const ulongT skipve = (C - 1 - _nc1)*W*H*D*sizeof(Tss); \
  54448. if (skipve) cimg::fseek(nfile,skipve,SEEK_CUR); \
  54449. } \
  54450. } \
  54451. } \
  54452. loaded = true; \
  54453. }
  54454. if (!filename && !file)
  54455. throw CImgArgumentException(_cimglist_instance
  54456. "load_cimg(): Specified filename is (null).",
  54457. cimglist_instance);
  54458. unsigned int
  54459. nn0 = std::min(n0,n1), nn1 = std::max(n0,n1),
  54460. nx0 = std::min(x0,x1), nx1 = std::max(x0,x1),
  54461. ny0 = std::min(y0,y1), ny1 = std::max(y0,y1),
  54462. nz0 = std::min(z0,z1), nz1 = std::max(z0,z1),
  54463. nc0 = std::min(c0,c1), nc1 = std::max(c0,c1);
  54464. std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
  54465. bool loaded = false, endian = cimg::endianness();
  54466. CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
  54467. *tmp = *str_pixeltype = *str_endian = 0;
  54468. unsigned int j, N, W, H, D, C;
  54469. int i, err;
  54470. j = 0; while ((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0;
  54471. err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]",
  54472. &N,str_pixeltype._data,str_endian._data);
  54473. if (err<2) {
  54474. if (!file) cimg::fclose(nfile);
  54475. throw CImgIOException(_cimglist_instance
  54476. "load_cimg(): CImg header not found in file '%s'.",
  54477. cimglist_instance,
  54478. filename?filename:"(FILE*)");
  54479. }
  54480. if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
  54481. else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
  54482. nn1 = n1==~0U?N - 1:n1;
  54483. if (nn1>=N)
  54484. throw CImgArgumentException(_cimglist_instance
  54485. "load_cimg(): Invalid specified coordinates [%u](%u,%u,%u,%u) -> [%u](%u,%u,%u,%u) "
  54486. "because file '%s' contains only %u images.",
  54487. cimglist_instance,
  54488. n0,x0,y0,z0,c0,n1,x1,y1,z1,c1,filename?filename:"(FILE*)",N);
  54489. assign(1 + nn1 - n0);
  54490. _cimg_load_cimg_case2("bool",bool);
  54491. _cimg_load_cimg_case2("unsigned_char",unsigned char);
  54492. _cimg_load_cimg_case2("uchar",unsigned char);
  54493. _cimg_load_cimg_case2("char",char);
  54494. _cimg_load_cimg_case2("unsigned_short",unsigned short);
  54495. _cimg_load_cimg_case2("ushort",unsigned short);
  54496. _cimg_load_cimg_case2("short",short);
  54497. _cimg_load_cimg_case2("unsigned_int",unsigned int);
  54498. _cimg_load_cimg_case2("uint",unsigned int);
  54499. _cimg_load_cimg_case2("int",int);
  54500. _cimg_load_cimg_case2("unsigned_long",ulongT);
  54501. _cimg_load_cimg_case2("ulong",ulongT);
  54502. _cimg_load_cimg_case2("long",longT);
  54503. _cimg_load_cimg_case2("unsigned_int64",uint64T);
  54504. _cimg_load_cimg_case2("uint64",uint64T);
  54505. _cimg_load_cimg_case2("int64",int64T);
  54506. _cimg_load_cimg_case2("float",float);
  54507. _cimg_load_cimg_case2("double",double);
  54508. if (!loaded) {
  54509. if (!file) cimg::fclose(nfile);
  54510. throw CImgIOException(_cimglist_instance
  54511. "load_cimg(): Unsupported pixel type '%s' for file '%s'.",
  54512. cimglist_instance,
  54513. str_pixeltype._data,filename?filename:"(FILE*)");
  54514. }
  54515. if (!file) cimg::fclose(nfile);
  54516. return *this;
  54517. }
  54518. //! Load a list from a PAR/REC (Philips) file.
  54519. /**
  54520. \param filename Filename to read data from.
  54521. **/
  54522. CImgList<T>& load_parrec(const char *const filename) {
  54523. if (!filename)
  54524. throw CImgArgumentException(_cimglist_instance
  54525. "load_parrec(): Specified filename is (null).",
  54526. cimglist_instance);
  54527. CImg<charT> body(1024), filenamepar(1024), filenamerec(1024);
  54528. *body = *filenamepar = *filenamerec = 0;
  54529. const char *const ext = cimg::split_filename(filename,body);
  54530. if (!std::strcmp(ext,"par")) {
  54531. std::strncpy(filenamepar,filename,filenamepar._width - 1);
  54532. cimg_snprintf(filenamerec,filenamerec._width,"%s.rec",body._data);
  54533. }
  54534. if (!std::strcmp(ext,"PAR")) {
  54535. std::strncpy(filenamepar,filename,filenamepar._width - 1);
  54536. cimg_snprintf(filenamerec,filenamerec._width,"%s.REC",body._data);
  54537. }
  54538. if (!std::strcmp(ext,"rec")) {
  54539. std::strncpy(filenamerec,filename,filenamerec._width - 1);
  54540. cimg_snprintf(filenamepar,filenamepar._width,"%s.par",body._data);
  54541. }
  54542. if (!std::strcmp(ext,"REC")) {
  54543. std::strncpy(filenamerec,filename,filenamerec._width - 1);
  54544. cimg_snprintf(filenamepar,filenamepar._width,"%s.PAR",body._data);
  54545. }
  54546. std::FILE *file = cimg::fopen(filenamepar,"r");
  54547. // Parse header file
  54548. CImgList<floatT> st_slices;
  54549. CImgList<uintT> st_global;
  54550. CImg<charT> line(256); *line = 0;
  54551. int err;
  54552. do { err = std::fscanf(file,"%255[^\n]%*c",line._data); } while (err!=EOF && (*line=='#' || *line=='.'));
  54553. do {
  54554. unsigned int sn,size_x,size_y,pixsize;
  54555. float rs,ri,ss;
  54556. 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);
  54557. if (err==7) {
  54558. CImg<floatT>::vector((float)sn,(float)pixsize,(float)size_x,(float)size_y,ri,rs,ss,0).move_to(st_slices);
  54559. unsigned int i; for (i = 0; i<st_global._width && sn<=st_global[i][2]; ++i) {}
  54560. if (i==st_global._width) CImg<uintT>::vector(size_x,size_y,sn).move_to(st_global);
  54561. else {
  54562. CImg<uintT> &vec = st_global[i];
  54563. if (size_x>vec[0]) vec[0] = size_x;
  54564. if (size_y>vec[1]) vec[1] = size_y;
  54565. vec[2] = sn;
  54566. }
  54567. st_slices[st_slices._width - 1][7] = (float)i;
  54568. }
  54569. } while (err==7);
  54570. // Read data
  54571. std::FILE *file2 = cimg::fopen(filenamerec,"rb");
  54572. cimglist_for(st_global,l) {
  54573. const CImg<uintT>& vec = st_global[l];
  54574. CImg<T>(vec[0],vec[1],vec[2]).move_to(*this);
  54575. }
  54576. cimglist_for(st_slices,l) {
  54577. const CImg<floatT>& vec = st_slices[l];
  54578. const unsigned int
  54579. sn = (unsigned int)vec[0] - 1,
  54580. pixsize = (unsigned int)vec[1],
  54581. size_x = (unsigned int)vec[2],
  54582. size_y = (unsigned int)vec[3],
  54583. imn = (unsigned int)vec[7];
  54584. const float ri = vec[4], rs = vec[5], ss = vec[6];
  54585. switch (pixsize) {
  54586. case 8 : {
  54587. CImg<ucharT> buf(size_x,size_y);
  54588. cimg::fread(buf._data,size_x*size_y,file2);
  54589. if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y);
  54590. CImg<T>& img = (*this)[imn];
  54591. cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
  54592. } break;
  54593. case 16 : {
  54594. CImg<ushortT> buf(size_x,size_y);
  54595. cimg::fread(buf._data,size_x*size_y,file2);
  54596. if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y);
  54597. CImg<T>& img = (*this)[imn];
  54598. cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
  54599. } break;
  54600. case 32 : {
  54601. CImg<uintT> buf(size_x,size_y);
  54602. cimg::fread(buf._data,size_x*size_y,file2);
  54603. if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y);
  54604. CImg<T>& img = (*this)[imn];
  54605. cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
  54606. } break;
  54607. default :
  54608. cimg::fclose(file);
  54609. cimg::fclose(file2);
  54610. throw CImgIOException(_cimglist_instance
  54611. "load_parrec(): Unsupported %d-bits pixel type for file '%s'.",
  54612. cimglist_instance,
  54613. pixsize,filename);
  54614. }
  54615. }
  54616. cimg::fclose(file);
  54617. cimg::fclose(file2);
  54618. if (!_width)
  54619. throw CImgIOException(_cimglist_instance
  54620. "load_parrec(): Failed to recognize valid PAR-REC data in file '%s'.",
  54621. cimglist_instance,
  54622. filename);
  54623. return *this;
  54624. }
  54625. //! Load a list from a PAR/REC (Philips) file \newinstance.
  54626. static CImgList<T> get_load_parrec(const char *const filename) {
  54627. return CImgList<T>().load_parrec(filename);
  54628. }
  54629. //! Load a list from a YUV image sequence file.
  54630. /**
  54631. \param filename Filename to read data from.
  54632. \param size_x Width of the images.
  54633. \param size_y Height of the images.
  54634. \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
  54635. \param first_frame Index of first image frame to read.
  54636. \param last_frame Index of last image frame to read.
  54637. \param step_frame Step applied between each frame.
  54638. \param yuv2rgb Apply YUV to RGB transformation during reading.
  54639. **/
  54640. CImgList<T>& load_yuv(const char *const filename,
  54641. const unsigned int size_x, const unsigned int size_y,
  54642. const unsigned int chroma_subsampling=444,
  54643. const unsigned int first_frame=0, const unsigned int last_frame=~0U,
  54644. const unsigned int step_frame=1, const bool yuv2rgb=true) {
  54645. return _load_yuv(0,filename,size_x,size_y,chroma_subsampling,
  54646. first_frame,last_frame,step_frame,yuv2rgb);
  54647. }
  54648. //! Load a list from a YUV image sequence file \newinstance.
  54649. static CImgList<T> get_load_yuv(const char *const filename,
  54650. const unsigned int size_x, const unsigned int size_y=1,
  54651. const unsigned int chroma_subsampling=444,
  54652. const unsigned int first_frame=0, const unsigned int last_frame=~0U,
  54653. const unsigned int step_frame=1, const bool yuv2rgb=true) {
  54654. return CImgList<T>().load_yuv(filename,size_x,size_y,chroma_subsampling,
  54655. first_frame,last_frame,step_frame,yuv2rgb);
  54656. }
  54657. //! Load a list from an image sequence YUV file \overloading.
  54658. CImgList<T>& load_yuv(std::FILE *const file,
  54659. const unsigned int size_x, const unsigned int size_y,
  54660. const unsigned int chroma_subsampling=444,
  54661. const unsigned int first_frame=0, const unsigned int last_frame=~0U,
  54662. const unsigned int step_frame=1, const bool yuv2rgb=true) {
  54663. return _load_yuv(file,0,size_x,size_y,chroma_subsampling,
  54664. first_frame,last_frame,step_frame,yuv2rgb);
  54665. }
  54666. //! Load a list from an image sequence YUV file \newinstance.
  54667. static CImgList<T> get_load_yuv(std::FILE *const file,
  54668. const unsigned int size_x, const unsigned int size_y=1,
  54669. const unsigned int chroma_subsampling=444,
  54670. const unsigned int first_frame=0, const unsigned int last_frame=~0U,
  54671. const unsigned int step_frame=1, const bool yuv2rgb=true) {
  54672. return CImgList<T>().load_yuv(file,size_x,size_y,chroma_subsampling,
  54673. first_frame,last_frame,step_frame,yuv2rgb);
  54674. }
  54675. CImgList<T>& _load_yuv(std::FILE *const file, const char *const filename,
  54676. const unsigned int size_x, const unsigned int size_y,
  54677. const unsigned int chroma_subsampling,
  54678. const unsigned int first_frame, const unsigned int last_frame,
  54679. const unsigned int step_frame, const bool yuv2rgb) {
  54680. if (!filename && !file)
  54681. throw CImgArgumentException(_cimglist_instance
  54682. "load_yuv(): Specified filename is (null).",
  54683. cimglist_instance);
  54684. if (chroma_subsampling!=420 && chroma_subsampling!=422 && chroma_subsampling!=444)
  54685. throw CImgArgumentException(_cimglist_instance
  54686. "load_yuv(): Specified chroma subsampling '%u' is invalid, for file '%s'.",
  54687. cimglist_instance,
  54688. chroma_subsampling,filename?filename:"(FILE*)");
  54689. const unsigned int
  54690. cfx = chroma_subsampling==420 || chroma_subsampling==422?2:1,
  54691. cfy = chroma_subsampling==420?2:1,
  54692. nfirst_frame = first_frame<last_frame?first_frame:last_frame,
  54693. nlast_frame = first_frame<last_frame?last_frame:first_frame,
  54694. nstep_frame = step_frame?step_frame:1;
  54695. if (!size_x || !size_y || size_x%cfx || size_y%cfy)
  54696. throw CImgArgumentException(_cimglist_instance
  54697. "load_yuv(): Specified dimensions (%u,%u) are invalid, for file '%s'.",
  54698. cimglist_instance,
  54699. size_x,size_y,filename?filename:"(FILE*)");
  54700. CImg<ucharT> YUV(size_x,size_y,1,3), UV(size_x/cfx,size_y/cfy,1,2);
  54701. std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
  54702. bool stop_flag = false;
  54703. int err;
  54704. if (nfirst_frame) {
  54705. err = cimg::fseek(nfile,(uint64T)nfirst_frame*(YUV._width*YUV._height + 2*UV._width*UV._height),SEEK_CUR);
  54706. if (err) {
  54707. if (!file) cimg::fclose(nfile);
  54708. throw CImgIOException(_cimglist_instance
  54709. "load_yuv(): File '%s' doesn't contain frame number %u.",
  54710. cimglist_instance,
  54711. filename?filename:"(FILE*)",nfirst_frame);
  54712. }
  54713. }
  54714. unsigned int frame;
  54715. for (frame = nfirst_frame; !stop_flag && frame<=nlast_frame; frame+=nstep_frame) {
  54716. YUV.get_shared_channel(0).fill(0);
  54717. // *TRY* to read the luminance part, do not replace by cimg::fread!
  54718. err = (int)std::fread((void*)(YUV._data),1,(size_t)YUV._width*YUV._height,nfile);
  54719. if (err!=(int)(YUV._width*YUV._height)) {
  54720. stop_flag = true;
  54721. if (err>0)
  54722. cimg::warn(_cimglist_instance
  54723. "load_yuv(): File '%s' contains incomplete data or given image dimensions "
  54724. "(%u,%u) are incorrect.",
  54725. cimglist_instance,
  54726. filename?filename:"(FILE*)",size_x,size_y);
  54727. } else {
  54728. UV.fill(0);
  54729. // *TRY* to read the luminance part, do not replace by cimg::fread!
  54730. err = (int)std::fread((void*)(UV._data),1,(size_t)UV.size(),nfile);
  54731. if (err!=(int)(UV.size())) {
  54732. stop_flag = true;
  54733. if (err>0)
  54734. cimg::warn(_cimglist_instance
  54735. "load_yuv(): File '%s' contains incomplete data or given image dimensions "
  54736. "(%u,%u) are incorrect.",
  54737. cimglist_instance,
  54738. filename?filename:"(FILE*)",size_x,size_y);
  54739. } else {
  54740. const ucharT *ptrs1 = UV._data, *ptrs2 = UV.data(0,0,0,1);
  54741. ucharT *ptrd1 = YUV.data(0,0,0,1), *ptrd2 = YUV.data(0,0,0,2);
  54742. const unsigned int wd = YUV._width;
  54743. switch (chroma_subsampling) {
  54744. case 420 :
  54745. cimg_forY(UV,y) {
  54746. cimg_forX(UV,x) {
  54747. const ucharT U = *(ptrs1++), V = *(ptrs2++);
  54748. ptrd1[wd] = U; *(ptrd1)++ = U;
  54749. ptrd1[wd] = U; *(ptrd1)++ = U;
  54750. ptrd2[wd] = V; *(ptrd2)++ = V;
  54751. ptrd2[wd] = V; *(ptrd2)++ = V;
  54752. }
  54753. ptrd1+=wd; ptrd2+=wd;
  54754. }
  54755. break;
  54756. case 422 :
  54757. cimg_forXY(UV,x,y) {
  54758. const ucharT U = *(ptrs1++), V = *(ptrs2++);
  54759. *(ptrd1++) = U; *(ptrd1++) = U;
  54760. *(ptrd2++) = V; *(ptrd2++) = V;
  54761. }
  54762. break;
  54763. default :
  54764. YUV.draw_image(0,0,0,1,UV);
  54765. }
  54766. if (yuv2rgb) YUV.YCbCrtoRGB();
  54767. insert(YUV);
  54768. if (nstep_frame>1) cimg::fseek(nfile,(uint64T)(nstep_frame - 1)*(size_x*size_y + size_x*size_y/2),SEEK_CUR);
  54769. }
  54770. }
  54771. }
  54772. if (is_empty())
  54773. throw CImgIOException(_cimglist_instance
  54774. "load_yuv() : Missing data in file '%s'.",
  54775. cimglist_instance,
  54776. filename?filename:"(FILE*)");
  54777. if (stop_flag && nlast_frame!=~0U && frame!=nlast_frame)
  54778. cimg::warn(_cimglist_instance
  54779. "load_yuv(): Frame %d not reached since only %u frames were found in file '%s'.",
  54780. cimglist_instance,
  54781. nlast_frame,frame - 1,filename?filename:"(FILE*)");
  54782. if (!file) cimg::fclose(nfile);
  54783. return *this;
  54784. }
  54785. //! Load an image from a video file, using OpenCV library.
  54786. /**
  54787. \param filename Filename, as a C-string.
  54788. \param first_frame Index of the first frame to read.
  54789. \param last_frame Index of the last frame to read.
  54790. \param step_frame Step value for frame reading.
  54791. \note If step_frame==0, the current video stream is forced to be released (without any frames read).
  54792. **/
  54793. CImgList<T>& load_video(const char *const filename,
  54794. const unsigned int first_frame=0, const unsigned int last_frame=~0U,
  54795. const unsigned int step_frame=1) {
  54796. #ifndef cimg_use_opencv
  54797. if (first_frame || last_frame!=~0U || step_frame>1)
  54798. throw CImgArgumentException(_cimglist_instance
  54799. "load_video() : File '%s', arguments 'first_frame', 'last_frame' "
  54800. "and 'step_frame' can be only set when using OpenCV "
  54801. "(-Dcimg_use_opencv must be enabled).",
  54802. cimglist_instance,filename);
  54803. return load_ffmpeg_external(filename);
  54804. #else
  54805. static CvCapture *captures[32] = { 0 };
  54806. static CImgList<charT> filenames(32);
  54807. static CImg<uintT> positions(32,1,1,1,0);
  54808. static int last_used_index = -1;
  54809. // Detect if a video capture already exists for the specified filename.
  54810. cimg::mutex(9);
  54811. int index = -1;
  54812. if (filename) {
  54813. if (last_used_index>=0 && !std::strcmp(filename,filenames[last_used_index])) {
  54814. index = last_used_index;
  54815. } else cimglist_for(filenames,l) if (filenames[l] && !std::strcmp(filename,filenames[l])) {
  54816. index = l; break;
  54817. }
  54818. } else index = last_used_index;
  54819. cimg::mutex(9,0);
  54820. // Release stream if needed.
  54821. if (!step_frame || (index>=0 && positions[index]>first_frame)) {
  54822. if (index>=0) {
  54823. cimg::mutex(9);
  54824. cvReleaseCapture(&captures[index]);
  54825. captures[index] = 0; filenames[index].assign(); positions[index] = 0;
  54826. if (last_used_index==index) last_used_index = -1;
  54827. index = -1;
  54828. cimg::mutex(9,0);
  54829. } else
  54830. if (filename)
  54831. cimg::warn(_cimglist_instance
  54832. "load_video() : File '%s', no opened video stream associated with filename found.",
  54833. cimglist_instance,filename);
  54834. else
  54835. cimg::warn(_cimglist_instance
  54836. "load_video() : No opened video stream found.",
  54837. cimglist_instance,filename);
  54838. if (!step_frame) return *this;
  54839. }
  54840. // Find empty slot for capturing video stream.
  54841. if (index<0) {
  54842. if (!filename)
  54843. throw CImgArgumentException(_cimglist_instance
  54844. "load_video(): No already open video reader found. You must specify a "
  54845. "non-(null) filename argument for the first call.",
  54846. cimglist_instance);
  54847. else { cimg::mutex(9); cimglist_for(filenames,l) if (!filenames[l]) { index = l; break; } cimg::mutex(9,0); }
  54848. if (index<0)
  54849. throw CImgIOException(_cimglist_instance
  54850. "load_video(): File '%s', no video reader slots available. "
  54851. "You have to release some of your previously opened videos.",
  54852. cimglist_instance,filename);
  54853. cimg::mutex(9);
  54854. captures[index] = cvCaptureFromFile(filename);
  54855. CImg<charT>::string(filename).move_to(filenames[index]);
  54856. positions[index] = 0;
  54857. cimg::mutex(9,0);
  54858. if (!captures[index]) {
  54859. filenames[index].assign();
  54860. std::fclose(cimg::fopen(filename,"rb")); // Check file availability.
  54861. throw CImgIOException(_cimglist_instance
  54862. "load_video(): File '%s', unable to detect format of video file.",
  54863. cimglist_instance,filename);
  54864. }
  54865. }
  54866. cimg::mutex(9);
  54867. const unsigned int nb_frames = (unsigned int)std::max(0.,cvGetCaptureProperty(captures[index],
  54868. CV_CAP_PROP_FRAME_COUNT));
  54869. cimg::mutex(9,0);
  54870. assign();
  54871. // Skip frames if necessary.
  54872. bool go_on = true;
  54873. unsigned int &pos = positions[index];
  54874. while (pos<first_frame) {
  54875. cimg::mutex(9);
  54876. if (!cvGrabFrame(captures[index])) { cimg::mutex(9,0); go_on = false; break; }
  54877. cimg::mutex(9,0);
  54878. ++pos;
  54879. }
  54880. // Read and convert frames.
  54881. const IplImage *src = 0;
  54882. if (go_on) {
  54883. const unsigned int _last_frame = std::min(nb_frames?nb_frames - 1:~0U,last_frame);
  54884. while (pos<=_last_frame) {
  54885. cimg::mutex(9);
  54886. src = cvQueryFrame(captures[index]);
  54887. if (src) {
  54888. CImg<T> frame(src->width,src->height,1,3);
  54889. const int step = (int)(src->widthStep - 3*src->width);
  54890. const unsigned char* ptrs = (unsigned char*)src->imageData;
  54891. T *ptr_r = frame.data(0,0,0,0), *ptr_g = frame.data(0,0,0,1), *ptr_b = frame.data(0,0,0,2);
  54892. if (step>0) cimg_forY(frame,y) {
  54893. cimg_forX(frame,x) { *(ptr_b++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_r++) = (T)*(ptrs++); }
  54894. ptrs+=step;
  54895. } else for (ulongT siz = (ulongT)src->width*src->height; siz; --siz) {
  54896. *(ptr_b++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_r++) = (T)*(ptrs++);
  54897. }
  54898. frame.move_to(*this);
  54899. ++pos;
  54900. bool skip_failed = false;
  54901. for (unsigned int i = 1; i<step_frame && pos<=_last_frame; ++i, ++pos)
  54902. if (!cvGrabFrame(captures[index])) { skip_failed = true; break; }
  54903. if (skip_failed) src = 0;
  54904. }
  54905. cimg::mutex(9,0);
  54906. if (!src) break;
  54907. }
  54908. }
  54909. if (!src || (nb_frames && pos>=nb_frames)) { // Close video stream when necessary.
  54910. cimg::mutex(9);
  54911. cvReleaseCapture(&captures[index]);
  54912. captures[index] = 0;
  54913. filenames[index].assign();
  54914. positions[index] = 0;
  54915. index = -1;
  54916. cimg::mutex(9,0);
  54917. }
  54918. cimg::mutex(9);
  54919. last_used_index = index;
  54920. cimg::mutex(9,0);
  54921. if (is_empty())
  54922. throw CImgIOException(_cimglist_instance
  54923. "load_video(): File '%s', unable to locate frame %u.",
  54924. cimglist_instance,filename,first_frame);
  54925. return *this;
  54926. #endif
  54927. }
  54928. //! Load an image from a video file, using OpenCV library \newinstance.
  54929. static CImgList<T> get_load_video(const char *const filename,
  54930. const unsigned int first_frame=0, const unsigned int last_frame=~0U,
  54931. const unsigned int step_frame=1) {
  54932. return CImgList<T>().load_video(filename,first_frame,last_frame,step_frame);
  54933. }
  54934. //! Load an image from a video file using the external tool 'ffmpeg'.
  54935. /**
  54936. \param filename Filename to read data from.
  54937. **/
  54938. CImgList<T>& load_ffmpeg_external(const char *const filename) {
  54939. if (!filename)
  54940. throw CImgArgumentException(_cimglist_instance
  54941. "load_ffmpeg_external(): Specified filename is (null).",
  54942. cimglist_instance);
  54943. std::fclose(cimg::fopen(filename,"rb")); // Check if file exists.
  54944. CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256);
  54945. std::FILE *file = 0;
  54946. do {
  54947. cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
  54948. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
  54949. cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001.ppm",filename_tmp._data);
  54950. if ((file=std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file);
  54951. } while (file);
  54952. cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%%6d.ppm",filename_tmp._data);
  54953. cimg_snprintf(command,command._width,"%s -i \"%s\" \"%s\"",
  54954. cimg::ffmpeg_path(),
  54955. CImg<charT>::string(filename)._system_strescape().data(),
  54956. CImg<charT>::string(filename_tmp2)._system_strescape().data());
  54957. cimg::system(command,0);
  54958. const unsigned int omode = cimg::exception_mode();
  54959. cimg::exception_mode(0);
  54960. assign();
  54961. unsigned int i = 1;
  54962. for (bool stop_flag = false; !stop_flag; ++i) {
  54963. cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u.ppm",filename_tmp._data,i);
  54964. CImg<T> img;
  54965. try { img.load_pnm(filename_tmp2); }
  54966. catch (CImgException&) { stop_flag = true; }
  54967. if (img) { img.move_to(*this); std::remove(filename_tmp2); }
  54968. }
  54969. cimg::exception_mode(omode);
  54970. if (is_empty())
  54971. throw CImgIOException(_cimglist_instance
  54972. "load_ffmpeg_external(): Failed to open file '%s' with external command 'ffmpeg'.",
  54973. cimglist_instance,
  54974. filename);
  54975. return *this;
  54976. }
  54977. //! Load an image from a video file using the external tool 'ffmpeg' \newinstance.
  54978. static CImgList<T> get_load_ffmpeg_external(const char *const filename) {
  54979. return CImgList<T>().load_ffmpeg_external(filename);
  54980. }
  54981. //! Load gif file, using ImageMagick or GraphicsMagick's external tools.
  54982. /**
  54983. \param filename Filename to read data from.
  54984. **/
  54985. CImgList<T>& load_gif_external(const char *const filename) {
  54986. if (!filename)
  54987. throw CImgArgumentException(_cimglist_instance
  54988. "load_gif_external(): Specified filename is (null).",
  54989. cimglist_instance);
  54990. std::fclose(cimg::fopen(filename,"rb")); // Check if file exists.
  54991. if (!_load_gif_external(filename,false))
  54992. if (!_load_gif_external(filename,true))
  54993. try { assign(CImg<T>().load_other(filename)); } catch (CImgException&) { assign(); }
  54994. if (is_empty())
  54995. throw CImgIOException(_cimglist_instance
  54996. "load_gif_external(): Failed to open file '%s'.",
  54997. cimglist_instance,filename);
  54998. return *this;
  54999. }
  55000. CImgList<T>& _load_gif_external(const char *const filename, const bool use_graphicsmagick=false) {
  55001. CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256);
  55002. std::FILE *file = 0;
  55003. do {
  55004. cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
  55005. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
  55006. if (use_graphicsmagick) cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png.0",filename_tmp._data);
  55007. else cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s-0.png",filename_tmp._data);
  55008. if ((file=std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file);
  55009. } while (file);
  55010. if (use_graphicsmagick) cimg_snprintf(command,command._width,"%s convert \"%s\" \"%s.png\"",
  55011. cimg::graphicsmagick_path(),
  55012. CImg<charT>::string(filename)._system_strescape().data(),
  55013. CImg<charT>::string(filename_tmp)._system_strescape().data());
  55014. else cimg_snprintf(command,command._width,"%s \"%s\" \"%s.png\"",
  55015. cimg::imagemagick_path(),
  55016. CImg<charT>::string(filename)._system_strescape().data(),
  55017. CImg<charT>::string(filename_tmp)._system_strescape().data());
  55018. cimg::system(command,0);
  55019. const unsigned int omode = cimg::exception_mode();
  55020. cimg::exception_mode(0);
  55021. assign();
  55022. // Try to read a single frame gif.
  55023. cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png",filename_tmp._data);
  55024. CImg<T> img;
  55025. try { img.load_png(filename_tmp2); }
  55026. catch (CImgException&) { }
  55027. if (img) { img.move_to(*this); std::remove(filename_tmp2); }
  55028. else { // Try to read animated gif.
  55029. unsigned int i = 0;
  55030. for (bool stop_flag = false; !stop_flag; ++i) {
  55031. if (use_graphicsmagick) cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png.%u",filename_tmp._data,i);
  55032. else cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s-%u.png",filename_tmp._data,i);
  55033. CImg<T> img;
  55034. try { img.load_png(filename_tmp2); }
  55035. catch (CImgException&) { stop_flag = true; }
  55036. if (img) { img.move_to(*this); std::remove(filename_tmp2); }
  55037. }
  55038. }
  55039. cimg::exception_mode(omode);
  55040. return *this;
  55041. }
  55042. //! Load gif file, using ImageMagick or GraphicsMagick's external tools \newinstance.
  55043. static CImgList<T> get_load_gif_external(const char *const filename) {
  55044. return CImgList<T>().load_gif_external(filename);
  55045. }
  55046. //! Load a gzipped list, using external tool 'gunzip'.
  55047. /**
  55048. \param filename Filename to read data from.
  55049. **/
  55050. CImgList<T>& load_gzip_external(const char *const filename) {
  55051. if (!filename)
  55052. throw CImgIOException(_cimglist_instance
  55053. "load_gzip_external(): Specified filename is (null).",
  55054. cimglist_instance);
  55055. std::fclose(cimg::fopen(filename,"rb")); // Check if file exists.
  55056. CImg<charT> command(1024), filename_tmp(256), body(256);
  55057. const char
  55058. *ext = cimg::split_filename(filename,body),
  55059. *ext2 = cimg::split_filename(body,0);
  55060. std::FILE *file = 0;
  55061. do {
  55062. if (!cimg::strcasecmp(ext,"gz")) {
  55063. if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
  55064. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
  55065. else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
  55066. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
  55067. } else {
  55068. if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
  55069. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
  55070. else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
  55071. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
  55072. }
  55073. if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
  55074. } while (file);
  55075. cimg_snprintf(command,command._width,"%s -c \"%s\" > \"%s\"",
  55076. cimg::gunzip_path(),
  55077. CImg<charT>::string(filename)._system_strescape().data(),
  55078. CImg<charT>::string(filename_tmp)._system_strescape().data());
  55079. cimg::system(command);
  55080. if (!(file = std_fopen(filename_tmp,"rb"))) {
  55081. cimg::fclose(cimg::fopen(filename,"r"));
  55082. throw CImgIOException(_cimglist_instance
  55083. "load_gzip_external(): Failed to open file '%s'.",
  55084. cimglist_instance,
  55085. filename);
  55086. } else cimg::fclose(file);
  55087. load(filename_tmp);
  55088. std::remove(filename_tmp);
  55089. return *this;
  55090. }
  55091. //! Load a gzipped list, using external tool 'gunzip' \newinstance.
  55092. static CImgList<T> get_load_gzip_external(const char *const filename) {
  55093. return CImgList<T>().load_gzip_external(filename);
  55094. }
  55095. //! Load a 3d object from a .OFF file.
  55096. /**
  55097. \param filename Filename to read data from.
  55098. \param[out] primitives At return, contains the list of 3d object primitives.
  55099. \param[out] colors At return, contains the list of 3d object colors.
  55100. \return List of 3d object vertices.
  55101. **/
  55102. template<typename tf, typename tc>
  55103. CImgList<T>& load_off(const char *const filename,
  55104. CImgList<tf>& primitives, CImgList<tc>& colors) {
  55105. return get_load_off(filename,primitives,colors).move_to(*this);
  55106. }
  55107. //! Load a 3d object from a .OFF file \newinstance.
  55108. template<typename tf, typename tc>
  55109. static CImgList<T> get_load_off(const char *const filename,
  55110. CImgList<tf>& primitives, CImgList<tc>& colors) {
  55111. return CImg<T>().load_off(filename,primitives,colors)<'x';
  55112. }
  55113. //! Load images from a TIFF file.
  55114. /**
  55115. \param filename Filename to read data from.
  55116. \param first_frame Index of first image frame to read.
  55117. \param last_frame Index of last image frame to read.
  55118. \param step_frame Step applied between each frame.
  55119. \param[out] voxel_size Voxel size, as stored in the filename.
  55120. \param[out] description Description, as stored in the filename.
  55121. **/
  55122. CImgList<T>& load_tiff(const char *const filename,
  55123. const unsigned int first_frame=0, const unsigned int last_frame=~0U,
  55124. const unsigned int step_frame=1,
  55125. float *const voxel_size=0,
  55126. CImg<charT> *const description=0) {
  55127. const unsigned int
  55128. nfirst_frame = first_frame<last_frame?first_frame:last_frame,
  55129. nstep_frame = step_frame?step_frame:1;
  55130. unsigned int nlast_frame = first_frame<last_frame?last_frame:first_frame;
  55131. #ifndef cimg_use_tiff
  55132. cimg::unused(voxel_size,description);
  55133. if (nfirst_frame || nlast_frame!=~0U || nstep_frame!=1)
  55134. throw CImgArgumentException(_cimglist_instance
  55135. "load_tiff(): Unable to load sub-images from file '%s' unless libtiff is enabled.",
  55136. cimglist_instance,
  55137. filename);
  55138. return assign(CImg<T>::get_load_tiff(filename));
  55139. #else
  55140. #if cimg_verbosity<3
  55141. TIFFSetWarningHandler(0);
  55142. TIFFSetErrorHandler(0);
  55143. #endif
  55144. TIFF *tif = TIFFOpen(filename,"r");
  55145. if (tif) {
  55146. unsigned int nb_images = 0;
  55147. do ++nb_images; while (TIFFReadDirectory(tif));
  55148. if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images))
  55149. cimg::warn(_cimglist_instance
  55150. "load_tiff(): Invalid specified frame range is [%u,%u] (step %u) since "
  55151. "file '%s' contains %u image(s).",
  55152. cimglist_instance,
  55153. nfirst_frame,nlast_frame,nstep_frame,filename,nb_images);
  55154. if (nfirst_frame>=nb_images) return assign();
  55155. if (nlast_frame>=nb_images) nlast_frame = nb_images - 1;
  55156. assign(1 + (nlast_frame - nfirst_frame)/nstep_frame);
  55157. TIFFSetDirectory(tif,0);
  55158. cimglist_for(*this,l) _data[l]._load_tiff(tif,nfirst_frame + l*nstep_frame,voxel_size,description);
  55159. TIFFClose(tif);
  55160. } else throw CImgIOException(_cimglist_instance
  55161. "load_tiff(): Failed to open file '%s'.",
  55162. cimglist_instance,
  55163. filename);
  55164. return *this;
  55165. #endif
  55166. }
  55167. //! Load a multi-page TIFF file \newinstance.
  55168. static CImgList<T> get_load_tiff(const char *const filename,
  55169. const unsigned int first_frame=0, const unsigned int last_frame=~0U,
  55170. const unsigned int step_frame=1,
  55171. float *const voxel_size=0,
  55172. CImg<charT> *const description=0) {
  55173. return CImgList<T>().load_tiff(filename,first_frame,last_frame,step_frame,voxel_size,description);
  55174. }
  55175. //@}
  55176. //----------------------------------
  55177. //
  55178. //! \name Data Output
  55179. //@{
  55180. //----------------------------------
  55181. //! Print information about the list on the standard output.
  55182. /**
  55183. \param title Label set to the information displayed.
  55184. \param display_stats Tells if image statistics must be computed and displayed.
  55185. **/
  55186. const CImgList<T>& print(const char *const title=0, const bool display_stats=true) const {
  55187. unsigned int msiz = 0;
  55188. cimglist_for(*this,l) msiz+=_data[l].size();
  55189. msiz*=sizeof(T);
  55190. const unsigned int mdisp = msiz<8*1024?0U:msiz<8*1024*1024?1U:2U;
  55191. CImg<charT> _title(64);
  55192. if (!title) cimg_snprintf(_title,_title._width,"CImgList<%s>",pixel_type());
  55193. std::fprintf(cimg::output(),"%s%s%s%s: %sthis%s = %p, %ssize%s = %u/%u [%u %s], %sdata%s = (CImg<%s>*)%p",
  55194. cimg::t_magenta,cimg::t_bold,title?title:_title._data,cimg::t_normal,
  55195. cimg::t_bold,cimg::t_normal,(void*)this,
  55196. cimg::t_bold,cimg::t_normal,_width,_allocated_width,
  55197. mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20)),
  55198. mdisp==0?"b":(mdisp==1?"Kio":"Mio"),
  55199. cimg::t_bold,cimg::t_normal,pixel_type(),(void*)begin());
  55200. if (_data) std::fprintf(cimg::output(),"..%p.\n",(void*)((char*)end() - 1));
  55201. else std::fprintf(cimg::output(),".\n");
  55202. char tmp[16] = { 0 };
  55203. cimglist_for(*this,ll) {
  55204. cimg_snprintf(tmp,sizeof(tmp),"[%d]",ll);
  55205. std::fprintf(cimg::output()," ");
  55206. _data[ll].print(tmp,display_stats);
  55207. if (ll==3 && width()>8) { ll = width() - 5; std::fprintf(cimg::output()," ...\n"); }
  55208. }
  55209. std::fflush(cimg::output());
  55210. return *this;
  55211. }
  55212. //! Display the current CImgList instance in an existing CImgDisplay window (by reference).
  55213. /**
  55214. \param disp Reference to an existing CImgDisplay instance, where the current image list will be displayed.
  55215. \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
  55216. \param align Appending alignmenet.
  55217. \note This function displays the list images of the current CImgList instance into an existing
  55218. CImgDisplay window.
  55219. Images of the list are appended in a single temporarly image for visualization purposes.
  55220. The function returns immediately.
  55221. **/
  55222. const CImgList<T>& display(CImgDisplay &disp, const char axis='x', const float align=0) const {
  55223. disp.display(*this,axis,align);
  55224. return *this;
  55225. }
  55226. //! Display the current CImgList instance in a new display window.
  55227. /**
  55228. \param disp Display window.
  55229. \param display_info Tells if image information are displayed on the standard output.
  55230. \param axis Alignment axis for images viewing.
  55231. \param align Apending alignment.
  55232. \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function.
  55233. \param exit_on_anykey Exit function when any key is pressed.
  55234. \note This function opens a new window with a specific title and displays the list images of the
  55235. current CImgList instance into it.
  55236. Images of the list are appended in a single temporarly image for visualization purposes.
  55237. The function returns when a key is pressed or the display window is closed by the user.
  55238. **/
  55239. const CImgList<T>& display(CImgDisplay &disp, const bool display_info,
  55240. const char axis='x', const float align=0,
  55241. unsigned int *const XYZ=0, const bool exit_on_anykey=false) const {
  55242. bool is_exit = false;
  55243. return _display(disp,0,0,display_info,axis,align,XYZ,exit_on_anykey,0,true,is_exit);
  55244. }
  55245. //! Display the current CImgList instance in a new display window.
  55246. /**
  55247. \param title Title of the opening display window.
  55248. \param display_info Tells if list information must be written on standard output.
  55249. \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
  55250. \param align Appending alignment.
  55251. \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function.
  55252. \param exit_on_anykey Exit function when any key is pressed.
  55253. **/
  55254. const CImgList<T>& display(const char *const title=0, const bool display_info=true,
  55255. const char axis='x', const float align=0,
  55256. unsigned int *const XYZ=0, const bool exit_on_anykey=false) const {
  55257. CImgDisplay disp;
  55258. bool is_exit = false;
  55259. return _display(disp,title,0,display_info,axis,align,XYZ,exit_on_anykey,0,true,is_exit);
  55260. }
  55261. const CImgList<T>& _display(CImgDisplay &disp, const char *const title, const CImgList<charT> *const titles,
  55262. const bool display_info, const char axis, const float align, unsigned int *const XYZ,
  55263. const bool exit_on_anykey, const unsigned int orig, const bool is_first_call,
  55264. bool &is_exit) const {
  55265. if (is_empty())
  55266. throw CImgInstanceException(_cimglist_instance
  55267. "display(): Empty instance.",
  55268. cimglist_instance);
  55269. if (!disp) {
  55270. if (axis=='x') {
  55271. unsigned int sum_width = 0, max_height = 0;
  55272. cimglist_for(*this,l) {
  55273. const CImg<T> &img = _data[l];
  55274. const unsigned int
  55275. w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
  55276. h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
  55277. sum_width+=w;
  55278. if (h>max_height) max_height = h;
  55279. }
  55280. disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:titles?titles->__display()._data:0,1);
  55281. } else {
  55282. unsigned int max_width = 0, sum_height = 0;
  55283. cimglist_for(*this,l) {
  55284. const CImg<T> &img = _data[l];
  55285. const unsigned int
  55286. w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
  55287. h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
  55288. if (w>max_width) max_width = w;
  55289. sum_height+=h;
  55290. }
  55291. disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:titles?titles->__display()._data:0,1);
  55292. }
  55293. if (!title && !titles) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width);
  55294. } else if (title) disp.set_title("%s",title);
  55295. else if (titles) disp.set_title("%s",titles->__display()._data);
  55296. const CImg<char> dtitle = CImg<char>::string(disp.title());
  55297. if (display_info) print(disp.title());
  55298. disp.show().flush();
  55299. if (_width==1) {
  55300. const unsigned int dw = disp._width, dh = disp._height;
  55301. if (!is_first_call)
  55302. disp.resize(cimg_fitscreen(_data[0]._width,_data[0]._height,_data[0]._depth),false);
  55303. disp.set_title("%s (%ux%ux%ux%u)",
  55304. dtitle.data(),_data[0]._width,_data[0]._height,_data[0]._depth,_data[0]._spectrum);
  55305. _data[0]._display(disp,0,false,XYZ,exit_on_anykey,!is_first_call);
  55306. if (disp.key()) is_exit = true;
  55307. disp.resize(cimg_fitscreen(dw,dh,1),false).set_title("%s",dtitle.data());
  55308. } else {
  55309. bool disp_resize = !is_first_call;
  55310. while (!disp.is_closed() && !is_exit) {
  55311. const CImg<intT> s = _select(disp,0,true,axis,align,exit_on_anykey,orig,disp_resize,!is_first_call,true);
  55312. disp_resize = true;
  55313. if (s[0]<0 && !disp.wheel()) { // No selections done.
  55314. if (disp.button()&2) { disp.flush(); break; }
  55315. is_exit = true;
  55316. } else if (disp.wheel()) { // Zoom in/out.
  55317. const int wheel = disp.wheel();
  55318. disp.set_wheel();
  55319. if (!is_first_call && wheel<0) break;
  55320. if (wheel>0 && _width>=4) {
  55321. const unsigned int
  55322. delta = std::max(1U,(unsigned int)cimg::round(0.3*_width)),
  55323. ind0 = (unsigned int)std::max(0,s[0] - (int)delta),
  55324. ind1 = (unsigned int)std::min(width() - 1,s[0] + (int)delta);
  55325. if ((ind0!=0 || ind1!=_width - 1) && ind1 - ind0>=3) {
  55326. const CImgList<T> sublist = get_shared_images(ind0,ind1);
  55327. CImgList<charT> t_sublist;
  55328. if (titles) t_sublist = titles->get_shared_images(ind0,ind1);
  55329. sublist._display(disp,0,titles?&t_sublist:0,false,axis,align,XYZ,exit_on_anykey,
  55330. orig + ind0,false,is_exit);
  55331. }
  55332. }
  55333. } else if (s[0]!=0 || s[1]!=width() - 1) {
  55334. const CImgList<T> sublist = get_shared_images(s[0],s[1]);
  55335. CImgList<charT> t_sublist;
  55336. if (titles) t_sublist = titles->get_shared_images(s[0],s[1]);
  55337. sublist._display(disp,0,titles?&t_sublist:0,false,axis,align,XYZ,exit_on_anykey,
  55338. orig + s[0],false,is_exit);
  55339. }
  55340. disp.set_title("%s",dtitle.data());
  55341. }
  55342. }
  55343. return *this;
  55344. }
  55345. // [internal] Return string to describe display title.
  55346. CImg<charT> __display() const {
  55347. CImg<charT> res, str;
  55348. cimglist_for(*this,l) {
  55349. CImg<charT>::string(_data[l]).move_to(str);
  55350. if (l!=width() - 1) {
  55351. str.resize(str._width + 1,1,1,1,0);
  55352. str[str._width - 2] = ',';
  55353. str[str._width - 1] = ' ';
  55354. }
  55355. res.append(str,'x');
  55356. }
  55357. if (!res) return CImg<charT>(1,1,1,1,0).move_to(res);
  55358. cimg::strellipsize(res,128,false);
  55359. if (_width>1) {
  55360. const unsigned int l = (unsigned int)std::strlen(res);
  55361. if (res._width<=l + 16) res.resize(l + 16,1,1,1,0);
  55362. cimg_snprintf(res._data + l,16," (#%u)",_width);
  55363. }
  55364. return res;
  55365. }
  55366. //! Save list into a file.
  55367. /**
  55368. \param filename Filename to write data to.
  55369. \param number When positive, represents an index added to the filename. Otherwise, no number is added.
  55370. \param digits Number of digits used for adding the number to the filename.
  55371. **/
  55372. const CImgList<T>& save(const char *const filename, const int number=-1, const unsigned int digits=6) const {
  55373. if (!filename)
  55374. throw CImgArgumentException(_cimglist_instance
  55375. "save(): Specified filename is (null).",
  55376. cimglist_instance);
  55377. // Do not test for empty instances, since .cimg format is able to manage empty instances.
  55378. const bool is_stdout = *filename=='-' && (!filename[1] || filename[1]=='.');
  55379. const char *const ext = cimg::split_filename(filename);
  55380. CImg<charT> nfilename(1024);
  55381. const char *const fn = is_stdout?filename:number>=0?cimg::number_filename(filename,number,digits,nfilename):
  55382. filename;
  55383. #ifdef cimglist_save_plugin
  55384. cimglist_save_plugin(fn);
  55385. #endif
  55386. #ifdef cimglist_save_plugin1
  55387. cimglist_save_plugin1(fn);
  55388. #endif
  55389. #ifdef cimglist_save_plugin2
  55390. cimglist_save_plugin2(fn);
  55391. #endif
  55392. #ifdef cimglist_save_plugin3
  55393. cimglist_save_plugin3(fn);
  55394. #endif
  55395. #ifdef cimglist_save_plugin4
  55396. cimglist_save_plugin4(fn);
  55397. #endif
  55398. #ifdef cimglist_save_plugin5
  55399. cimglist_save_plugin5(fn);
  55400. #endif
  55401. #ifdef cimglist_save_plugin6
  55402. cimglist_save_plugin6(fn);
  55403. #endif
  55404. #ifdef cimglist_save_plugin7
  55405. cimglist_save_plugin7(fn);
  55406. #endif
  55407. #ifdef cimglist_save_plugin8
  55408. cimglist_save_plugin8(fn);
  55409. #endif
  55410. if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true);
  55411. else if (!cimg::strcasecmp(ext,"cimg") || !*ext) return save_cimg(fn,false);
  55412. else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,444,true);
  55413. else if (!cimg::strcasecmp(ext,"avi") ||
  55414. !cimg::strcasecmp(ext,"mov") ||
  55415. !cimg::strcasecmp(ext,"asf") ||
  55416. !cimg::strcasecmp(ext,"divx") ||
  55417. !cimg::strcasecmp(ext,"flv") ||
  55418. !cimg::strcasecmp(ext,"mpg") ||
  55419. !cimg::strcasecmp(ext,"m1v") ||
  55420. !cimg::strcasecmp(ext,"m2v") ||
  55421. !cimg::strcasecmp(ext,"m4v") ||
  55422. !cimg::strcasecmp(ext,"mjp") ||
  55423. !cimg::strcasecmp(ext,"mp4") ||
  55424. !cimg::strcasecmp(ext,"mkv") ||
  55425. !cimg::strcasecmp(ext,"mpe") ||
  55426. !cimg::strcasecmp(ext,"movie") ||
  55427. !cimg::strcasecmp(ext,"ogm") ||
  55428. !cimg::strcasecmp(ext,"ogg") ||
  55429. !cimg::strcasecmp(ext,"ogv") ||
  55430. !cimg::strcasecmp(ext,"qt") ||
  55431. !cimg::strcasecmp(ext,"rm") ||
  55432. !cimg::strcasecmp(ext,"vob") ||
  55433. !cimg::strcasecmp(ext,"wmv") ||
  55434. !cimg::strcasecmp(ext,"xvid") ||
  55435. !cimg::strcasecmp(ext,"mpeg")) return save_video(fn);
  55436. #ifdef cimg_use_tiff
  55437. else if (!cimg::strcasecmp(ext,"tif") ||
  55438. !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn);
  55439. #endif
  55440. else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn);
  55441. else {
  55442. if (_width==1) _data[0].save(fn,-1);
  55443. else cimglist_for(*this,l) { _data[l].save(fn,is_stdout?-1:l); if (is_stdout) std::fputc(EOF,cimg::_stdout()); }
  55444. }
  55445. return *this;
  55446. }
  55447. //! Tell if an image list can be saved as one single file.
  55448. /**
  55449. \param filename Filename, as a C-string.
  55450. \return \c true if the file format supports multiple images, \c false otherwise.
  55451. **/
  55452. static bool is_saveable(const char *const filename) {
  55453. const char *const ext = cimg::split_filename(filename);
  55454. if (!cimg::strcasecmp(ext,"cimgz") ||
  55455. #ifdef cimg_use_tiff
  55456. !cimg::strcasecmp(ext,"tif") ||
  55457. !cimg::strcasecmp(ext,"tiff") ||
  55458. #endif
  55459. !cimg::strcasecmp(ext,"yuv") ||
  55460. !cimg::strcasecmp(ext,"avi") ||
  55461. !cimg::strcasecmp(ext,"mov") ||
  55462. !cimg::strcasecmp(ext,"asf") ||
  55463. !cimg::strcasecmp(ext,"divx") ||
  55464. !cimg::strcasecmp(ext,"flv") ||
  55465. !cimg::strcasecmp(ext,"mpg") ||
  55466. !cimg::strcasecmp(ext,"m1v") ||
  55467. !cimg::strcasecmp(ext,"m2v") ||
  55468. !cimg::strcasecmp(ext,"m4v") ||
  55469. !cimg::strcasecmp(ext,"mjp") ||
  55470. !cimg::strcasecmp(ext,"mp4") ||
  55471. !cimg::strcasecmp(ext,"mkv") ||
  55472. !cimg::strcasecmp(ext,"mpe") ||
  55473. !cimg::strcasecmp(ext,"movie") ||
  55474. !cimg::strcasecmp(ext,"ogm") ||
  55475. !cimg::strcasecmp(ext,"ogg") ||
  55476. !cimg::strcasecmp(ext,"ogv") ||
  55477. !cimg::strcasecmp(ext,"qt") ||
  55478. !cimg::strcasecmp(ext,"rm") ||
  55479. !cimg::strcasecmp(ext,"vob") ||
  55480. !cimg::strcasecmp(ext,"wmv") ||
  55481. !cimg::strcasecmp(ext,"xvid") ||
  55482. !cimg::strcasecmp(ext,"mpeg")) return true;
  55483. return false;
  55484. }
  55485. //! Save image sequence as a GIF animated file.
  55486. /**
  55487. \param filename Filename to write data to.
  55488. \param fps Number of desired frames per second.
  55489. \param nb_loops Number of loops (\c 0 for infinite looping).
  55490. **/
  55491. const CImgList<T>& save_gif_external(const char *const filename, const float fps=25,
  55492. const unsigned int nb_loops=0) {
  55493. CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256);
  55494. CImgList<charT> filenames;
  55495. std::FILE *file = 0;
  55496. #ifdef cimg_use_png
  55497. #define _cimg_save_gif_ext "png"
  55498. #else
  55499. #define _cimg_save_gif_ext "ppm"
  55500. #endif
  55501. do {
  55502. cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
  55503. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
  55504. cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001." _cimg_save_gif_ext,filename_tmp._data);
  55505. if ((file=std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file);
  55506. } while (file);
  55507. cimglist_for(*this,l) {
  55508. cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u." _cimg_save_gif_ext,filename_tmp._data,l + 1);
  55509. CImg<charT>::string(filename_tmp2).move_to(filenames);
  55510. if (_data[l]._depth>1 || _data[l]._spectrum!=3) _data[l].get_resize(-100,-100,1,3).save(filename_tmp2);
  55511. else _data[l].save(filename_tmp2);
  55512. }
  55513. cimg_snprintf(command,command._width,"%s -delay %u -loop %u",
  55514. cimg::imagemagick_path(),(unsigned int)std::max(0.0f,cimg::round(100/fps)),nb_loops);
  55515. CImg<ucharT>::string(command).move_to(filenames,0);
  55516. cimg_snprintf(command,command._width,"\"%s\"",
  55517. CImg<charT>::string(filename)._system_strescape().data());
  55518. CImg<ucharT>::string(command).move_to(filenames);
  55519. CImg<charT> _command = filenames>'x';
  55520. cimg_for(_command,p,char) if (!*p) *p = ' ';
  55521. _command.back() = 0;
  55522. cimg::system(_command);
  55523. file = std_fopen(filename,"rb");
  55524. if (!file)
  55525. throw CImgIOException(_cimglist_instance
  55526. "save_gif_external(): Failed to save file '%s' with external command 'magick/convert'.",
  55527. cimglist_instance,
  55528. filename);
  55529. else cimg::fclose(file);
  55530. cimglist_for_in(*this,1,filenames._width - 1,l) std::remove(filenames[l]);
  55531. return *this;
  55532. }
  55533. //! Save list as a YUV image sequence file.
  55534. /**
  55535. \param filename Filename to write data to.
  55536. \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
  55537. \param is_rgb Tells if the RGB to YUV conversion must be done for saving.
  55538. **/
  55539. const CImgList<T>& save_yuv(const char *const filename=0,
  55540. const unsigned int chroma_subsampling=444,
  55541. const bool is_rgb=true) const {
  55542. return _save_yuv(0,filename,chroma_subsampling,is_rgb);
  55543. }
  55544. //! Save image sequence into a YUV file.
  55545. /**
  55546. \param file File to write data to.
  55547. \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
  55548. \param is_rgb Tells if the RGB to YUV conversion must be done for saving.
  55549. **/
  55550. const CImgList<T>& save_yuv(std::FILE *const file,
  55551. const unsigned int chroma_subsampling=444,
  55552. const bool is_rgb=true) const {
  55553. return _save_yuv(file,0,chroma_subsampling,is_rgb);
  55554. }
  55555. const CImgList<T>& _save_yuv(std::FILE *const file, const char *const filename,
  55556. const unsigned int chroma_subsampling,
  55557. const bool is_rgb) const {
  55558. if (!file && !filename)
  55559. throw CImgArgumentException(_cimglist_instance
  55560. "save_yuv(): Specified filename is (null).",
  55561. cimglist_instance);
  55562. if (chroma_subsampling!=420 && chroma_subsampling!=422 && chroma_subsampling!=444)
  55563. throw CImgArgumentException(_cimglist_instance
  55564. "save_yuv(): Specified chroma subsampling %u is invalid, for file '%s'.",
  55565. cimglist_instance,
  55566. chroma_subsampling,filename?filename:"(FILE*)");
  55567. if (is_empty()) { cimg::fempty(file,filename); return *this; }
  55568. const unsigned int
  55569. cfx = chroma_subsampling==420 || chroma_subsampling==422?2:1,
  55570. cfy = chroma_subsampling==420?2:1,
  55571. w0 = (*this)[0]._width, h0 = (*this)[0]._height,
  55572. width0 = w0 + (w0%cfx), height0 = h0 + (h0%cfy);
  55573. std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
  55574. cimglist_for(*this,l) {
  55575. const CImg<T> &frame = (*this)[l];
  55576. cimg_forZ(frame,z) {
  55577. CImg<ucharT> YUV;
  55578. if (sizeof(T)==1 && !is_rgb &&
  55579. frame._width==width0 && frame._height==height0 && frame._depth==1 && frame._spectrum==3)
  55580. YUV.assign((unsigned char*)frame._data,width0,height0,1,3,true);
  55581. else {
  55582. YUV = frame.get_slice(z);
  55583. if (YUV._width!=width0 || YUV._height!=height0) YUV.resize(width0,height0,1,-100,0);
  55584. if (YUV._spectrum!=3) YUV.resize(-100,-100,1,3,YUV._spectrum==1?1:0);
  55585. if (is_rgb) YUV.RGBtoYCbCr();
  55586. }
  55587. if (chroma_subsampling==444)
  55588. cimg::fwrite(YUV._data,(size_t)YUV._width*YUV._height*3,nfile);
  55589. else {
  55590. cimg::fwrite(YUV._data,(size_t)YUV._width*YUV._height,nfile);
  55591. CImg<ucharT> UV = YUV.get_channels(1,2);
  55592. UV.resize(UV._width/cfx,UV._height/cfy,1,2,2);
  55593. cimg::fwrite(UV._data,(size_t)UV._width*UV._height*2,nfile);
  55594. }
  55595. }
  55596. }
  55597. if (!file) cimg::fclose(nfile);
  55598. return *this;
  55599. }
  55600. //! Save list into a .cimg file.
  55601. /**
  55602. \param filename Filename to write data to.
  55603. \param is_compressed Tells if data compression must be enabled.
  55604. **/
  55605. const CImgList<T>& save_cimg(const char *const filename, const bool is_compressed=false) const {
  55606. return _save_cimg(0,filename,is_compressed);
  55607. }
  55608. const CImgList<T>& _save_cimg(std::FILE *const file, const char *const filename, const bool is_compressed) const {
  55609. if (!file && !filename)
  55610. throw CImgArgumentException(_cimglist_instance
  55611. "save_cimg(): Specified filename is (null).",
  55612. cimglist_instance);
  55613. #ifndef cimg_use_zlib
  55614. if (is_compressed)
  55615. cimg::warn(_cimglist_instance
  55616. "save_cimg(): Unable to save compressed data in file '%s' unless zlib is enabled, "
  55617. "saving them uncompressed.",
  55618. cimglist_instance,
  55619. filename?filename:"(FILE*)");
  55620. #endif
  55621. std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
  55622. const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little";
  55623. if (std::strstr(ptype,"unsigned")==ptype) std::fprintf(nfile,"%u unsigned_%s %s_endian\n",_width,ptype + 9,etype);
  55624. else std::fprintf(nfile,"%u %s %s_endian\n",_width,ptype,etype);
  55625. cimglist_for(*this,l) {
  55626. const CImg<T>& img = _data[l];
  55627. std::fprintf(nfile,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum);
  55628. if (img._data) {
  55629. CImg<T> tmp;
  55630. if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); }
  55631. const CImg<T>& ref = cimg::endianness()?tmp:img;
  55632. bool failed_to_compress = true;
  55633. if (is_compressed) {
  55634. #ifdef cimg_use_zlib
  55635. const ulongT siz = sizeof(T)*ref.size();
  55636. uLongf csiz = siz + siz/100 + 16;
  55637. Bytef *const cbuf = new Bytef[csiz];
  55638. if (compress(cbuf,&csiz,(Bytef*)ref._data,siz))
  55639. cimg::warn(_cimglist_instance
  55640. "save_cimg(): Failed to save compressed data for file '%s', saving them uncompressed.",
  55641. cimglist_instance,
  55642. filename?filename:"(FILE*)");
  55643. else {
  55644. std::fprintf(nfile," #%lu\n",csiz);
  55645. cimg::fwrite(cbuf,csiz,nfile);
  55646. delete[] cbuf;
  55647. failed_to_compress = false;
  55648. }
  55649. #endif
  55650. }
  55651. if (failed_to_compress) { // Write in a non-compressed way.
  55652. std::fputc('\n',nfile);
  55653. cimg::fwrite(ref._data,ref.size(),nfile);
  55654. }
  55655. } else std::fputc('\n',nfile);
  55656. }
  55657. if (!file) cimg::fclose(nfile);
  55658. return *this;
  55659. }
  55660. //! Save list into a .cimg file.
  55661. /**
  55662. \param file File to write data to.
  55663. \param is_compressed Tells if data compression must be enabled.
  55664. **/
  55665. const CImgList<T>& save_cimg(std::FILE *file, const bool is_compressed=false) const {
  55666. return _save_cimg(file,0,is_compressed);
  55667. }
  55668. const CImgList<T>& _save_cimg(std::FILE *const file, const char *const filename,
  55669. const unsigned int n0,
  55670. const unsigned int x0, const unsigned int y0,
  55671. const unsigned int z0, const unsigned int c0) const {
  55672. #define _cimg_save_cimg_case(Ts,Tss) \
  55673. if (!saved && !cimg::strcasecmp(Ts,str_pixeltype)) { \
  55674. for (unsigned int l = 0; l<lmax; ++l) { \
  55675. j = 0; while ((i=std::fgetc(nfile))!='\n') tmp[j++]=(char)i; tmp[j] = 0; \
  55676. W = H = D = C = 0; \
  55677. if (cimg_sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \
  55678. throw CImgIOException(_cimglist_instance \
  55679. "save_cimg(): Invalid size (%u,%u,%u,%u) of image[%u], for file '%s'.", \
  55680. cimglist_instance, \
  55681. W,H,D,C,l,filename?filename:"(FILE*)"); \
  55682. if (W*H*D*C>0) { \
  55683. if (l<n0 || x0>=W || y0>=H || z0>=D || c0>=D) cimg::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \
  55684. else { \
  55685. const CImg<T>& img = (*this)[l - n0]; \
  55686. const T *ptrs = img._data; \
  55687. const unsigned int \
  55688. x1 = x0 + img._width - 1, \
  55689. y1 = y0 + img._height - 1, \
  55690. z1 = z0 + img._depth - 1, \
  55691. c1 = c0 + img._spectrum - 1, \
  55692. nx1 = x1>=W?W - 1:x1, \
  55693. ny1 = y1>=H?H - 1:y1, \
  55694. nz1 = z1>=D?D - 1:z1, \
  55695. nc1 = c1>=C?C - 1:c1; \
  55696. CImg<Tss> raw(1 + nx1 - x0); \
  55697. const unsigned int skipvb = c0*W*H*D*sizeof(Tss); \
  55698. if (skipvb) cimg::fseek(nfile,skipvb,SEEK_CUR); \
  55699. for (unsigned int v = 1 + nc1 - c0; v; --v) { \
  55700. const unsigned int skipzb = z0*W*H*sizeof(Tss); \
  55701. if (skipzb) cimg::fseek(nfile,skipzb,SEEK_CUR); \
  55702. for (unsigned int z = 1 + nz1 - z0; z; --z) { \
  55703. const unsigned int skipyb = y0*W*sizeof(Tss); \
  55704. if (skipyb) cimg::fseek(nfile,skipyb,SEEK_CUR); \
  55705. for (unsigned int y = 1 + ny1 - y0; y; --y) { \
  55706. const unsigned int skipxb = x0*sizeof(Tss); \
  55707. if (skipxb) cimg::fseek(nfile,skipxb,SEEK_CUR); \
  55708. raw.assign(ptrs, raw._width); \
  55709. ptrs+=img._width; \
  55710. if (endian) cimg::invert_endianness(raw._data,raw._width); \
  55711. cimg::fwrite(raw._data,raw._width,nfile); \
  55712. const unsigned int skipxe = (W - 1 - nx1)*sizeof(Tss); \
  55713. if (skipxe) cimg::fseek(nfile,skipxe,SEEK_CUR); \
  55714. } \
  55715. const unsigned int skipye = (H - 1 - ny1)*W*sizeof(Tss); \
  55716. if (skipye) cimg::fseek(nfile,skipye,SEEK_CUR); \
  55717. } \
  55718. const unsigned int skipze = (D - 1 - nz1)*W*H*sizeof(Tss); \
  55719. if (skipze) cimg::fseek(nfile,skipze,SEEK_CUR); \
  55720. } \
  55721. const unsigned int skipve = (C - 1 - nc1)*W*H*D*sizeof(Tss); \
  55722. if (skipve) cimg::fseek(nfile,skipve,SEEK_CUR); \
  55723. } \
  55724. } \
  55725. } \
  55726. saved = true; \
  55727. }
  55728. if (!file && !filename)
  55729. throw CImgArgumentException(_cimglist_instance
  55730. "save_cimg(): Specified filename is (null).",
  55731. cimglist_instance);
  55732. if (is_empty())
  55733. throw CImgInstanceException(_cimglist_instance
  55734. "save_cimg(): Empty instance, for file '%s'.",
  55735. cimglist_instance,
  55736. filename?filename:"(FILE*)");
  55737. std::FILE *const nfile = file?file:cimg::fopen(filename,"rb+");
  55738. bool saved = false, endian = cimg::endianness();
  55739. CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
  55740. *tmp = *str_pixeltype = *str_endian = 0;
  55741. unsigned int j, N, W, H, D, C;
  55742. int i, err;
  55743. j = 0; while ((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0;
  55744. err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype._data,str_endian._data);
  55745. if (err<2) {
  55746. if (!file) cimg::fclose(nfile);
  55747. throw CImgIOException(_cimglist_instance
  55748. "save_cimg(): CImg header not found in file '%s'.",
  55749. cimglist_instance,
  55750. filename?filename:"(FILE*)");
  55751. }
  55752. if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
  55753. else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
  55754. const unsigned int lmax = std::min(N,n0 + _width);
  55755. _cimg_save_cimg_case("bool",bool);
  55756. _cimg_save_cimg_case("unsigned_char",unsigned char);
  55757. _cimg_save_cimg_case("uchar",unsigned char);
  55758. _cimg_save_cimg_case("char",char);
  55759. _cimg_save_cimg_case("unsigned_short",unsigned short);
  55760. _cimg_save_cimg_case("ushort",unsigned short);
  55761. _cimg_save_cimg_case("short",short);
  55762. _cimg_save_cimg_case("unsigned_int",unsigned int);
  55763. _cimg_save_cimg_case("uint",unsigned int);
  55764. _cimg_save_cimg_case("int",int);
  55765. _cimg_save_cimg_case("unsigned_int64",uint64T);
  55766. _cimg_save_cimg_case("uint64",uint64T);
  55767. _cimg_save_cimg_case("int64",int64T);
  55768. _cimg_save_cimg_case("float",float);
  55769. _cimg_save_cimg_case("double",double);
  55770. if (!saved) {
  55771. if (!file) cimg::fclose(nfile);
  55772. throw CImgIOException(_cimglist_instance
  55773. "save_cimg(): Unsupported data type '%s' for file '%s'.",
  55774. cimglist_instance,
  55775. filename?filename:"(FILE*)",str_pixeltype._data);
  55776. }
  55777. if (!file) cimg::fclose(nfile);
  55778. return *this;
  55779. }
  55780. //! Insert the image instance into into an existing .cimg file, at specified coordinates.
  55781. /**
  55782. \param filename Filename to write data to.
  55783. \param n0 Starting index of images to write.
  55784. \param x0 Starting X-coordinates of image regions to write.
  55785. \param y0 Starting Y-coordinates of image regions to write.
  55786. \param z0 Starting Z-coordinates of image regions to write.
  55787. \param c0 Starting C-coordinates of image regions to write.
  55788. **/
  55789. const CImgList<T>& save_cimg(const char *const filename,
  55790. const unsigned int n0,
  55791. const unsigned int x0, const unsigned int y0,
  55792. const unsigned int z0, const unsigned int c0) const {
  55793. return _save_cimg(0,filename,n0,x0,y0,z0,c0);
  55794. }
  55795. //! Insert the image instance into into an existing .cimg file, at specified coordinates.
  55796. /**
  55797. \param file File to write data to.
  55798. \param n0 Starting index of images to write.
  55799. \param x0 Starting X-coordinates of image regions to write.
  55800. \param y0 Starting Y-coordinates of image regions to write.
  55801. \param z0 Starting Z-coordinates of image regions to write.
  55802. \param c0 Starting C-coordinates of image regions to write.
  55803. **/
  55804. const CImgList<T>& save_cimg(std::FILE *const file,
  55805. const unsigned int n0,
  55806. const unsigned int x0, const unsigned int y0,
  55807. const unsigned int z0, const unsigned int c0) const {
  55808. return _save_cimg(file,0,n0,x0,y0,z0,c0);
  55809. }
  55810. static void _save_empty_cimg(std::FILE *const file, const char *const filename,
  55811. const unsigned int nb,
  55812. const unsigned int dx, const unsigned int dy,
  55813. const unsigned int dz, const unsigned int dc) {
  55814. std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
  55815. const ulongT siz = (ulongT)dx*dy*dz*dc*sizeof(T);
  55816. std::fprintf(nfile,"%u %s\n",nb,pixel_type());
  55817. for (unsigned int i=nb; i; --i) {
  55818. std::fprintf(nfile,"%u %u %u %u\n",dx,dy,dz,dc);
  55819. for (ulongT off = siz; off; --off) std::fputc(0,nfile);
  55820. }
  55821. if (!file) cimg::fclose(nfile);
  55822. }
  55823. //! Save empty (non-compressed) .cimg file with specified dimensions.
  55824. /**
  55825. \param filename Filename to write data to.
  55826. \param nb Number of images to write.
  55827. \param dx Width of images in the written file.
  55828. \param dy Height of images in the written file.
  55829. \param dz Depth of images in the written file.
  55830. \param dc Spectrum of images in the written file.
  55831. **/
  55832. static void save_empty_cimg(const char *const filename,
  55833. const unsigned int nb,
  55834. const unsigned int dx, const unsigned int dy=1,
  55835. const unsigned int dz=1, const unsigned int dc=1) {
  55836. return _save_empty_cimg(0,filename,nb,dx,dy,dz,dc);
  55837. }
  55838. //! Save empty .cimg file with specified dimensions.
  55839. /**
  55840. \param file File to write data to.
  55841. \param nb Number of images to write.
  55842. \param dx Width of images in the written file.
  55843. \param dy Height of images in the written file.
  55844. \param dz Depth of images in the written file.
  55845. \param dc Spectrum of images in the written file.
  55846. **/
  55847. static void save_empty_cimg(std::FILE *const file,
  55848. const unsigned int nb,
  55849. const unsigned int dx, const unsigned int dy=1,
  55850. const unsigned int dz=1, const unsigned int dc=1) {
  55851. return _save_empty_cimg(file,0,nb,dx,dy,dz,dc);
  55852. }
  55853. //! Save list as a TIFF file.
  55854. /**
  55855. \param filename Filename to write data to.
  55856. \param compression_type Compression mode used to write data.
  55857. \param voxel_size Voxel size, to be stored in the filename.
  55858. \param description Description, to be stored in the filename.
  55859. \param use_bigtiff Allow to save big tiff files (>4Gb).
  55860. **/
  55861. const CImgList<T>& save_tiff(const char *const filename, const unsigned int compression_type=0,
  55862. const float *const voxel_size=0, const char *const description=0,
  55863. const bool use_bigtiff=true) const {
  55864. if (!filename)
  55865. throw CImgArgumentException(_cimglist_instance
  55866. "save_tiff(): Specified filename is (null).",
  55867. cimglist_instance);
  55868. if (is_empty()) { cimg::fempty(0,filename); return *this; }
  55869. #ifndef cimg_use_tiff
  55870. if (_width==1) _data[0].save_tiff(filename,compression_type,voxel_size,description,use_bigtiff);
  55871. else cimglist_for(*this,l) {
  55872. CImg<charT> nfilename(1024);
  55873. cimg::number_filename(filename,l,6,nfilename);
  55874. _data[l].save_tiff(nfilename,compression_type,voxel_size,description,use_bigtiff);
  55875. }
  55876. #else
  55877. ulongT siz = 0;
  55878. cimglist_for(*this,l) siz+=_data[l].size();
  55879. const bool _use_bigtiff = use_bigtiff && sizeof(siz)>=8 && siz*sizeof(T)>=1UL<<31; // No bigtiff for small images.
  55880. TIFF *tif = TIFFOpen(filename,_use_bigtiff?"w8":"w4");
  55881. if (tif) {
  55882. for (unsigned int dir = 0, l = 0; l<_width; ++l) {
  55883. const CImg<T>& img = (*this)[l];
  55884. cimg_forZ(img,z) img._save_tiff(tif,dir++,z,compression_type,voxel_size,description);
  55885. }
  55886. TIFFClose(tif);
  55887. } else
  55888. throw CImgIOException(_cimglist_instance
  55889. "save_tiff(): Failed to open stream for file '%s'.",
  55890. cimglist_instance,
  55891. filename);
  55892. #endif
  55893. return *this;
  55894. }
  55895. //! Save list as a gzipped file, using external tool 'gzip'.
  55896. /**
  55897. \param filename Filename to write data to.
  55898. **/
  55899. const CImgList<T>& save_gzip_external(const char *const filename) const {
  55900. if (!filename)
  55901. throw CImgIOException(_cimglist_instance
  55902. "save_gzip_external(): Specified filename is (null).",
  55903. cimglist_instance);
  55904. CImg<charT> command(1024), filename_tmp(256), body(256);
  55905. const char
  55906. *ext = cimg::split_filename(filename,body),
  55907. *ext2 = cimg::split_filename(body,0);
  55908. std::FILE *file;
  55909. do {
  55910. if (!cimg::strcasecmp(ext,"gz")) {
  55911. if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
  55912. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
  55913. else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg",
  55914. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
  55915. } else {
  55916. if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
  55917. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
  55918. else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg",
  55919. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
  55920. }
  55921. if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
  55922. } while (file);
  55923. if (is_saveable(body)) {
  55924. save(filename_tmp);
  55925. cimg_snprintf(command,command._width,"%s -c \"%s\" > \"%s\"",
  55926. cimg::gzip_path(),
  55927. CImg<charT>::string(filename_tmp)._system_strescape().data(),
  55928. CImg<charT>::string(filename)._system_strescape().data());
  55929. cimg::system(command);
  55930. file = std_fopen(filename,"rb");
  55931. if (!file)
  55932. throw CImgIOException(_cimglist_instance
  55933. "save_gzip_external(): Failed to save file '%s' with external command 'gzip'.",
  55934. cimglist_instance,
  55935. filename);
  55936. else cimg::fclose(file);
  55937. std::remove(filename_tmp);
  55938. } else {
  55939. CImg<charT> nfilename(1024);
  55940. cimglist_for(*this,l) {
  55941. cimg::number_filename(body,l,6,nfilename);
  55942. if (*ext) cimg_sprintf(nfilename._data + std::strlen(nfilename),".%s",ext);
  55943. _data[l].save_gzip_external(nfilename);
  55944. }
  55945. }
  55946. return *this;
  55947. }
  55948. //! Save image sequence, using the OpenCV library.
  55949. /**
  55950. \param filename Filename to write data to.
  55951. \param fps Number of frames per second.
  55952. \param codec Type of compression (See http://www.fourcc.org/codecs.php to see available codecs).
  55953. \param keep_open Tells if the video writer associated to the specified filename
  55954. must be kept open or not (to allow frames to be added in the same file afterwards).
  55955. **/
  55956. const CImgList<T>& save_video(const char *const filename, const unsigned int fps=25,
  55957. const char *codec=0, const bool keep_open=false) const {
  55958. #ifndef cimg_use_opencv
  55959. cimg::unused(codec,keep_open);
  55960. return save_ffmpeg_external(filename,fps);
  55961. #else
  55962. static CvVideoWriter *writers[32] = { 0 };
  55963. static CImgList<charT> filenames(32);
  55964. static CImg<intT> sizes(32,2,1,1,0);
  55965. static int last_used_index = -1;
  55966. // Detect if a video writer already exists for the specified filename.
  55967. cimg::mutex(9);
  55968. int index = -1;
  55969. if (filename) {
  55970. if (last_used_index>=0 && !std::strcmp(filename,filenames[last_used_index])) {
  55971. index = last_used_index;
  55972. } else cimglist_for(filenames,l) if (filenames[l] && !std::strcmp(filename,filenames[l])) {
  55973. index = l; break;
  55974. }
  55975. } else index = last_used_index;
  55976. cimg::mutex(9,0);
  55977. // Find empty slot for capturing video stream.
  55978. if (index<0) {
  55979. if (!filename)
  55980. throw CImgArgumentException(_cimglist_instance
  55981. "save_video(): No already open video writer found. You must specify a "
  55982. "non-(null) filename argument for the first call.",
  55983. cimglist_instance);
  55984. else { cimg::mutex(9); cimglist_for(filenames,l) if (!filenames[l]) { index = l; break; } cimg::mutex(9,0); }
  55985. if (index<0)
  55986. throw CImgIOException(_cimglist_instance
  55987. "save_video(): File '%s', no video writer slots available. "
  55988. "You have to release some of your previously opened videos.",
  55989. cimglist_instance,filename);
  55990. if (is_empty())
  55991. throw CImgInstanceException(_cimglist_instance
  55992. "save_video(): Instance list is empty.",
  55993. cimglist_instance);
  55994. const unsigned int W = _data?_data[0]._width:0, H = _data?_data[0]._height:0;
  55995. if (!W || !H)
  55996. throw CImgInstanceException(_cimglist_instance
  55997. "save_video(): Frame [0] is an empty image.",
  55998. cimglist_instance);
  55999. #define _cimg_docase(x) ((x)>='a'&&(x)<='z'?(x) + 'A' - 'a':(x))
  56000. const char
  56001. *const _codec = codec && *codec?codec:cimg_OS==2?"mpeg":"mp4v",
  56002. codec0 = _cimg_docase(_codec[0]),
  56003. codec1 = _codec[0]?_cimg_docase(_codec[1]):0,
  56004. codec2 = _codec[1]?_cimg_docase(_codec[2]):0,
  56005. codec3 = _codec[2]?_cimg_docase(_codec[3]):0;
  56006. cimg::mutex(9);
  56007. writers[index] = cvCreateVideoWriter(filename,CV_FOURCC(codec0,codec1,codec2,codec3),
  56008. fps,cvSize(W,H));
  56009. CImg<charT>::string(filename).move_to(filenames[index]);
  56010. sizes(index,0) = W; sizes(index,1) = H;
  56011. cimg::mutex(9,0);
  56012. if (!writers[index])
  56013. throw CImgIOException(_cimglist_instance
  56014. "save_video(): File '%s', unable to initialize video writer with codec '%c%c%c%c'.",
  56015. cimglist_instance,filename,
  56016. codec0,codec1,codec2,codec3);
  56017. }
  56018. if (!is_empty()) {
  56019. const unsigned int W = sizes(index,0), H = sizes(index,1);
  56020. cimg::mutex(9);
  56021. IplImage *ipl = cvCreateImage(cvSize(W,H),8,3);
  56022. cimglist_for(*this,l) {
  56023. CImg<T> &src = _data[l];
  56024. if (src.is_empty())
  56025. cimg::warn(_cimglist_instance
  56026. "save_video(): Skip empty frame %d for file '%s'.",
  56027. cimglist_instance,l,filename);
  56028. if (src._depth>1 || src._spectrum>3)
  56029. cimg::warn(_cimglist_instance
  56030. "save_video(): Frame %u has incompatible dimension (%u,%u,%u,%u). "
  56031. "Some image data may be ignored when writing frame into video file '%s'.",
  56032. cimglist_instance,l,src._width,src._height,src._depth,src._spectrum,filename);
  56033. if (src._width==W && src._height==H && src._spectrum==3) {
  56034. const T *ptr_r = src.data(0,0,0,0), *ptr_g = src.data(0,0,0,1), *ptr_b = src.data(0,0,0,2);
  56035. char *ptrd = ipl->imageData;
  56036. cimg_forXY(src,x,y) {
  56037. *(ptrd++) = (char)*(ptr_b++); *(ptrd++) = (char)*(ptr_g++); *(ptrd++) = (char)*(ptr_r++);
  56038. }
  56039. } else {
  56040. CImg<unsigned char> _src(src,false);
  56041. _src.channels(0,std::min(_src._spectrum - 1,2U)).resize(W,H);
  56042. _src.resize(W,H,1,3,_src._spectrum==1);
  56043. const unsigned char *ptr_r = _src.data(0,0,0,0), *ptr_g = _src.data(0,0,0,1), *ptr_b = _src.data(0,0,0,2);
  56044. char *ptrd = ipl->imageData;
  56045. cimg_forXY(_src,x,y) {
  56046. *(ptrd++) = (char)*(ptr_b++); *(ptrd++) = (char)*(ptr_g++); *(ptrd++) = (char)*(ptr_r++);
  56047. }
  56048. }
  56049. cvWriteFrame(writers[index],ipl);
  56050. }
  56051. cvReleaseImage(&ipl);
  56052. cimg::mutex(9,0);
  56053. }
  56054. cimg::mutex(9);
  56055. if (!keep_open) {
  56056. cvReleaseVideoWriter(&writers[index]);
  56057. writers[index] = 0;
  56058. filenames[index].assign();
  56059. sizes(index,0) = sizes(index,1) = 0;
  56060. last_used_index = -1;
  56061. } else last_used_index = index;
  56062. cimg::mutex(9,0);
  56063. return *this;
  56064. #endif
  56065. }
  56066. //! Save image sequence, using the external tool 'ffmpeg'.
  56067. /**
  56068. \param filename Filename to write data to.
  56069. \param fps Number of frames per second.
  56070. \param codec Type of compression.
  56071. \param bitrate Output bitrate
  56072. **/
  56073. const CImgList<T>& save_ffmpeg_external(const char *const filename, const unsigned int fps=25,
  56074. const char *const codec=0, const unsigned int bitrate=2048) const {
  56075. if (!filename)
  56076. throw CImgArgumentException(_cimglist_instance
  56077. "save_ffmpeg_external(): Specified filename is (null).",
  56078. cimglist_instance);
  56079. if (is_empty()) { cimg::fempty(0,filename); return *this; }
  56080. const char
  56081. *const ext = cimg::split_filename(filename),
  56082. *const _codec = codec?codec:!cimg::strcasecmp(ext,"flv")?"flv":"mpeg2video";
  56083. CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256);
  56084. CImgList<charT> filenames;
  56085. std::FILE *file = 0;
  56086. cimglist_for(*this,l) if (!_data[l].is_sameXYZ(_data[0]))
  56087. throw CImgInstanceException(_cimglist_instance
  56088. "save_ffmpeg_external(): Invalid instance dimensions for file '%s'.",
  56089. cimglist_instance,
  56090. filename);
  56091. do {
  56092. cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
  56093. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
  56094. cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001.ppm",filename_tmp._data);
  56095. if ((file=std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file);
  56096. } while (file);
  56097. cimglist_for(*this,l) {
  56098. cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u.ppm",filename_tmp._data,l + 1);
  56099. CImg<charT>::string(filename_tmp2).move_to(filenames);
  56100. if (_data[l]._depth>1 || _data[l]._spectrum!=3) _data[l].get_resize(-100,-100,1,3).save_pnm(filename_tmp2);
  56101. else _data[l].save_pnm(filename_tmp2);
  56102. }
  56103. cimg_snprintf(command,command._width,"%s -i \"%s_%%6d.ppm\" -vcodec %s -b %uk -r %u -y \"%s\"",
  56104. cimg::ffmpeg_path(),
  56105. CImg<charT>::string(filename_tmp)._system_strescape().data(),
  56106. _codec,bitrate,fps,
  56107. CImg<charT>::string(filename)._system_strescape().data());
  56108. cimg::system(command);
  56109. file = std_fopen(filename,"rb");
  56110. if (!file)
  56111. throw CImgIOException(_cimglist_instance
  56112. "save_ffmpeg_external(): Failed to save file '%s' with external command 'ffmpeg'.",
  56113. cimglist_instance,
  56114. filename);
  56115. else cimg::fclose(file);
  56116. cimglist_for(*this,l) std::remove(filenames[l]);
  56117. return *this;
  56118. }
  56119. //! Serialize a CImgList<T> instance into a raw CImg<unsigned char> buffer.
  56120. /**
  56121. \param is_compressed tells if zlib compression must be used for serialization
  56122. (this requires 'cimg_use_zlib' been enabled).
  56123. **/
  56124. CImg<ucharT> get_serialize(const bool is_compressed=false) const {
  56125. #ifndef cimg_use_zlib
  56126. if (is_compressed)
  56127. cimg::warn(_cimglist_instance
  56128. "get_serialize(): Unable to compress data unless zlib is enabled, "
  56129. "storing them uncompressed.",
  56130. cimglist_instance);
  56131. #endif
  56132. CImgList<ucharT> stream;
  56133. CImg<charT> tmpstr(128);
  56134. const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little";
  56135. if (std::strstr(ptype,"unsigned")==ptype)
  56136. cimg_snprintf(tmpstr,tmpstr._width,"%u unsigned_%s %s_endian\n",_width,ptype + 9,etype);
  56137. else
  56138. cimg_snprintf(tmpstr,tmpstr._width,"%u %s %s_endian\n",_width,ptype,etype);
  56139. CImg<ucharT>::string(tmpstr,false).move_to(stream);
  56140. cimglist_for(*this,l) {
  56141. const CImg<T>& img = _data[l];
  56142. cimg_snprintf(tmpstr,tmpstr._width,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum);
  56143. CImg<ucharT>::string(tmpstr,false).move_to(stream);
  56144. if (img._data) {
  56145. CImg<T> tmp;
  56146. if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); }
  56147. const CImg<T>& ref = cimg::endianness()?tmp:img;
  56148. bool failed_to_compress = true;
  56149. if (is_compressed) {
  56150. #ifdef cimg_use_zlib
  56151. const ulongT siz = sizeof(T)*ref.size();
  56152. uLongf csiz = (ulongT)compressBound(siz);
  56153. Bytef *const cbuf = new Bytef[csiz];
  56154. if (compress(cbuf,&csiz,(Bytef*)ref._data,siz))
  56155. cimg::warn(_cimglist_instance
  56156. "get_serialize(): Failed to save compressed data, saving them uncompressed.",
  56157. cimglist_instance);
  56158. else {
  56159. cimg_snprintf(tmpstr,tmpstr._width," #%lu\n",csiz);
  56160. CImg<ucharT>::string(tmpstr,false).move_to(stream);
  56161. CImg<ucharT>(cbuf,csiz).move_to(stream);
  56162. delete[] cbuf;
  56163. failed_to_compress = false;
  56164. }
  56165. #endif
  56166. }
  56167. if (failed_to_compress) { // Write in a non-compressed way.
  56168. CImg<charT>::string("\n",false).move_to(stream);
  56169. stream.insert(1);
  56170. stream.back().assign((unsigned char*)ref._data,ref.size()*sizeof(T),1,1,1,true);
  56171. }
  56172. } else CImg<charT>::string("\n",false).move_to(stream);
  56173. }
  56174. cimglist_apply(stream,unroll)('y');
  56175. return stream>'y';
  56176. }
  56177. //! Unserialize a CImg<unsigned char> serialized buffer into a CImgList<T> list.
  56178. template<typename t>
  56179. static CImgList<T> get_unserialize(const CImg<t>& buffer) {
  56180. #ifdef cimg_use_zlib
  56181. #define _cimgz_unserialize_case(Tss) { \
  56182. Bytef *cbuf = 0; \
  56183. if (sizeof(t)!=1 || cimg::type<t>::string()==cimg::type<bool>::string()) { \
  56184. cbuf = new Bytef[csiz]; Bytef *_cbuf = cbuf; \
  56185. for (ulongT i = 0; i<csiz; ++i) *(_cbuf++) = (Bytef)*(stream++); \
  56186. is_bytef = false; \
  56187. } else { cbuf = (Bytef*)stream; stream+=csiz; is_bytef = true; } \
  56188. raw.assign(W,H,D,C); \
  56189. uLongf destlen = raw.size()*sizeof(Tss); \
  56190. uncompress((Bytef*)raw._data,&destlen,cbuf,csiz); \
  56191. if (!is_bytef) delete[] cbuf; \
  56192. }
  56193. #else
  56194. #define _cimgz_unserialize_case(Tss) \
  56195. throw CImgArgumentException("CImgList<%s>::get_unserialize(): Unable to unserialize compressed data " \
  56196. "unless zlib is enabled.", \
  56197. pixel_type());
  56198. #endif
  56199. #define _cimg_unserialize_case(Ts,Tss) \
  56200. if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \
  56201. for (unsigned int l = 0; l<N; ++l) { \
  56202. j = 0; while ((i=(int)*stream)!='\n' && stream<estream && j<255) { ++stream; tmp[j++] = (char)i; } \
  56203. ++stream; tmp[j] = 0; \
  56204. W = H = D = C = 0; csiz = 0; \
  56205. if ((err = cimg_sscanf(tmp,"%u %u %u %u #" cimg_fuint64,&W,&H,&D,&C,&csiz))<4) \
  56206. throw CImgArgumentException("CImgList<%s>::unserialize(): Invalid specified size (%u,%u,%u,%u) for " \
  56207. "image #%u in serialized buffer.", \
  56208. pixel_type(),W,H,D,C,l); \
  56209. if (W*H*D*C>0) { \
  56210. CImg<Tss> raw; \
  56211. CImg<T> &img = res._data[l]; \
  56212. if (err==5) _cimgz_unserialize_case(Tss) \
  56213. else if (sizeof(Tss)==sizeof(t) && cimg::type<Tss>::is_float()==cimg::type<t>::is_float()) { \
  56214. raw.assign((Tss*)stream,W,H,D,C,true); \
  56215. stream+=raw.size(); \
  56216. } else { \
  56217. raw.assign(W,H,D,C); \
  56218. CImg<ucharT> _raw((unsigned char*)raw._data,W*sizeof(Tss),H,D,C,true); \
  56219. cimg_for(_raw,p,unsigned char) *p = (unsigned char)*(stream++); \
  56220. } \
  56221. if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \
  56222. raw.move_to(img); \
  56223. } \
  56224. } \
  56225. loaded = true; \
  56226. }
  56227. if (buffer.is_empty())
  56228. throw CImgArgumentException("CImgList<%s>::get_unserialize(): Specified serialized buffer is (null).",
  56229. pixel_type());
  56230. CImgList<T> res;
  56231. const t *stream = buffer._data, *const estream = buffer._data + buffer.size();
  56232. bool loaded = false, endian = cimg::endianness(), is_bytef = false;
  56233. CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
  56234. *tmp = *str_pixeltype = *str_endian = 0;
  56235. unsigned int j, N = 0, W, H, D, C;
  56236. uint64T csiz;
  56237. int i, err;
  56238. cimg::unused(is_bytef);
  56239. do {
  56240. j = 0; while ((i=(int)*stream)!='\n' && stream<estream && j<255) { ++stream; tmp[j++] = (char)i; }
  56241. ++stream; tmp[j] = 0;
  56242. } while (*tmp=='#' && stream<estream);
  56243. err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]",
  56244. &N,str_pixeltype._data,str_endian._data);
  56245. if (err<2)
  56246. throw CImgArgumentException("CImgList<%s>::get_unserialize(): CImg header not found in serialized buffer.",
  56247. pixel_type());
  56248. if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
  56249. else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
  56250. res.assign(N);
  56251. _cimg_unserialize_case("bool",bool);
  56252. _cimg_unserialize_case("unsigned_char",unsigned char);
  56253. _cimg_unserialize_case("uchar",unsigned char);
  56254. _cimg_unserialize_case("char",char);
  56255. _cimg_unserialize_case("unsigned_short",unsigned short);
  56256. _cimg_unserialize_case("ushort",unsigned short);
  56257. _cimg_unserialize_case("short",short);
  56258. _cimg_unserialize_case("unsigned_int",unsigned int);
  56259. _cimg_unserialize_case("uint",unsigned int);
  56260. _cimg_unserialize_case("int",int);
  56261. _cimg_unserialize_case("unsigned_int64",uint64T);
  56262. _cimg_unserialize_case("uint64",uint64T);
  56263. _cimg_unserialize_case("int64",int64T);
  56264. _cimg_unserialize_case("float",float);
  56265. _cimg_unserialize_case("double",double);
  56266. if (!loaded)
  56267. throw CImgArgumentException("CImgList<%s>::get_unserialize(): Unsupported pixel type '%s' defined "
  56268. "in serialized buffer.",
  56269. pixel_type(),str_pixeltype._data);
  56270. return res;
  56271. }
  56272. //@}
  56273. //----------------------------------
  56274. //
  56275. //! \name Others
  56276. //@{
  56277. //----------------------------------
  56278. //! Crop font along the X-axis.
  56279. /**
  56280. **/
  56281. CImgList<T>& crop_font() {
  56282. return get_crop_font().move_to(*this);
  56283. }
  56284. //! Crop font along the X-axis \newinstance.
  56285. /**
  56286. **/
  56287. CImgList<T> get_crop_font() const {
  56288. CImgList<T> res;
  56289. cimglist_for(*this,l) {
  56290. const CImg<T>& letter = (*this)[l];
  56291. int xmin = letter.width(), xmax = 0;
  56292. cimg_forXY(letter,x,y) if (letter(x,y)) { if (x<xmin) xmin = x; if (x>xmax) xmax = x; }
  56293. if (xmin>xmax) CImg<T>(letter._width,letter._height,1,letter._spectrum,0).move_to(res);
  56294. else letter.get_crop(xmin,0,xmax,letter._height - 1).move_to(res);
  56295. }
  56296. res[' '].resize(res['f']._width,-100,-100,-100,0);
  56297. if (' ' + 256<res.size()) res[' ' + 256].resize(res['f']._width,-100,-100,-100,0);
  56298. return res;
  56299. }
  56300. //! Return a CImg pre-defined font with desired size.
  56301. /**
  56302. \param font_height Height of the desired font (exact match for 13,23,53,103).
  56303. \param is_variable_width Decide if the font has a variable (\c true) or fixed (\c false) width.
  56304. **/
  56305. static const CImgList<ucharT>& font(const unsigned int font_height, const bool is_variable_width=true) {
  56306. if (!font_height) return CImgList<ucharT>::const_empty();
  56307. cimg::mutex(11);
  56308. // Decompress nearest base font data if needed.
  56309. static const char *data_fonts[] = { cimg::data_font12x13, cimg::data_font20x23, cimg::data_font47x53, 0 };
  56310. static const unsigned int data_widths[] = { 12,20,47,90 }, data_heights[] = { 13,23,53,103 },
  56311. data_Ms[] = { 86,79,57,47 };
  56312. const unsigned int data_ind = font_height<=13U?0U:font_height<=23U?1U:font_height<=53U?2U:3U;
  56313. static CImg<ucharT> base_fonts[4];
  56314. CImg<ucharT> &base_font = base_fonts[data_ind];
  56315. if (!base_font) {
  56316. const unsigned int w = data_widths[data_ind], h = data_heights[data_ind], M = data_Ms[data_ind];
  56317. base_font.assign(256*w,h);
  56318. const char *data_font = data_fonts[data_ind];
  56319. unsigned char *ptrd = base_font;
  56320. const unsigned char *const ptrde = base_font.end();
  56321. // Special case needed for 90x103 to avoid MS compiler limit with big strings.
  56322. CImg<char> data90x103;
  56323. if (!data_font) {
  56324. ((CImg<char>(cimg::_data_font90x103[0],
  56325. (unsigned int)std::strlen(cimg::_data_font90x103[0]),1,1,1,true),
  56326. CImg<char>(cimg::_data_font90x103[1],
  56327. (unsigned int)std::strlen(cimg::_data_font90x103[1]) + 1,1,1,1,true))>'x').
  56328. move_to(data90x103);
  56329. data_font = data90x103.data();
  56330. }
  56331. // Uncompress font data (decode RLE).
  56332. for (const char *ptrs = data_font; *ptrs; ++ptrs) {
  56333. const int c = (int)(*ptrs - M - 32), v = c>=0?255:0, n = c>=0?c:-c;
  56334. if (ptrd + n<=ptrde) { std::memset(ptrd,v,n); ptrd+=n; }
  56335. else { std::memset(ptrd,v,ptrde - ptrd); break; }
  56336. }
  56337. }
  56338. // Find optimal font cache location to return.
  56339. static CImgList<ucharT> fonts[16];
  56340. static bool is_variable_widths[16] = { 0 };
  56341. unsigned int ind = ~0U;
  56342. for (int i = 0; i<16; ++i)
  56343. if (!fonts[i] || (is_variable_widths[i]==is_variable_width && font_height==fonts[i][0]._height)) {
  56344. ind = (unsigned int)i; break; // Found empty slot or cached font.
  56345. }
  56346. if (ind==~0U) { // No empty slots nor existing font in cache.
  56347. fonts->assign();
  56348. std::memmove(fonts,fonts + 1,15*sizeof(CImgList<ucharT>));
  56349. std::memmove(is_variable_widths,is_variable_widths + 1,15*sizeof(bool));
  56350. std::memset((void*)(fonts + (ind=15)),0,sizeof(CImgList<ucharT>)); // Free a slot in cache for new font.
  56351. }
  56352. CImgList<ucharT> &font = fonts[ind];
  56353. // Render requested font.
  56354. if (!font) {
  56355. const unsigned int padding_x = font_height<33U?1U:font_height<53U?2U:font_height<103U?3U:4U;
  56356. is_variable_widths[ind] = is_variable_width;
  56357. font = base_font.get_split('x',256);
  56358. if (font_height!=font[0]._height)
  56359. cimglist_for(font,l)
  56360. font[l].resize(std::max(1U,font[l]._width*font_height/font[l]._height),font_height,-100,-100,
  56361. font[0]._height>font_height?2:5);
  56362. if (is_variable_width) font.crop_font();
  56363. cimglist_for(font,l) font[l].resize(font[l]._width + padding_x,-100,1,1,0,0,0.5);
  56364. font.insert(256,0);
  56365. cimglist_for_in(font,0,255,l) font[l].assign(font[l + 256]._width,font[l + 256]._height,1,3,1);
  56366. }
  56367. cimg::mutex(11,0);
  56368. return font;
  56369. }
  56370. //! Compute a 1d Fast Fourier Transform, along specified axis.
  56371. /**
  56372. \param axis Axis along which the Fourier transform is computed.
  56373. \param invert Tells if the direct (\c false) or inverse transform (\c true) is computed.
  56374. **/
  56375. CImgList<T>& FFT(const char axis, const bool invert=false) {
  56376. if (is_empty()) return *this;
  56377. if (_width==1) insert(1);
  56378. if (_width>2)
  56379. cimg::warn(_cimglist_instance
  56380. "FFT(): Instance has more than 2 images",
  56381. cimglist_instance);
  56382. CImg<T>::FFT(_data[0],_data[1],axis,invert);
  56383. return *this;
  56384. }
  56385. //! Compute a 1-D Fast Fourier Transform, along specified axis \newinstance.
  56386. CImgList<Tfloat> get_FFT(const char axis, const bool invert=false) const {
  56387. return CImgList<Tfloat>(*this,false).FFT(axis,invert);
  56388. }
  56389. //! Compute a n-d Fast Fourier Transform.
  56390. /**
  56391. \param invert Tells if the direct (\c false) or inverse transform (\c true) is computed.
  56392. **/
  56393. CImgList<T>& FFT(const bool invert=false) {
  56394. if (is_empty()) return *this;
  56395. if (_width==1) insert(1);
  56396. if (_width>2)
  56397. cimg::warn(_cimglist_instance
  56398. "FFT(): Instance has more than 2 images",
  56399. cimglist_instance);
  56400. CImg<T>::FFT(_data[0],_data[1],invert);
  56401. return *this;
  56402. }
  56403. //! Compute a n-d Fast Fourier Transform \newinstance.
  56404. CImgList<Tfloat> get_FFT(const bool invert=false) const {
  56405. return CImgList<Tfloat>(*this,false).FFT(invert);
  56406. }
  56407. //! Reverse primitives orientations of a 3d object.
  56408. /**
  56409. **/
  56410. CImgList<T>& reverse_object3d() {
  56411. cimglist_for(*this,l) {
  56412. CImg<T>& p = _data[l];
  56413. switch (p.size()) {
  56414. case 2 : case 3: cimg::swap(p[0],p[1]); break;
  56415. case 6 : cimg::swap(p[0],p[1],p[2],p[4],p[3],p[5]); break;
  56416. case 9 : cimg::swap(p[0],p[1],p[3],p[5],p[4],p[6]); break;
  56417. case 4 : cimg::swap(p[0],p[1],p[2],p[3]); break;
  56418. 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;
  56419. }
  56420. }
  56421. return *this;
  56422. }
  56423. //! Reverse primitives orientations of a 3d object \newinstance.
  56424. CImgList<T> get_reverse_object3d() const {
  56425. return (+*this).reverse_object3d();
  56426. }
  56427. //@}
  56428. }; // struct CImgList<T> { ...
  56429. /*
  56430. #---------------------------------------------
  56431. #
  56432. # Completion of previously declared functions
  56433. #
  56434. #----------------------------------------------
  56435. */
  56436. namespace cimg {
  56437. // Functions to return standard streams 'stdin', 'stdout' and 'stderr'.
  56438. // (throw a CImgIOException when macro 'cimg_use_r' is defined).
  56439. inline FILE* _stdin(const bool throw_exception) {
  56440. #ifndef cimg_use_r
  56441. cimg::unused(throw_exception);
  56442. return stdin;
  56443. #else
  56444. if (throw_exception) {
  56445. cimg::exception_mode(0);
  56446. throw CImgIOException("cimg::stdin(): Reference to 'stdin' stream not allowed in R mode "
  56447. "('cimg_use_r' is defined).");
  56448. }
  56449. return 0;
  56450. #endif
  56451. }
  56452. inline FILE* _stdout(const bool throw_exception) {
  56453. #ifndef cimg_use_r
  56454. cimg::unused(throw_exception);
  56455. return stdout;
  56456. #else
  56457. if (throw_exception) {
  56458. cimg::exception_mode(0);
  56459. throw CImgIOException("cimg::stdout(): Reference to 'stdout' stream not allowed in R mode "
  56460. "('cimg_use_r' is defined).");
  56461. }
  56462. return 0;
  56463. #endif
  56464. }
  56465. inline FILE* _stderr(const bool throw_exception) {
  56466. #ifndef cimg_use_r
  56467. cimg::unused(throw_exception);
  56468. return stderr;
  56469. #else
  56470. if (throw_exception) {
  56471. cimg::exception_mode(0);
  56472. throw CImgIOException("cimg::stderr(): Reference to 'stderr' stream not allowed in R mode "
  56473. "('cimg_use_r' is defined).");
  56474. }
  56475. return 0;
  56476. #endif
  56477. }
  56478. // Open a file (with wide character support on Windows).
  56479. inline std::FILE *win_fopen(const char *const path, const char *const mode) {
  56480. #if cimg_OS==2
  56481. // Convert 'path' to a wide-character string.
  56482. int err = MultiByteToWideChar(CP_UTF8,0,path,-1,0,0);
  56483. if (!err) return std_fopen(path,mode);
  56484. CImg<wchar_t> wpath(err);
  56485. err = MultiByteToWideChar(CP_UTF8,0,path,-1,wpath,err);
  56486. if (!err) return std_fopen(path,mode);
  56487. // Convert 'mode' to a wide-character string.
  56488. err = MultiByteToWideChar(CP_UTF8,0,mode,-1,0,0);
  56489. if (!err) return std_fopen(path,mode);
  56490. CImg<wchar_t> wmode(err);
  56491. err = MultiByteToWideChar(CP_UTF8,0,mode,-1,wmode,err);
  56492. if (!err) return std_fopen(path,mode);
  56493. return _wfopen(wpath,wmode);
  56494. #else
  56495. return std_fopen(path,mode);
  56496. #endif
  56497. }
  56498. //! Get/set path to store temporary files.
  56499. /**
  56500. \param user_path Specified path, or \c 0 to get the path currently used.
  56501. \param reinit_path Force path to be recalculated (may take some time).
  56502. \return Path where temporary files can be saved.
  56503. **/
  56504. inline const char* temporary_path(const char *const user_path, const bool reinit_path) {
  56505. #define _cimg_test_temporary_path(p) \
  56506. if (!path_found) { \
  56507. cimg_snprintf(s_path,s_path.width(),"%s",p); \
  56508. cimg_snprintf(tmp,tmp._width,"%s%c%s",s_path.data(),cimg_file_separator,filename_tmp._data); \
  56509. if ((file=std_fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; } \
  56510. }
  56511. static CImg<char> s_path;
  56512. cimg::mutex(7);
  56513. if (reinit_path) s_path.assign();
  56514. if (user_path) {
  56515. if (!s_path) s_path.assign(1024);
  56516. std::strncpy(s_path,user_path,1023);
  56517. } else if (!s_path) {
  56518. s_path.assign(1024);
  56519. bool path_found = false;
  56520. CImg<char> tmp(1024), filename_tmp(256);
  56521. std::FILE *file = 0;
  56522. cimg_snprintf(filename_tmp,filename_tmp._width,"%s.tmp",cimg::filenamerand());
  56523. char *tmpPath = std::getenv("TMP");
  56524. if (!tmpPath) { tmpPath = std::getenv("TEMP"); winformat_string(tmpPath); }
  56525. if (tmpPath) _cimg_test_temporary_path(tmpPath);
  56526. #if cimg_OS==2
  56527. _cimg_test_temporary_path("C:\\WINNT\\Temp");
  56528. _cimg_test_temporary_path("C:\\WINDOWS\\Temp");
  56529. _cimg_test_temporary_path("C:\\Temp");
  56530. _cimg_test_temporary_path("C:");
  56531. _cimg_test_temporary_path("D:\\WINNT\\Temp");
  56532. _cimg_test_temporary_path("D:\\WINDOWS\\Temp");
  56533. _cimg_test_temporary_path("D:\\Temp");
  56534. _cimg_test_temporary_path("D:");
  56535. #else
  56536. _cimg_test_temporary_path("/tmp");
  56537. _cimg_test_temporary_path("/var/tmp");
  56538. #endif
  56539. if (!path_found) {
  56540. *s_path = 0;
  56541. std::strncpy(tmp,filename_tmp,tmp._width - 1);
  56542. if ((file=std_fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; }
  56543. }
  56544. if (!path_found) {
  56545. cimg::mutex(7,0);
  56546. throw CImgIOException("cimg::temporary_path(): Failed to locate path for writing temporary files.\n");
  56547. }
  56548. }
  56549. cimg::mutex(7,0);
  56550. return s_path;
  56551. }
  56552. //! Get/set path to the <i>Program Files/</i> directory (Windows only).
  56553. /**
  56554. \param user_path Specified path, or \c 0 to get the path currently used.
  56555. \param reinit_path Force path to be recalculated (may take some time).
  56556. \return Path containing the program files.
  56557. **/
  56558. #if cimg_OS==2
  56559. inline const char* programfiles_path(const char *const user_path, const bool reinit_path) {
  56560. static CImg<char> s_path;
  56561. cimg::mutex(7);
  56562. if (reinit_path) s_path.assign();
  56563. if (user_path) {
  56564. if (!s_path) s_path.assign(1024);
  56565. std::strncpy(s_path,user_path,1023);
  56566. } else if (!s_path) {
  56567. s_path.assign(MAX_PATH);
  56568. *s_path = 0;
  56569. // Note: in the following line, 0x26 = CSIDL_PROGRAM_FILES (not defined on every compiler).
  56570. #if !defined(__INTEL_COMPILER)
  56571. if (!SHGetSpecialFolderPathA(0,s_path,0x0026,false)) {
  56572. const char *const pfPath = std::getenv("PROGRAMFILES");
  56573. if (pfPath) std::strncpy(s_path,pfPath,MAX_PATH - 1);
  56574. else std::strcpy(s_path,"C:\\PROGRA~1");
  56575. }
  56576. #else
  56577. std::strcpy(s_path,"C:\\PROGRA~1");
  56578. #endif
  56579. }
  56580. cimg::mutex(7,0);
  56581. return s_path;
  56582. }
  56583. #endif
  56584. //! Get/set path to the ImageMagick's \c convert binary.
  56585. /**
  56586. \param user_path Specified path, or \c 0 to get the path currently used.
  56587. \param reinit_path Force path to be recalculated (may take some time).
  56588. \return Path containing the \c convert binary.
  56589. **/
  56590. inline const char* imagemagick_path(const char *const user_path, const bool reinit_path) {
  56591. static CImg<char> s_path;
  56592. cimg::mutex(7);
  56593. if (reinit_path) s_path.assign();
  56594. if (user_path) {
  56595. if (!s_path) s_path.assign(1024);
  56596. std::strncpy(s_path,user_path,1023);
  56597. } else if (!s_path) {
  56598. s_path.assign(1024);
  56599. bool path_found = false;
  56600. std::FILE *file = 0;
  56601. #if cimg_OS==2
  56602. const char *const pf_path = programfiles_path();
  56603. for (int l = 0; l<2 && !path_found; ++l) {
  56604. const char *const s_exe = l?"convert":"magick";
  56605. cimg_snprintf(s_path,s_path._width,".\\%s.exe",s_exe);
  56606. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56607. for (int k = 32; k>=10 && !path_found; --k) {
  56608. cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%.2d-\\%s.exe",pf_path,k,s_exe);
  56609. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56610. }
  56611. for (int k = 9; k>=0 && !path_found; --k) {
  56612. cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d-Q\\%s.exe",pf_path,k,s_exe);
  56613. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56614. }
  56615. for (int k = 32; k>=0 && !path_found; --k) {
  56616. cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d\\%s.exe",pf_path,k,s_exe);
  56617. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56618. }
  56619. for (int k = 32; k>=10 && !path_found; --k) {
  56620. cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe);
  56621. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56622. }
  56623. for (int k = 9; k>=0 && !path_found; --k) {
  56624. cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe);
  56625. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56626. }
  56627. for (int k = 32; k>=0 && !path_found; --k) {
  56628. cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe);
  56629. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56630. }
  56631. for (int k = 32; k>=10 && !path_found; --k) {
  56632. cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%.2d-\\%s.exe",k,s_exe);
  56633. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56634. }
  56635. for (int k = 9; k>=0 && !path_found; --k) {
  56636. cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d-Q\\%s.exe",k,s_exe);
  56637. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56638. }
  56639. for (int k = 32; k>=0 && !path_found; --k) {
  56640. cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d\\%s.exe",k,s_exe);
  56641. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56642. }
  56643. for (int k = 32; k>=10 && !path_found; --k) {
  56644. cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",k,s_exe);
  56645. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56646. }
  56647. for (int k = 9; k>=0 && !path_found; --k) {
  56648. cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",k,s_exe);
  56649. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56650. }
  56651. for (int k = 32; k>=0 && !path_found; --k) {
  56652. cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",k,s_exe);
  56653. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56654. }
  56655. for (int k = 32; k>=10 && !path_found; --k) {
  56656. cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%.2d-\\%s.exe",k,s_exe);
  56657. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56658. }
  56659. for (int k = 9; k>=0 && !path_found; --k) {
  56660. cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d-Q\\%s.exe",k,s_exe);
  56661. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56662. }
  56663. for (int k = 32; k>=0 && !path_found; --k) {
  56664. cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d\\%s.exe",k,s_exe);
  56665. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56666. }
  56667. for (int k = 32; k>=10 && !path_found; --k) {
  56668. cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",k,s_exe);
  56669. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56670. }
  56671. for (int k = 9; k>=0 && !path_found; --k) {
  56672. cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",k,s_exe);
  56673. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56674. }
  56675. for (int k = 32; k>=0 && !path_found; --k) {
  56676. cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",k,s_exe);
  56677. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56678. }
  56679. if (!path_found) cimg_snprintf(s_path,s_path._width,"%s.exe",s_exe);
  56680. }
  56681. #else
  56682. std::strcpy(s_path,"./magick");
  56683. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56684. if (!path_found) {
  56685. std::strcpy(s_path,"./convert");
  56686. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56687. }
  56688. if (!path_found) std::strcpy(s_path,"convert");
  56689. #endif
  56690. winformat_string(s_path);
  56691. }
  56692. cimg::mutex(7,0);
  56693. return s_path;
  56694. }
  56695. //! Get/set path to the GraphicsMagick's \c gm binary.
  56696. /**
  56697. \param user_path Specified path, or \c 0 to get the path currently used.
  56698. \param reinit_path Force path to be recalculated (may take some time).
  56699. \return Path containing the \c gm binary.
  56700. **/
  56701. inline const char* graphicsmagick_path(const char *const user_path, const bool reinit_path) {
  56702. static CImg<char> s_path;
  56703. cimg::mutex(7);
  56704. if (reinit_path) s_path.assign();
  56705. if (user_path) {
  56706. if (!s_path) s_path.assign(1024);
  56707. std::strncpy(s_path,user_path,1023);
  56708. } else if (!s_path) {
  56709. s_path.assign(1024);
  56710. bool path_found = false;
  56711. std::FILE *file = 0;
  56712. #if cimg_OS==2
  56713. const char *const pf_path = programfiles_path();
  56714. if (!path_found) {
  56715. std::strcpy(s_path,".\\gm.exe");
  56716. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56717. }
  56718. for (int k = 32; k>=10 && !path_found; --k) {
  56719. cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%.2d-\\gm.exe",pf_path,k);
  56720. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56721. }
  56722. for (int k = 9; k>=0 && !path_found; --k) {
  56723. cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d-Q\\gm.exe",pf_path,k);
  56724. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56725. }
  56726. for (int k = 32; k>=0 && !path_found; --k) {
  56727. cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d\\gm.exe",pf_path,k);
  56728. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56729. }
  56730. for (int k = 32; k>=10 && !path_found; --k) {
  56731. cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",pf_path,k);
  56732. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56733. }
  56734. for (int k = 9; k>=0 && !path_found; --k) {
  56735. cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",pf_path,k);
  56736. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56737. }
  56738. for (int k = 32; k>=0 && !path_found; --k) {
  56739. cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",pf_path,k);
  56740. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56741. }
  56742. for (int k = 32; k>=10 && !path_found; --k) {
  56743. cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%.2d-\\gm.exe",k);
  56744. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56745. }
  56746. for (int k = 9; k>=0 && !path_found; --k) {
  56747. cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d-Q\\gm.exe",k);
  56748. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56749. }
  56750. for (int k = 32; k>=0 && !path_found; --k) {
  56751. cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d\\gm.exe",k);
  56752. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56753. }
  56754. for (int k = 32; k>=10 && !path_found; --k) {
  56755. cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k);
  56756. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56757. }
  56758. for (int k = 9; k>=0 && !path_found; --k) {
  56759. cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k);
  56760. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56761. }
  56762. for (int k = 32; k>=0 && !path_found; --k) {
  56763. cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k);
  56764. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56765. }
  56766. for (int k = 32; k>=10 && !path_found; --k) {
  56767. cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%.2d-\\gm.exe",k);
  56768. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56769. }
  56770. for (int k = 9; k>=0 && !path_found; --k) {
  56771. cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d-Q\\gm.exe",k);
  56772. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56773. }
  56774. for (int k = 32; k>=0 && !path_found; --k) {
  56775. cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d\\gm.exe",k);
  56776. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56777. }
  56778. for (int k = 32; k>=10 && !path_found; --k) {
  56779. cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k);
  56780. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56781. }
  56782. for (int k = 9; k>=0 && !path_found; --k) {
  56783. cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k);
  56784. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56785. }
  56786. for (int k = 32; k>=0 && !path_found; --k) {
  56787. cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k);
  56788. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56789. }
  56790. if (!path_found) std::strcpy(s_path,"gm.exe");
  56791. #else
  56792. if (!path_found) {
  56793. std::strcpy(s_path,"./gm");
  56794. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56795. }
  56796. if (!path_found) std::strcpy(s_path,"gm");
  56797. #endif
  56798. winformat_string(s_path);
  56799. }
  56800. cimg::mutex(7,0);
  56801. return s_path;
  56802. }
  56803. //! Get/set path to the XMedcon's \c medcon binary.
  56804. /**
  56805. \param user_path Specified path, or \c 0 to get the path currently used.
  56806. \param reinit_path Force path to be recalculated (may take some time).
  56807. \return Path containing the \c medcon binary.
  56808. **/
  56809. inline const char* medcon_path(const char *const user_path, const bool reinit_path) {
  56810. static CImg<char> s_path;
  56811. cimg::mutex(7);
  56812. if (reinit_path) s_path.assign();
  56813. if (user_path) {
  56814. if (!s_path) s_path.assign(1024);
  56815. std::strncpy(s_path,user_path,1023);
  56816. } else if (!s_path) {
  56817. s_path.assign(1024);
  56818. bool path_found = false;
  56819. std::FILE *file = 0;
  56820. #if cimg_OS==2
  56821. const char *const pf_path = programfiles_path();
  56822. if (!path_found) {
  56823. std::strcpy(s_path,".\\medcon.exe");
  56824. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56825. }
  56826. if (!path_found) {
  56827. cimg_snprintf(s_path,s_path._width,"%s\\XMedCon\\bin\\medcon.bat",pf_path);
  56828. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56829. }
  56830. if (!path_found) {
  56831. cimg_snprintf(s_path,s_path._width,"%s\\XMedCon\\bin\\medcon.exe",pf_path);
  56832. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56833. }
  56834. if (!path_found) {
  56835. std::strcpy(s_path,"C:\\XMedCon\\bin\\medcon.exe");
  56836. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56837. }
  56838. if (!path_found) std::strcpy(s_path,"medcon.exe");
  56839. #else
  56840. if (!path_found) {
  56841. std::strcpy(s_path,"./medcon");
  56842. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56843. }
  56844. if (!path_found) std::strcpy(s_path,"medcon");
  56845. #endif
  56846. winformat_string(s_path);
  56847. }
  56848. cimg::mutex(7,0);
  56849. return s_path;
  56850. }
  56851. //! Get/set path to the FFMPEG's \c ffmpeg binary.
  56852. /**
  56853. \param user_path Specified path, or \c 0 to get the path currently used.
  56854. \param reinit_path Force path to be recalculated (may take some time).
  56855. \return Path containing the \c ffmpeg binary.
  56856. **/
  56857. inline const char *ffmpeg_path(const char *const user_path, const bool reinit_path) {
  56858. static CImg<char> s_path;
  56859. cimg::mutex(7);
  56860. if (reinit_path) s_path.assign();
  56861. if (user_path) {
  56862. if (!s_path) s_path.assign(1024);
  56863. std::strncpy(s_path,user_path,1023);
  56864. } else if (!s_path) {
  56865. s_path.assign(1024);
  56866. bool path_found = false;
  56867. std::FILE *file = 0;
  56868. #if cimg_OS==2
  56869. if (!path_found) {
  56870. std::strcpy(s_path,".\\ffmpeg.exe");
  56871. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56872. }
  56873. if (!path_found) std::strcpy(s_path,"ffmpeg.exe");
  56874. #else
  56875. if (!path_found) {
  56876. std::strcpy(s_path,"./ffmpeg");
  56877. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56878. }
  56879. if (!path_found) std::strcpy(s_path,"ffmpeg");
  56880. #endif
  56881. winformat_string(s_path);
  56882. }
  56883. cimg::mutex(7,0);
  56884. return s_path;
  56885. }
  56886. //! Get/set path to the \c gzip binary.
  56887. /**
  56888. \param user_path Specified path, or \c 0 to get the path currently used.
  56889. \param reinit_path Force path to be recalculated (may take some time).
  56890. \return Path containing the \c gzip binary.
  56891. **/
  56892. inline const char *gzip_path(const char *const user_path, const bool reinit_path) {
  56893. static CImg<char> s_path;
  56894. cimg::mutex(7);
  56895. if (reinit_path) s_path.assign();
  56896. if (user_path) {
  56897. if (!s_path) s_path.assign(1024);
  56898. std::strncpy(s_path,user_path,1023);
  56899. } else if (!s_path) {
  56900. s_path.assign(1024);
  56901. bool path_found = false;
  56902. std::FILE *file = 0;
  56903. #if cimg_OS==2
  56904. if (!path_found) {
  56905. std::strcpy(s_path,".\\gzip.exe");
  56906. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56907. }
  56908. if (!path_found) std::strcpy(s_path,"gzip.exe");
  56909. #else
  56910. if (!path_found) {
  56911. std::strcpy(s_path,"./gzip");
  56912. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56913. }
  56914. if (!path_found) std::strcpy(s_path,"gzip");
  56915. #endif
  56916. winformat_string(s_path);
  56917. }
  56918. cimg::mutex(7,0);
  56919. return s_path;
  56920. }
  56921. //! Get/set path to the \c gunzip binary.
  56922. /**
  56923. \param user_path Specified path, or \c 0 to get the path currently used.
  56924. \param reinit_path Force path to be recalculated (may take some time).
  56925. \return Path containing the \c gunzip binary.
  56926. **/
  56927. inline const char *gunzip_path(const char *const user_path, const bool reinit_path) {
  56928. static CImg<char> s_path;
  56929. cimg::mutex(7);
  56930. if (reinit_path) s_path.assign();
  56931. if (user_path) {
  56932. if (!s_path) s_path.assign(1024);
  56933. std::strncpy(s_path,user_path,1023);
  56934. } else if (!s_path) {
  56935. s_path.assign(1024);
  56936. bool path_found = false;
  56937. std::FILE *file = 0;
  56938. #if cimg_OS==2
  56939. if (!path_found) {
  56940. std::strcpy(s_path,".\\gunzip.exe");
  56941. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56942. }
  56943. if (!path_found) std::strcpy(s_path,"gunzip.exe");
  56944. #else
  56945. if (!path_found) {
  56946. std::strcpy(s_path,"./gunzip");
  56947. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56948. }
  56949. if (!path_found) std::strcpy(s_path,"gunzip");
  56950. #endif
  56951. winformat_string(s_path);
  56952. }
  56953. cimg::mutex(7,0);
  56954. return s_path;
  56955. }
  56956. //! Get/set path to the \c dcraw binary.
  56957. /**
  56958. \param user_path Specified path, or \c 0 to get the path currently used.
  56959. \param reinit_path Force path to be recalculated (may take some time).
  56960. \return Path containing the \c dcraw binary.
  56961. **/
  56962. inline const char *dcraw_path(const char *const user_path, const bool reinit_path) {
  56963. static CImg<char> s_path;
  56964. cimg::mutex(7);
  56965. if (reinit_path) s_path.assign();
  56966. if (user_path) {
  56967. if (!s_path) s_path.assign(1024);
  56968. std::strncpy(s_path,user_path,1023);
  56969. } else if (!s_path) {
  56970. s_path.assign(1024);
  56971. bool path_found = false;
  56972. std::FILE *file = 0;
  56973. #if cimg_OS==2
  56974. if (!path_found) {
  56975. std::strcpy(s_path,".\\dcraw.exe");
  56976. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56977. }
  56978. if (!path_found) std::strcpy(s_path,"dcraw.exe");
  56979. #else
  56980. if (!path_found) {
  56981. std::strcpy(s_path,"./dcraw");
  56982. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  56983. }
  56984. if (!path_found) std::strcpy(s_path,"dcraw");
  56985. #endif
  56986. winformat_string(s_path);
  56987. }
  56988. cimg::mutex(7,0);
  56989. return s_path;
  56990. }
  56991. //! Get/set path to the \c wget binary.
  56992. /**
  56993. \param user_path Specified path, or \c 0 to get the path currently used.
  56994. \param reinit_path Force path to be recalculated (may take some time).
  56995. \return Path containing the \c wget binary.
  56996. **/
  56997. inline const char *wget_path(const char *const user_path, const bool reinit_path) {
  56998. static CImg<char> s_path;
  56999. cimg::mutex(7);
  57000. if (reinit_path) s_path.assign();
  57001. if (user_path) {
  57002. if (!s_path) s_path.assign(1024);
  57003. std::strncpy(s_path,user_path,1023);
  57004. } else if (!s_path) {
  57005. s_path.assign(1024);
  57006. bool path_found = false;
  57007. std::FILE *file = 0;
  57008. #if cimg_OS==2
  57009. if (!path_found) {
  57010. std::strcpy(s_path,".\\wget.exe");
  57011. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  57012. }
  57013. if (!path_found) std::strcpy(s_path,"wget.exe");
  57014. #else
  57015. if (!path_found) {
  57016. std::strcpy(s_path,"./wget");
  57017. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  57018. }
  57019. if (!path_found) std::strcpy(s_path,"wget");
  57020. #endif
  57021. winformat_string(s_path);
  57022. }
  57023. cimg::mutex(7,0);
  57024. return s_path;
  57025. }
  57026. //! Get/set path to the \c curl binary.
  57027. /**
  57028. \param user_path Specified path, or \c 0 to get the path currently used.
  57029. \param reinit_path Force path to be recalculated (may take some time).
  57030. \return Path containing the \c curl binary.
  57031. **/
  57032. inline const char *curl_path(const char *const user_path, const bool reinit_path) {
  57033. static CImg<char> s_path;
  57034. cimg::mutex(7);
  57035. if (reinit_path) s_path.assign();
  57036. if (user_path) {
  57037. if (!s_path) s_path.assign(1024);
  57038. std::strncpy(s_path,user_path,1023);
  57039. } else if (!s_path) {
  57040. s_path.assign(1024);
  57041. bool path_found = false;
  57042. std::FILE *file = 0;
  57043. #if cimg_OS==2
  57044. if (!path_found) {
  57045. std::strcpy(s_path,".\\curl.exe");
  57046. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  57047. }
  57048. if (!path_found) std::strcpy(s_path,"curl.exe");
  57049. #else
  57050. if (!path_found) {
  57051. std::strcpy(s_path,"./curl");
  57052. if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  57053. }
  57054. if (!path_found) std::strcpy(s_path,"curl");
  57055. #endif
  57056. winformat_string(s_path);
  57057. }
  57058. cimg::mutex(7,0);
  57059. return s_path;
  57060. }
  57061. // [internal] Sorting function, used by cimg::files().
  57062. inline int _sort_files(const void* a, const void* b) {
  57063. const CImg<char> &sa = *(CImg<char>*)a, &sb = *(CImg<char>*)b;
  57064. return std::strcmp(sa._data,sb._data);
  57065. }
  57066. //! Return list of files/directories in specified directory.
  57067. /**
  57068. \param path Path to the directory. Set to 0 for current directory.
  57069. \param is_pattern Tell if specified path has a matching pattern in it.
  57070. \param mode Output type, can be primary { 0=files only | 1=folders only | 2=files + folders }.
  57071. \param include_path Tell if \c path must be included in resulting filenames.
  57072. \return A list of filenames.
  57073. **/
  57074. inline CImgList<char> files(const char *const path, const bool is_pattern=false,
  57075. const unsigned int mode=2, const bool include_path=false) {
  57076. if (!path || !*path) return files("*",true,mode,include_path);
  57077. CImgList<char> res;
  57078. // If path is a valid folder name, ignore argument 'is_pattern'.
  57079. const bool _is_pattern = is_pattern && !cimg::is_directory(path);
  57080. bool is_root = false, is_current = false;
  57081. cimg::unused(is_root,is_current);
  57082. // Clean format of input path.
  57083. CImg<char> pattern, _path = CImg<char>::string(path);
  57084. #if cimg_OS==2
  57085. for (char *ps = _path; *ps; ++ps) if (*ps=='\\') *ps='/';
  57086. #endif
  57087. char *pd = _path;
  57088. for (char *ps = pd; *ps; ++ps) { if (*ps!='/' || *ps!=*(ps+1)) *(pd++) = *ps; }
  57089. *pd = 0;
  57090. unsigned int lp = (unsigned int)std::strlen(_path);
  57091. if (!_is_pattern && lp && _path[lp - 1]=='/') {
  57092. _path[lp - 1] = 0; --lp;
  57093. #if cimg_OS!=2
  57094. is_root = !*_path;
  57095. #endif
  57096. }
  57097. // Separate folder path and matching pattern.
  57098. if (_is_pattern) {
  57099. const unsigned int bpos = (unsigned int)(cimg::basename(_path,'/') - _path.data());
  57100. CImg<char>::string(_path).move_to(pattern);
  57101. if (bpos) {
  57102. _path[bpos - 1] = 0; // End 'path' at last slash.
  57103. #if cimg_OS!=2
  57104. is_root = !*_path;
  57105. #endif
  57106. } else { // No path to folder specified, assuming current folder.
  57107. is_current = true; *_path = 0;
  57108. }
  57109. lp = (unsigned int)std::strlen(_path);
  57110. }
  57111. // Windows version.
  57112. #if cimg_OS==2
  57113. if (!_is_pattern) {
  57114. pattern.assign(lp + 3);
  57115. std::memcpy(pattern,_path,lp);
  57116. pattern[lp] = '/'; pattern[lp + 1] = '*'; pattern[lp + 2] = 0;
  57117. }
  57118. WIN32_FIND_DATAA file_data;
  57119. const HANDLE dir = FindFirstFileA(pattern.data(),&file_data);
  57120. if (dir==INVALID_HANDLE_VALUE) return CImgList<char>::const_empty();
  57121. do {
  57122. const char *const filename = file_data.cFileName;
  57123. if (*filename!='.' || (filename[1] && (filename[1]!='.' || filename[2]))) {
  57124. const unsigned int lf = (unsigned int)std::strlen(filename);
  57125. const bool is_directory = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)!=0;
  57126. if ((!mode && !is_directory) || (mode==1 && is_directory) || mode>=2) {
  57127. if (include_path) {
  57128. CImg<char> full_filename((lp?lp+1:0) + lf + 1);
  57129. if (lp) { std::memcpy(full_filename,_path,lp); full_filename[lp] = '/'; }
  57130. std::memcpy(full_filename._data + (lp?lp + 1:0),filename,lf + 1);
  57131. full_filename.move_to(res);
  57132. } else CImg<char>(filename,lf + 1).move_to(res);
  57133. }
  57134. }
  57135. } while (FindNextFileA(dir,&file_data));
  57136. FindClose(dir);
  57137. // Unix version (posix).
  57138. #elif cimg_OS == 1
  57139. DIR *const dir = opendir(is_root?"/":is_current?".":_path.data());
  57140. if (!dir) return CImgList<char>::const_empty();
  57141. struct dirent *ent;
  57142. while ((ent=readdir(dir))!=0) {
  57143. const char *const filename = ent->d_name;
  57144. if (*filename!='.' || (filename[1] && (filename[1]!='.' || filename[2]))) {
  57145. const unsigned int lf = (unsigned int)std::strlen(filename);
  57146. CImg<char> full_filename(lp + lf + 2);
  57147. if (!is_current) {
  57148. full_filename.assign(lp + lf + 2);
  57149. if (lp) std::memcpy(full_filename,_path,lp);
  57150. full_filename[lp] = '/';
  57151. std::memcpy(full_filename._data + lp + 1,filename,lf + 1);
  57152. } else full_filename.assign(filename,lf + 1);
  57153. struct stat st;
  57154. if (stat(full_filename,&st)==-1) continue;
  57155. const bool is_directory = (st.st_mode & S_IFDIR)!=0;
  57156. if ((!mode && !is_directory) || (mode==1 && is_directory) || mode==2) {
  57157. if (include_path) {
  57158. if (!_is_pattern || (_is_pattern && !fnmatch(pattern,full_filename,0)))
  57159. full_filename.move_to(res);
  57160. } else {
  57161. if (!_is_pattern || (_is_pattern && !fnmatch(pattern,full_filename,0)))
  57162. CImg<char>(filename,lf + 1).move_to(res);
  57163. }
  57164. }
  57165. }
  57166. }
  57167. closedir(dir);
  57168. #endif
  57169. // Sort resulting list by lexicographic order.
  57170. if (res._width>=2) std::qsort(res._data,res._width,sizeof(CImg<char>),_sort_files);
  57171. return res;
  57172. }
  57173. //! Try to guess format from an image file.
  57174. /**
  57175. \param file Input file (can be \c 0 if \c filename is set).
  57176. \param filename Filename, as a C-string (can be \c 0 if \c file is set).
  57177. \return C-string containing the guessed file format, or \c 0 if nothing has been guessed.
  57178. **/
  57179. inline const char *ftype(std::FILE *const file, const char *const filename) {
  57180. if (!file && !filename)
  57181. throw CImgArgumentException("cimg::ftype(): Specified filename is (null).");
  57182. static const char
  57183. *const _pnm = "pnm",
  57184. *const _pfm = "pfm",
  57185. *const _bmp = "bmp",
  57186. *const _gif = "gif",
  57187. *const _jpg = "jpg",
  57188. *const _off = "off",
  57189. *const _pan = "pan",
  57190. *const _png = "png",
  57191. *const _tif = "tif",
  57192. *const _inr = "inr",
  57193. *const _dcm = "dcm";
  57194. const char *f_type = 0;
  57195. CImg<char> header;
  57196. const unsigned int omode = cimg::exception_mode();
  57197. cimg::exception_mode(0);
  57198. try {
  57199. header._load_raw(file,filename,512,1,1,1,false,false,0);
  57200. const unsigned char *const uheader = (unsigned char*)header._data;
  57201. if (!std::strncmp(header,"OFF\n",4)) f_type = _off; // OFF.
  57202. else if (!std::strncmp(header,"#INRIMAGE",9)) f_type = _inr; // INRIMAGE.
  57203. else if (!std::strncmp(header,"PANDORE",7)) f_type = _pan; // PANDORE.
  57204. else if (!std::strncmp(header.data() + 128,"DICM",4)) f_type = _dcm; // DICOM.
  57205. else if (uheader[0]==0xFF && uheader[1]==0xD8 && uheader[2]==0xFF) f_type = _jpg; // JPEG.
  57206. else if (header[0]=='B' && header[1]=='M') f_type = _bmp; // BMP.
  57207. else if (header[0]=='G' && header[1]=='I' && header[2]=='F' && header[3]=='8' && header[5]=='a' && // GIF.
  57208. (header[4]=='7' || header[4]=='9')) f_type = _gif;
  57209. else if (uheader[0]==0x89 && uheader[1]==0x50 && uheader[2]==0x4E && uheader[3]==0x47 && // PNG.
  57210. uheader[4]==0x0D && uheader[5]==0x0A && uheader[6]==0x1A && uheader[7]==0x0A) f_type = _png;
  57211. else if ((uheader[0]==0x49 && uheader[1]==0x49) || (uheader[0]==0x4D && uheader[1]==0x4D)) f_type = _tif; // TIFF.
  57212. else { // PNM or PFM.
  57213. CImgList<char> _header = header.get_split(CImg<char>::vector('\n'),0,false);
  57214. cimglist_for(_header,l) {
  57215. if (_header(l,0)=='#') continue;
  57216. if (_header[l]._height==2 && _header(l,0)=='P') {
  57217. const char c = _header(l,1);
  57218. if (c=='f' || c=='F') { f_type = _pfm; break; }
  57219. if (c>='1' && c<='9') { f_type = _pnm; break; }
  57220. }
  57221. f_type = 0; break;
  57222. }
  57223. }
  57224. } catch (CImgIOException&) { }
  57225. cimg::exception_mode(omode);
  57226. return f_type;
  57227. }
  57228. //! Load file from network as a local temporary file.
  57229. /**
  57230. \param url URL of the filename, as a C-string.
  57231. \param[out] filename_local C-string containing the path to a local copy of \c filename.
  57232. \param timeout Maximum time (in seconds) authorized for downloading the file from the URL.
  57233. \param try_fallback When using libcurl, tells using system calls as fallbacks in case of libcurl failure.
  57234. \param referer Referer used, as a C-string.
  57235. \return Value of \c filename_local.
  57236. \note Use the \c libcurl library, or the external binaries \c wget or \c curl to perform the download.
  57237. **/
  57238. inline char *load_network(const char *const url, char *const filename_local,
  57239. const unsigned int timeout, const bool try_fallback,
  57240. const char *const referer) {
  57241. if (!url)
  57242. throw CImgArgumentException("cimg::load_network(): Specified URL is (null).");
  57243. if (!filename_local)
  57244. throw CImgArgumentException("cimg::load_network(): Specified destination string is (null).");
  57245. const char *const __ext = cimg::split_filename(url), *const _ext = (*__ext && __ext>url)?__ext - 1:__ext;
  57246. CImg<char> ext = CImg<char>::string(_ext);
  57247. std::FILE *file = 0;
  57248. *filename_local = 0;
  57249. if (ext._width>16 || !cimg::strncasecmp(ext,"cgi",3)) *ext = 0;
  57250. else cimg::strwindows_reserved(ext);
  57251. do {
  57252. cimg_snprintf(filename_local,256,"%s%c%s%s",
  57253. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext._data);
  57254. if ((file=std_fopen(filename_local,"rb"))!=0) cimg::fclose(file);
  57255. } while (file);
  57256. #ifdef cimg_use_curl
  57257. const unsigned int omode = cimg::exception_mode();
  57258. cimg::exception_mode(0);
  57259. try {
  57260. CURL *curl = 0;
  57261. CURLcode res;
  57262. curl = curl_easy_init();
  57263. if (curl) {
  57264. file = cimg::fopen(filename_local,"wb");
  57265. curl_easy_setopt(curl,CURLOPT_URL,url);
  57266. curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,0);
  57267. curl_easy_setopt(curl,CURLOPT_WRITEDATA,file);
  57268. curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,0L);
  57269. curl_easy_setopt(curl,CURLOPT_SSL_VERIFYHOST,0L);
  57270. curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,1L);
  57271. if (timeout) curl_easy_setopt(curl,CURLOPT_TIMEOUT,(long)timeout);
  57272. if (std::strchr(url,'?')) curl_easy_setopt(curl,CURLOPT_HTTPGET,1L);
  57273. if (referer) curl_easy_setopt(curl,CURLOPT_REFERER,referer);
  57274. res = curl_easy_perform(curl);
  57275. curl_easy_cleanup(curl);
  57276. cimg::fseek(file,0,SEEK_END); // Check if file size is 0.
  57277. const cimg_ulong siz = cimg::ftell(file);
  57278. cimg::fclose(file);
  57279. if (siz>0 && res==CURLE_OK) {
  57280. cimg::exception_mode(omode);
  57281. return filename_local;
  57282. } else std::remove(filename_local);
  57283. }
  57284. } catch (...) { }
  57285. cimg::exception_mode(omode);
  57286. if (!try_fallback) throw CImgIOException("cimg::load_network(): Failed to load file '%s' with libcurl.",url);
  57287. #endif
  57288. CImg<char> command((unsigned int)std::strlen(url) + 64);
  57289. cimg::unused(try_fallback);
  57290. // Try with 'curl' first.
  57291. if (timeout) {
  57292. if (referer)
  57293. cimg_snprintf(command,command._width,"%s -e %s -m %u -f --silent --compressed -o \"%s\" \"%s\"",
  57294. cimg::curl_path(),referer,timeout,filename_local,url);
  57295. else
  57296. cimg_snprintf(command,command._width,"%s -m %u -f --silent --compressed -o \"%s\" \"%s\"",
  57297. cimg::curl_path(),timeout,filename_local,url);
  57298. } else {
  57299. if (referer)
  57300. cimg_snprintf(command,command._width,"%s -e %s -f --silent --compressed -o \"%s\" \"%s\"",
  57301. cimg::curl_path(),referer,filename_local,url);
  57302. else
  57303. cimg_snprintf(command,command._width,"%s -f --silent --compressed -o \"%s\" \"%s\"",
  57304. cimg::curl_path(),filename_local,url);
  57305. }
  57306. cimg::system(command);
  57307. if (!(file = std_fopen(filename_local,"rb"))) {
  57308. // Try with 'wget' otherwise.
  57309. if (timeout) {
  57310. if (referer)
  57311. cimg_snprintf(command,command._width,"%s --referer=%s -T %u -q -r -l 0 --no-cache -O \"%s\" \"%s\"",
  57312. cimg::wget_path(),referer,timeout,filename_local,url);
  57313. else
  57314. cimg_snprintf(command,command._width,"%s -T %u -q -r -l 0 --no-cache -O \"%s\" \"%s\"",
  57315. cimg::wget_path(),timeout,filename_local,url);
  57316. } else {
  57317. if (referer)
  57318. cimg_snprintf(command,command._width,"%s --referer=%s -q -r -l 0 --no-cache -O \"%s\" \"%s\"",
  57319. cimg::wget_path(),referer,filename_local,url);
  57320. else
  57321. cimg_snprintf(command,command._width,"%s -q -r -l 0 --no-cache -O \"%s\" \"%s\"",
  57322. cimg::wget_path(),filename_local,url);
  57323. }
  57324. cimg::system(command);
  57325. if (!(file = std_fopen(filename_local,"rb")))
  57326. throw CImgIOException("cimg::load_network(): Failed to load file '%s' with external commands "
  57327. "'wget' or 'curl'.",url);
  57328. cimg::fclose(file);
  57329. // Try gunzip it.
  57330. cimg_snprintf(command,command._width,"%s.gz",filename_local);
  57331. std::rename(filename_local,command);
  57332. cimg_snprintf(command,command._width,"%s --quiet \"%s.gz\"",
  57333. gunzip_path(),filename_local);
  57334. cimg::system(command);
  57335. file = std_fopen(filename_local,"rb");
  57336. if (!file) {
  57337. cimg_snprintf(command,command._width,"%s.gz",filename_local);
  57338. std::rename(command,filename_local);
  57339. file = std_fopen(filename_local,"rb");
  57340. }
  57341. }
  57342. cimg::fseek(file,0,SEEK_END); // Check if file size is 0.
  57343. if (std::ftell(file)<=0)
  57344. throw CImgIOException("cimg::load_network(): Failed to load URL '%s' with external commands "
  57345. "'wget' or 'curl'.",url);
  57346. cimg::fclose(file);
  57347. return filename_local;
  57348. }
  57349. // Implement a tic/toc mechanism to display elapsed time of algorithms.
  57350. inline cimg_ulong tictoc(const bool is_tic) {
  57351. cimg::mutex(2);
  57352. static CImg<cimg_ulong> times(64);
  57353. static unsigned int pos = 0;
  57354. const cimg_ulong t1 = cimg::time();
  57355. if (is_tic) {
  57356. // Tic
  57357. times[pos++] = t1;
  57358. if (pos>=times._width)
  57359. throw CImgArgumentException("cimg::tic(): Too much calls to 'cimg::tic()' without calls to 'cimg::toc()'.");
  57360. cimg::mutex(2,0);
  57361. return t1;
  57362. }
  57363. // Toc
  57364. if (!pos)
  57365. throw CImgArgumentException("cimg::toc(): No previous call to 'cimg::tic()' has been made.");
  57366. const cimg_ulong
  57367. t0 = times[--pos],
  57368. dt = t1>=t0?(t1 - t0):cimg::type<cimg_ulong>::max();
  57369. const unsigned int
  57370. edays = (unsigned int)(dt/86400000.0),
  57371. ehours = (unsigned int)((dt - edays*86400000.0)/3600000.0),
  57372. emin = (unsigned int)((dt - edays*86400000.0 - ehours*3600000.0)/60000.0),
  57373. esec = (unsigned int)((dt - edays*86400000.0 - ehours*3600000.0 - emin*60000.0)/1000.0),
  57374. ems = (unsigned int)(dt - edays*86400000.0 - ehours*3600000.0 - emin*60000.0 - esec*1000.0);
  57375. if (!edays && !ehours && !emin && !esec)
  57376. std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u ms%s\n",
  57377. cimg::t_red,1 + 2*pos,"",ems,cimg::t_normal);
  57378. else {
  57379. if (!edays && !ehours && !emin)
  57380. std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u sec %u ms%s\n",
  57381. cimg::t_red,1 + 2*pos,"",esec,ems,cimg::t_normal);
  57382. else {
  57383. if (!edays && !ehours)
  57384. std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u min %u sec %u ms%s\n",
  57385. cimg::t_red,1 + 2*pos,"",emin,esec,ems,cimg::t_normal);
  57386. else{
  57387. if (!edays)
  57388. std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u hours %u min %u sec %u ms%s\n",
  57389. cimg::t_red,1 + 2*pos,"",ehours,emin,esec,ems,cimg::t_normal);
  57390. else{
  57391. std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u days %u hours %u min %u sec %u ms%s\n",
  57392. cimg::t_red,1 + 2*pos,"",edays,ehours,emin,esec,ems,cimg::t_normal);
  57393. }
  57394. }
  57395. }
  57396. }
  57397. cimg::mutex(2,0);
  57398. return dt;
  57399. }
  57400. // Return a temporary string describing the size of a memory buffer.
  57401. inline const char *strbuffersize(const cimg_ulong size) {
  57402. static CImg<char> res(256);
  57403. cimg::mutex(5);
  57404. if (size<1024LU) cimg_snprintf(res,res._width,"%lu byte%s",(unsigned long)size,size>1?"s":"");
  57405. else if (size<1024*1024LU) { const float nsize = size/1024.0f; cimg_snprintf(res,res._width,"%.1f Kio",nsize); }
  57406. else if (size<1024*1024*1024LU) {
  57407. const float nsize = size/(1024*1024.0f); cimg_snprintf(res,res._width,"%.1f Mio",nsize);
  57408. } else { const float nsize = size/(1024*1024*1024.0f); cimg_snprintf(res,res._width,"%.1f Gio",nsize); }
  57409. cimg::mutex(5,0);
  57410. return res;
  57411. }
  57412. //! Display a simple dialog box, and wait for the user's response.
  57413. /**
  57414. \param title Title of the dialog window.
  57415. \param msg Main message displayed inside the dialog window.
  57416. \param button1_label Label of the 1st button.
  57417. \param button2_label Label of the 2nd button (\c 0 to hide button).
  57418. \param button3_label Label of the 3rd button (\c 0 to hide button).
  57419. \param button4_label Label of the 4th button (\c 0 to hide button).
  57420. \param button5_label Label of the 5th button (\c 0 to hide button).
  57421. \param button6_label Label of the 6th button (\c 0 to hide button).
  57422. \param logo Image logo displayed at the left of the main message.
  57423. \param is_centered Tells if the dialog window must be centered on the screen.
  57424. \return Indice of clicked button (from \c 0 to \c 5), or \c -1 if the dialog window has been closed by the user.
  57425. \note
  57426. - Up to 6 buttons can be defined in the dialog window.
  57427. - The function returns when a user clicked one of the button or closed the dialog window.
  57428. - If a button text is set to 0, the corresponding button (and the followings) will not appear in the dialog box.
  57429. At least one button must be specified.
  57430. **/
  57431. template<typename t>
  57432. inline int dialog(const char *const title, const char *const msg,
  57433. const char *const button1_label, const char *const button2_label,
  57434. const char *const button3_label, const char *const button4_label,
  57435. const char *const button5_label, const char *const button6_label,
  57436. const CImg<t>& logo, const bool is_centered=false) {
  57437. #if cimg_display==0
  57438. cimg::unused(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label,
  57439. logo._data,is_centered);
  57440. throw CImgIOException("cimg::dialog(): No display available.");
  57441. #else
  57442. static const unsigned char
  57443. black[] = { 0,0,0 }, white[] = { 255,255,255 }, gray[] = { 200,200,200 }, gray2[] = { 150,150,150 };
  57444. // Create buttons and canvas graphics
  57445. CImgList<unsigned char> buttons, cbuttons, sbuttons;
  57446. if (button1_label) { CImg<unsigned char>().draw_text(0,0,button1_label,black,gray,1,13).move_to(buttons);
  57447. if (button2_label) { CImg<unsigned char>().draw_text(0,0,button2_label,black,gray,1,13).move_to(buttons);
  57448. if (button3_label) { CImg<unsigned char>().draw_text(0,0,button3_label,black,gray,1,13).move_to(buttons);
  57449. if (button4_label) { CImg<unsigned char>().draw_text(0,0,button4_label,black,gray,1,13).move_to(buttons);
  57450. if (button5_label) { CImg<unsigned char>().draw_text(0,0,button5_label,black,gray,1,13).move_to(buttons);
  57451. if (button6_label) { CImg<unsigned char>().draw_text(0,0,button6_label,black,gray,1,13).move_to(buttons);
  57452. }}}}}}
  57453. if (!buttons._width)
  57454. throw CImgArgumentException("cimg::dialog(): No buttons have been defined.");
  57455. cimglist_for(buttons,l) buttons[l].resize(-100,-100,1,3);
  57456. unsigned int bw = 0, bh = 0;
  57457. cimglist_for(buttons,l) { bw = std::max(bw,buttons[l]._width); bh = std::max(bh,buttons[l]._height); }
  57458. bw+=8; bh+=8;
  57459. if (bw<64) bw = 64;
  57460. if (bw>128) bw = 128;
  57461. if (bh<24) bh = 24;
  57462. if (bh>48) bh = 48;
  57463. CImg<unsigned char> button(bw,bh,1,3);
  57464. button.draw_rectangle(0,0,bw - 1,bh - 1,gray);
  57465. button.draw_line(0,0,bw - 1,0,white).draw_line(0,bh - 1,0,0,white);
  57466. button.draw_line(bw - 1,0,bw - 1,bh - 1,black).draw_line(bw - 1,bh - 1,0,bh - 1,black);
  57467. button.draw_line(1,bh - 2,bw - 2,bh - 2,gray2).draw_line(bw - 2,bh - 2,bw - 2,1,gray2);
  57468. CImg<unsigned char> sbutton(bw,bh,1,3);
  57469. sbutton.draw_rectangle(0,0,bw - 1,bh - 1,gray);
  57470. sbutton.draw_line(0,0,bw - 1,0,black).draw_line(bw - 1,0,bw - 1,bh - 1,black);
  57471. sbutton.draw_line(bw - 1,bh - 1,0,bh - 1,black).draw_line(0,bh - 1,0,0,black);
  57472. sbutton.draw_line(1,1,bw - 2,1,white).draw_line(1,bh - 2,1,1,white);
  57473. sbutton.draw_line(bw - 2,1,bw - 2,bh - 2,black).draw_line(bw - 2,bh - 2,1,bh - 2,black);
  57474. sbutton.draw_line(2,bh - 3,bw - 3,bh - 3,gray2).draw_line(bw - 3,bh - 3,bw - 3,2,gray2);
  57475. sbutton.draw_line(4,4,bw - 5,4,black,1,0xAAAAAAAA,true).draw_line(bw - 5,4,bw - 5,bh - 5,black,1,0xAAAAAAAA,false);
  57476. sbutton.draw_line(bw - 5,bh - 5,4,bh - 5,black,1,0xAAAAAAAA,false).draw_line(4,bh - 5,4,4,black,1,0xAAAAAAAA,false);
  57477. CImg<unsigned char> cbutton(bw,bh,1,3);
  57478. cbutton.draw_rectangle(0,0,bw - 1,bh - 1,black).draw_rectangle(1,1,bw - 2,bh - 2,gray2).
  57479. draw_rectangle(2,2,bw - 3,bh - 3,gray);
  57480. cbutton.draw_line(4,4,bw - 5,4,black,1,0xAAAAAAAA,true).draw_line(bw - 5,4,bw - 5,bh - 5,black,1,0xAAAAAAAA,false);
  57481. cbutton.draw_line(bw - 5,bh - 5,4,bh - 5,black,1,0xAAAAAAAA,false).draw_line(4,bh - 5,4,4,black,1,0xAAAAAAAA,false);
  57482. cimglist_for(buttons,ll) {
  57483. CImg<unsigned char>(cbutton).
  57484. draw_image(1 + (bw -buttons[ll].width())/2,1 + (bh - buttons[ll].height())/2,buttons[ll]).
  57485. move_to(cbuttons);
  57486. CImg<unsigned char>(sbutton).
  57487. draw_image((bw - buttons[ll].width())/2,(bh - buttons[ll].height())/2,buttons[ll]).
  57488. move_to(sbuttons);
  57489. CImg<unsigned char>(button).
  57490. draw_image((bw - buttons[ll].width())/2,(bh - buttons[ll].height())/2,buttons[ll]).
  57491. move_to(buttons[ll]);
  57492. }
  57493. CImg<unsigned char> canvas;
  57494. if (msg)
  57495. ((CImg<unsigned char>().draw_text(0,0,"%s",gray,0,1,13,msg)*=-1)+=200).resize(-100,-100,1,3).move_to(canvas);
  57496. const unsigned int
  57497. bwall = (buttons._width - 1)*(12 + bw) + bw,
  57498. w = cimg::max(196U,36 + logo._width + canvas._width,24 + bwall),
  57499. h = cimg::max(96U,36 + canvas._height + bh,36 + logo._height + bh),
  57500. lx = 12 + (canvas._data?0:((w - 24 - logo._width)/2)),
  57501. ly = (h - 12 - bh - logo._height)/2,
  57502. tx = lx + logo._width + 12,
  57503. ty = (h - 12 - bh - canvas._height)/2,
  57504. bx = (w - bwall)/2,
  57505. by = h - 12 - bh;
  57506. if (canvas._data)
  57507. canvas = CImg<unsigned char>(w,h,1,3).
  57508. draw_rectangle(0,0,w - 1,h - 1,gray).
  57509. draw_line(0,0,w - 1,0,white).draw_line(0,h - 1,0,0,white).
  57510. draw_line(w - 1,0,w - 1,h - 1,black).draw_line(w - 1,h - 1,0,h - 1,black).
  57511. draw_image(tx,ty,canvas);
  57512. else
  57513. canvas = CImg<unsigned char>(w,h,1,3).
  57514. draw_rectangle(0,0,w - 1,h - 1,gray).
  57515. draw_line(0,0,w - 1,0,white).draw_line(0,h - 1,0,0,white).
  57516. draw_line(w - 1,0,w - 1,h - 1,black).draw_line(w - 1,h - 1,0,h - 1,black);
  57517. if (logo._data) canvas.draw_image(lx,ly,logo);
  57518. unsigned int xbuttons[6] = { 0 };
  57519. cimglist_for(buttons,lll) { xbuttons[lll] = bx + (bw + 12)*lll; canvas.draw_image(xbuttons[lll],by,buttons[lll]); }
  57520. // Open window and enter events loop
  57521. CImgDisplay disp(canvas,title?title:" ",0,false,is_centered?true:false);
  57522. if (is_centered) disp.move((CImgDisplay::screen_width() - disp.width())/2,
  57523. (CImgDisplay::screen_height() - disp.height())/2);
  57524. bool stop_flag = false, refresh = false;
  57525. int oselected = -1, oclicked = -1, selected = -1, clicked = -1;
  57526. while (!disp.is_closed() && !stop_flag) {
  57527. if (refresh) {
  57528. if (clicked>=0)
  57529. CImg<unsigned char>(canvas).draw_image(xbuttons[clicked],by,cbuttons[clicked]).display(disp);
  57530. else {
  57531. if (selected>=0)
  57532. CImg<unsigned char>(canvas).draw_image(xbuttons[selected],by,sbuttons[selected]).display(disp);
  57533. else canvas.display(disp);
  57534. }
  57535. refresh = false;
  57536. }
  57537. disp.wait(15);
  57538. if (disp.is_resized()) disp.resize(disp,false);
  57539. if (disp.button()&1) {
  57540. oclicked = clicked;
  57541. clicked = -1;
  57542. cimglist_for(buttons,l)
  57543. if (disp.mouse_y()>=(int)by && disp.mouse_y()<(int)(by + bh) &&
  57544. disp.mouse_x()>=(int)xbuttons[l] && disp.mouse_x()<(int)(xbuttons[l] + bw)) {
  57545. clicked = selected = l;
  57546. refresh = true;
  57547. }
  57548. if (clicked!=oclicked) refresh = true;
  57549. } else if (clicked>=0) stop_flag = true;
  57550. if (disp.key()) {
  57551. oselected = selected;
  57552. switch (disp.key()) {
  57553. case cimg::keyESC : selected = -1; stop_flag = true; break;
  57554. case cimg::keyENTER : if (selected<0) selected = 0; stop_flag = true; break;
  57555. case cimg::keyTAB :
  57556. case cimg::keyARROWRIGHT :
  57557. case cimg::keyARROWDOWN : selected = (selected + 1)%buttons.width(); break;
  57558. case cimg::keyARROWLEFT :
  57559. case cimg::keyARROWUP : selected = (selected + buttons.width() - 1)%buttons.width(); break;
  57560. }
  57561. disp.set_key();
  57562. if (selected!=oselected) refresh = true;
  57563. }
  57564. }
  57565. if (!disp) selected = -1;
  57566. return selected;
  57567. #endif
  57568. }
  57569. //! Display a simple dialog box, and wait for the user's response \specialization.
  57570. inline int dialog(const char *const title, const char *const msg,
  57571. const char *const button1_label, const char *const button2_label, const char *const button3_label,
  57572. const char *const button4_label, const char *const button5_label, const char *const button6_label,
  57573. const bool is_centered) {
  57574. return dialog(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label,
  57575. CImg<unsigned char>::_logo40x38(),is_centered);
  57576. }
  57577. //! Evaluate math expression.
  57578. /**
  57579. \param expression C-string describing the formula to evaluate.
  57580. \param x Value of the pre-defined variable \c x.
  57581. \param y Value of the pre-defined variable \c y.
  57582. \param z Value of the pre-defined variable \c z.
  57583. \param c Value of the pre-defined variable \c c.
  57584. \return Result of the formula evaluation.
  57585. \note Set \c expression to \c 0 to keep evaluating the last specified \c expression.
  57586. \par Example
  57587. \code
  57588. const double
  57589. res1 = cimg::eval("cos(x)^2 + sin(y)^2",2,2), // will return '1'.
  57590. res2 = cimg::eval(0,1,1); // will return '1' too.
  57591. \endcode
  57592. **/
  57593. inline double eval(const char *const expression, const double x, const double y, const double z, const double c) {
  57594. static const CImg<float> empty;
  57595. return empty.eval(expression,x,y,z,c);
  57596. }
  57597. template<typename t>
  57598. inline CImg<typename cimg::superset<double,t>::type> eval(const char *const expression, const CImg<t>& xyzc) {
  57599. static const CImg<float> empty;
  57600. return empty.eval(expression,xyzc);
  57601. }
  57602. // End of cimg:: namespace
  57603. }
  57604. // End of cimg_library:: namespace
  57605. }
  57606. //! Short alias name.
  57607. namespace cil = cimg_library_suffixed;
  57608. #ifdef _cimg_redefine_False
  57609. #define False 0
  57610. #endif
  57611. #ifdef _cimg_redefine_True
  57612. #define True 1
  57613. #endif
  57614. #ifdef _cimg_redefine_min
  57615. #define min(a,b) (((a)<(b))?(a):(b))
  57616. #endif
  57617. #ifdef _cimg_redefine_max
  57618. #define max(a,b) (((a)>(b))?(a):(b))
  57619. #endif
  57620. #ifdef _cimg_redefine_PI
  57621. #define PI 3.141592653589793238462643383
  57622. #endif
  57623. #ifdef _MSC_VER
  57624. #pragma warning(pop)
  57625. #endif
  57626. #endif
  57627. // Local Variables:
  57628. // mode: c++
  57629. // End: