Image Operations.cpp 120 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. namespace EE{
  4. #include "Import/BC.h"
  5. #include "Import/ETC.h"
  6. /******************************************************************************/
  7. static Bool Decompress(Image &image, IMAGE_TYPE &type, IMAGE_MODE &mode, Int &mip_maps) // returns if image exists
  8. {
  9. type =image.type ();
  10. mode =image.mode ();
  11. mip_maps=image.mipMaps();
  12. if(image.is())
  13. {
  14. if(image.compressed())return image.copyTry(image, -1, -1, -1, IMAGE_R8G8B8A8, image.cube() ? IMAGE_SOFT_CUBE : IMAGE_SOFT, 1);
  15. return true;
  16. }
  17. return false;
  18. }
  19. static void Compress(Image &image, IMAGE_TYPE type, IMAGE_MODE mode, Int mip_maps)
  20. {
  21. image.copyTry(image, -1, -1, -1, type, mode, mip_maps);
  22. }
  23. /******************************************************************************/
  24. Bool Image::extractMipMap(Image &dest, Int type, Int mode, Int mip_map, DIR_ENUM cube_face)C
  25. {
  26. Bool ok=false;
  27. Image temp;
  28. if(InRange(mip_map, mipMaps()))
  29. {
  30. if(type<=0 )type=T.type();
  31. if(mode< 0 )mode=T.mode();
  32. if(IsCube(IMAGE_MODE(mode)))mode=IMAGE_SOFT;
  33. if(temp.createTry(Max(1, w()>>mip_map), Max(1, h()>>mip_map), Max(1, d()>>mip_map), hwType(), IMAGE_SOFT, 1, false))
  34. if(temp.lock(LOCK_WRITE))
  35. {
  36. if(lockRead(mip_map, cube_face))
  37. {
  38. ok=true;
  39. Int blocks_y=Min(ImageBlocksY(hwW(), hwH(), mip_map, hwType()), ImageBlocksY(temp.hwW(), temp.hwH(), 0, temp.hwType()));
  40. REPD(z, temp.d())
  41. {
  42. C Byte *src = data() + z* pitch2();
  43. Byte *dest=temp.data() + z*temp.pitch2();
  44. REPD(y, blocks_y)CopyFast(dest + y*temp.pitch(), src + y*pitch(), Min(temp.pitch(), pitch()));
  45. }
  46. unlock();
  47. }
  48. temp.unlock();
  49. }
  50. if(ok)ok=temp.copyTry(temp, -1, -1, -1, type, mode, 1);
  51. }
  52. Swap(dest, temp);
  53. return ok;
  54. }
  55. /******************************************************************************/
  56. Bool Image::injectMipMap(C Image &src, Int mip_map, DIR_ENUM cube_face, FILTER_TYPE filter, Bool clamp, Bool mtrl_base_1)
  57. {
  58. Bool ok=false;
  59. if(InRange(mip_map, mipMaps()))
  60. {
  61. C Image *s=&src;
  62. Image temp;
  63. Int w=Max(1, T.w()>>mip_map), h=Max(1, T.h()>>mip_map), d=Max(1, T.d()>>mip_map);
  64. if(s->w()!=w || s->h()!=h || s->d()!=d || s->hwType()!=hwType() || s==this)
  65. if(s->copyTry(temp, w, h, d, hwType(), IMAGE_SOFT, 1, filter, clamp, false, false, mtrl_base_1, false))s=&temp;else return false; // resize to mip size, use target type
  66. if(lock(LOCK_WRITE, mip_map, cube_face))
  67. {
  68. if(s->lockRead())
  69. {
  70. ok=true;
  71. Int blocks_y=Min(ImageBlocksY(hwW(), hwH(), mip_map, hwType()), ImageBlocksY(s->hwW(), s->hwH(), 0, s->hwType()));
  72. REPD(z, Min(ld(), s->d()))
  73. {
  74. C Byte *src =s->data() + z*s->pitch2();
  75. Byte *dest= data() + z* pitch2();
  76. REPD(y, blocks_y)CopyFast(dest + y*pitch(), src + y*s->pitch(), Min(pitch(), s->pitch()));
  77. }
  78. s->unlock();
  79. }
  80. unlock();
  81. }
  82. }
  83. return ok;
  84. }
  85. /******************************************************************************/
  86. Image& Image::clear()
  87. {
  88. if(soft())Zero(_data_all, memUsage());else
  89. {
  90. Int faces=T.faces();
  91. REPD(m, mipMaps())
  92. REPD(f, faces)
  93. if(lock(LOCK_WRITE, m, DIR_ENUM(f)))
  94. {
  95. Zero(data(), ld()*pitch2());
  96. unlock();
  97. }
  98. }
  99. return T;
  100. }
  101. /******************************************************************************/
  102. Image& Image::normalize(Bool red, Bool green, Bool blue, Bool alpha, C BoxI *box)
  103. {
  104. if(red || green || blue || alpha)
  105. {
  106. IMAGE_TYPE type;
  107. IMAGE_MODE mode;
  108. Int mip_maps;
  109. if(Decompress(T, type, mode, mip_maps))
  110. {
  111. Vec4 min, max; if(stats(&min, &max, null, null, null, null, box))
  112. {
  113. Flt d; Vec4 mul, add;
  114. if(red && (d=max.x-min.x)){mul.x=1.0f/d; add.x=-min.x*mul.x;}else{mul.x=1; add.x=0;}
  115. if(green && (d=max.y-min.y)){mul.y=1.0f/d; add.y=-min.y*mul.y;}else{mul.y=1; add.y=0;}
  116. if(blue && (d=max.z-min.z)){mul.z=1.0f/d; add.z=-min.z*mul.z;}else{mul.z=1; add.z=0;}
  117. if(alpha && (d=max.w-min.w)){mul.w=1.0f/d; add.w=-min.w*mul.w;}else{mul.w=1; add.w=0;}
  118. mulAdd(mul, add, box);
  119. }
  120. Compress(T, type, mode, mip_maps);
  121. }
  122. }
  123. return T;
  124. }
  125. /******************************************************************************/
  126. Image& Image::mulAdd(C Vec4 &mul, C Vec4 &add, C BoxI *box)
  127. {
  128. if(mul!=Vec4(1) || add!=Vec4Zero)
  129. {
  130. IMAGE_TYPE type;
  131. IMAGE_MODE mode;
  132. Int mip_maps;
  133. if(Decompress(T, type, mode, mip_maps))
  134. {
  135. if(lock())
  136. {
  137. BoxI b(0, size3()); if(box)b&=*box;
  138. for(Int z=b.min.z; z<b.max.z; z++)
  139. for(Int y=b.min.y; y<b.max.y; y++)
  140. for(Int x=b.min.x; x<b.max.x; x++)color3DF(x, y, z, color3DF(x, y, z)*mul+add);
  141. unlock().updateMipMaps();
  142. }
  143. Compress(T, type, mode, mip_maps);
  144. }
  145. }
  146. return T;
  147. }
  148. /******************************************************************************/
  149. void Image::bumpToNormal(Image &dest, Flt scale, Bool high_precision)C
  150. {
  151. C Image *src=this;
  152. Image temp;
  153. if(compressed())if(copyTry(temp, -1, -1, 1, IMAGE_R8G8B8A8, IMAGE_SOFT, 1))src=&temp;else return;
  154. if(src->lockRead())
  155. {
  156. Image normal;
  157. if( normal.createTry(src->w(), src->h(), 1, high_precision ? IMAGE_F32_4 : IMAGE_R8G8B8A8, IMAGE_SOFT, 1) && normal.lock(LOCK_WRITE))
  158. {
  159. high_precision=(normal.hwType()==IMAGE_F32_4); // verify in case it was created as different type
  160. Bool src_hp=src->highPrecision(),
  161. src_1c=(ImageTI[src->type()].channels==1);
  162. if( !src_hp)scale/=(src_1c ? (1<<(8*src->bytePP()))-1 : 255);
  163. Flt z=2/scale;
  164. REPD(y, src->h())
  165. REPD(x, src->w())
  166. {
  167. Vec4 nrm_bump;
  168. if(src_hp)
  169. {
  170. nrm_bump.w=src->pixelF( x , y );
  171. Flt l=src->pixelF((x+src->w()-1)%src->w(), y ),
  172. r=src->pixelF((x+ 1)%src->w(), y ),
  173. u=src->pixelF( x , (y+src->h()-1)%src->h()),
  174. d=src->pixelF( x , (y+ 1)%src->h());
  175. nrm_bump.x=l-r;
  176. nrm_bump.y=u-d;
  177. }else
  178. if(src_1c)
  179. {
  180. nrm_bump.w=src->pixel( x , y )*scale;
  181. Int l=src->pixel((x+src->w()-1)%src->w(), y ),
  182. r=src->pixel((x+ 1)%src->w(), y ),
  183. u=src->pixel( x , (y+src->h()-1)%src->h()),
  184. d=src->pixel( x , (y+ 1)%src->h());
  185. nrm_bump.x=l-r;
  186. nrm_bump.y=u-d;
  187. }else
  188. {
  189. nrm_bump.w=src->color( x , y ).r*scale;
  190. Byte l=src->color((x+src->w()-1)%src->w(), y ).r,
  191. r=src->color((x+ 1)%src->w(), y ).r,
  192. u=src->color( x , (y+src->h()-1)%src->h()).r,
  193. d=src->color( x , (y+ 1)%src->h()).r;
  194. nrm_bump.x=l-r;
  195. nrm_bump.y=u-d;
  196. }
  197. #if 0
  198. nrm_bump.x*=scale;
  199. nrm_bump.y*=scale;
  200. nrm_bump.z =2;
  201. #else
  202. nrm_bump.z=z;
  203. #endif
  204. nrm_bump.xyz.normalize();
  205. if(high_precision)normal.pixF4(x, y)=nrm_bump;else
  206. {
  207. Color color;
  208. color.r= Round(nrm_bump.x*127)+128;
  209. color.g= Round(nrm_bump.y*127)+128;
  210. color.b= Round(nrm_bump.z*127)+128;
  211. color.a=FltToByte(nrm_bump.w);
  212. normal.color(x, y, color);
  213. }
  214. }
  215. src ->unlock();
  216. normal.unlock().updateMipMaps(FILTER_BEST, false); // normal maps are usually wrapped
  217. Swap(dest, normal);
  218. }else
  219. {
  220. src->unlock();
  221. }
  222. }
  223. }
  224. /******************************************************************************
  225. Image& Image::dither(IMAGE_TYPE type)
  226. {
  227. if(!compressed())switch(type)
  228. {
  229. case IMAGE_BC1:
  230. case IMAGE_BC2:
  231. case IMAGE_BC3: if(lock()) // "x<<1, y<<2" was tested as best combination (from x=0..3 and y=0..3, XOR was also tested instead of ADD but it made no difference)
  232. {
  233. if(hwType()==IMAGE_B8G8R8A8 || hwType()==IMAGE_R8G8B8A8)
  234. {
  235. REPD(y, T.h())
  236. REPD(x, T.w())
  237. {
  238. VecB4 &v=pixB4(x, y);
  239. Int d=(((x<<1)+(y<<2))&7)-4;
  240. v.x=Mid(v.x+ d , 0, 255); // red (8-bit original, 5-bit on DXT, 3-bit dither)
  241. v.y=Mid(v.y+(d>>1), 0, 255); // green (8-bit original, 6-bit on DXT, 2-bit dither)
  242. v.z=Mid(v.z+ d , 0, 255); // blue (8-bit original, 5-bit on DXT, 3-bit dither)
  243. }
  244. }else
  245. {
  246. REPD(y, T.h())
  247. REPD(x, T.w())
  248. {
  249. Color c=color(x, y);
  250. Int d=(((x<<1)+(y<<2))&7)-4;
  251. c.r=Mid(c.r+ d , 0, 255); // red (8-bit original, 5-bit on DXT, 3-bit dither)
  252. c.g=Mid(c.g+(d>>1), 0, 255); // green (8-bit original, 6-bit on DXT, 2-bit dither)
  253. c.b=Mid(c.b+ d , 0, 255); // blue (8-bit original, 5-bit on DXT, 3-bit dither)
  254. color(x, y, c);
  255. }
  256. }
  257. unlock();
  258. }break;
  259. case IMAGE_ETC1: if(lock()) // ETC has 4/4/4 bits or 5/5/5 bits per pixel (set 3-bit dither because 4 is visually too big, set smaller dither for green because it has bigger perceptual value in ETC encoder)
  260. {
  261. if(hwType()==IMAGE_B8G8R8A8 || hwType()==IMAGE_R8G8B8A8)
  262. {
  263. REPD(y, T.h())
  264. REPD(x, T.w())
  265. {
  266. VecB4 &v=pixB4(x, y);
  267. Int d=(((x<<1)+(y<<2))&7)-4;
  268. v.x=Mid(v.x+ d , 0, 255);
  269. v.y=Mid(v.y+(d>>1), 0, 255);
  270. v.z=Mid(v.z+ d , 0, 255);
  271. }
  272. }else
  273. {
  274. REPD(y, T.h())
  275. REPD(x, T.w())
  276. {
  277. Color c=color(x, y);
  278. Int d=(((x<<1)+(y<<2))&7)-4;
  279. c.r=Mid(c.r+ d , 0, 255);
  280. c.g=Mid(c.g+(d>>1), 0, 255);
  281. c.b=Mid(c.b+ d , 0, 255);
  282. color(x, y, c);
  283. }
  284. }
  285. unlock();
  286. }break;
  287. case IMAGE_PVRTC1_2:
  288. case IMAGE_PVRTC1_4: if(lock()) // 2-bit dithering gave best results
  289. {
  290. if(hwType()==IMAGE_B8G8R8A8 || hwType()==IMAGE_R8G8B8A8)
  291. {
  292. REPD(y, T.h())
  293. REPD(x, T.w())
  294. {
  295. VecB4 &v=pixB4(x, y);
  296. Int d=(((x<<0)+(y<<1))&3)-2;
  297. v.x=Mid(v.x+d, 0, 255);
  298. v.y=Mid(v.y+d, 0, 255);
  299. v.z=Mid(v.z+d, 0, 255);
  300. }
  301. }else
  302. {
  303. REPD(y, T.h())
  304. REPD(x, T.w())
  305. {
  306. Color c=color(x, y);
  307. Int d=(((x<<0)+(y<<1))&3)-2;
  308. c.r=Mid(c.r+d, 0, 255);
  309. c.g=Mid(c.g+d, 0, 255);
  310. c.b=Mid(c.b+d, 0, 255);
  311. color(x, y, c);
  312. }
  313. }
  314. unlock();
  315. }break;
  316. }
  317. return T;
  318. }
  319. /******************************************************************************/
  320. Image& Image::fastCrop(Int w, Int h, Int d)
  321. {
  322. if(is() && mipMaps()==1)
  323. {
  324. _size.x=Min(Max(1, w), hwW());
  325. _size.y=Min(Max(1, h), hwH());
  326. _size.z=Min(Max(1, d), hwD());
  327. if(soft())_lock_size=_size;
  328. setPartial();
  329. }
  330. return T;
  331. }
  332. /******************************************************************************/
  333. void Image::crop(Image &dest, Int x, Int y, Int w, Int h)C
  334. {
  335. crop3D(dest, x, y, 0, w, h, d());
  336. }
  337. void Image::crop3D(Image &dest, Int x, Int y, Int z, Int w, Int h, Int d)C
  338. {
  339. if(!is() || w<=0 || h<=0 || d<=0){dest.del(); return;}
  340. if(&dest==this && x==0 && y==0 && z==0 && w==T.w() && h==T.h() && d==T.d())return; // no change needed
  341. C Image *src=this;
  342. Image temp;
  343. if(compressed())if(copyTry(temp, -1, -1, -1, IMAGE_R8G8B8A8, IMAGE_SOFT, 1))src=&temp;else return;
  344. if(src->lockRead())
  345. {
  346. Int mip_maps=((mipMaps()>1) ? 0 : 1);
  347. Image temp;
  348. if( temp.createTry(w, h, d, src->type(), src->mode(), mip_maps) && temp.lock(LOCK_WRITE))
  349. {
  350. if(src->bytePP()<=4)
  351. {
  352. REPD(sz, d)
  353. REPD(sy, h)
  354. REPD(sx, w)temp.pixel3D(sx, sy, sz, src->pixel3D(x+sx, y+sy, z+sz));
  355. }else
  356. if(ImageTI[src->type()].channels<=1)
  357. {
  358. REPD(sz, d)
  359. REPD(sy, h)
  360. REPD(sx, w)temp.pixel3DF(sx, sy, sz, src->pixel3DF(x+sx, y+sy, z+sz));
  361. }else
  362. {
  363. REPD(sz, d)
  364. REPD(sy, h)
  365. REPD(sx, w)temp.color3DF(sx, sy, sz, src->color3DF(x+sx, y+sy, z+sz));
  366. }
  367. temp.unlock().updateMipMaps();
  368. temp.copyTry(temp, -1, -1, -1, T.type(), T.mode(), mip_maps);
  369. Swap(dest, temp);
  370. }
  371. src->unlock();
  372. }
  373. }
  374. /******************************************************************************/
  375. Image& Image::resize(Int w, Int h, FILTER_TYPE filter, Bool clamp, Bool alpha_weight, Bool keep_edges)
  376. {
  377. MAX(w, 1);
  378. MAX(h, 1);
  379. if(is() && (w!=T.w() || h!=T.h()))copyTry(T, w, h, -1, -1, -1, -1, filter, clamp, alpha_weight, keep_edges);
  380. return T;
  381. }
  382. /******************************************************************************/
  383. Image& Image::resize3D(Int w, Int h, Int d, FILTER_TYPE filter, Bool clamp, Bool alpha_weight, Bool keep_edges)
  384. {
  385. MAX(w, 1);
  386. MAX(h, 1);
  387. MAX(d, 1);
  388. if(is() && (w!=T.w() || h!=T.h() || d!=T.d()))copyTry(T, w, h, d, -1, -1, -1, filter, clamp, alpha_weight, keep_edges);
  389. return T;
  390. }
  391. /******************************************************************************/
  392. // MIRROR
  393. /******************************************************************************/
  394. Image& Image::mirrorX()
  395. {
  396. if(is())
  397. {
  398. C Image *src=this;
  399. Image temp;
  400. if(compressed())
  401. if(copyTry(temp, -1, -1, -1, IMAGE_R8G8B8A8, IMAGE_SOFT, 1))src=&temp;else return T;
  402. if(src->lockRead())
  403. {
  404. Bool ok=false;
  405. Image mirror; if(mirror.createTry(src->w(), src->h(), src->d(), src->type(), src->mode(), src->mipMaps()))
  406. if(mirror.lock(LOCK_WRITE))
  407. {
  408. if(mirror.highPrecision())
  409. {
  410. REPD(z, mirror.d())
  411. REPD(y, mirror.h())
  412. REPD(x, mirror.w())mirror.color3DF(x, y, z, src->color3DF(mirror.w()-1-x, y, z));
  413. }else
  414. {
  415. REPD(z, mirror.d())
  416. REPD(y, mirror.h())
  417. REPD(x, mirror.w())mirror.color3D(x, y, z, src->color3D(mirror.w()-1-x, y, z));
  418. }
  419. mirror.unlock();
  420. ok=mirror.copyTry(mirror, w(), h(), d(), type(), mode(), mipMaps());
  421. }
  422. src->unlock();
  423. if(ok)Swap(T, mirror.updateMipMaps());
  424. }
  425. }
  426. return T;
  427. }
  428. /******************************************************************************/
  429. Image& Image::mirrorY()
  430. {
  431. if(is())
  432. {
  433. C Image *src=this;
  434. Image temp;
  435. if(compressed())
  436. if(copyTry(temp, -1, -1, -1, IMAGE_R8G8B8A8, IMAGE_SOFT, 1))src=&temp;else return T;
  437. if(src->lockRead())
  438. {
  439. Bool ok=false;
  440. Image mirror; if(mirror.createTry(src->w(), src->h(), src->d(), src->type(), src->mode(), src->mipMaps()))
  441. if(mirror.lock(LOCK_WRITE))
  442. {
  443. if(mirror.highPrecision())
  444. {
  445. REPD(z, mirror.d())
  446. REPD(y, mirror.h())
  447. REPD(x, mirror.w())mirror.color3DF(x, y, z, src->color3DF(x, mirror.h()-1-y, z));
  448. }else
  449. {
  450. REPD(z, mirror.d())
  451. REPD(y, mirror.h())
  452. REPD(x, mirror.w())mirror.color3D(x, y, z, src->color3D(x, mirror.h()-1-y, z));
  453. }
  454. mirror.unlock();
  455. ok=mirror.copyTry(mirror, w(), h(), d(), type(), mode(), mipMaps());
  456. }
  457. src->unlock();
  458. if(ok)Swap(T, mirror.updateMipMaps());
  459. }
  460. }
  461. return T;
  462. }
  463. /******************************************************************************/
  464. // ALPHA
  465. /******************************************************************************/
  466. Image& Image::alphaFromKey(C Color &key)
  467. {
  468. IMAGE_TYPE type;
  469. IMAGE_MODE mode;
  470. Int mip_maps;
  471. if(Decompress(T, type, mode, mip_maps))
  472. {
  473. if(!ImageTI[type].a){copy(T, -1, -1, -1, IMAGE_R8G8B8A8, mode, mip_maps); type=T.type();} // if image doesn't have alpha channel then convert to R8G8B8A8, set 'type' to value after conversion so it won't be converted back
  474. if(lock())
  475. {
  476. REPD(z, T.d())
  477. REPD(y, T.h())
  478. REPD(x, T.w()){Color c=color3D(x, y, z); color3D(x, y, z, (c==key) ? TRANSPARENT : Color(c.r, c.g, c.b));}
  479. unlock().updateMipMaps();
  480. Compress(T, type, mode, mip_maps);
  481. }
  482. }
  483. return T;
  484. }
  485. /******************************************************************************/
  486. Image& Image::alphaFromBrightness()
  487. {
  488. IMAGE_TYPE type;
  489. IMAGE_MODE mode;
  490. Int mip_maps;
  491. if(Decompress(T, type, mode, mip_maps))
  492. {
  493. if(!ImageTI[T.type()].a){copy(T, -1, -1, -1, ImageTypeIncludeAlpha(T.type()), mode, mip_maps); type=T.type();} // if image doesn't have alpha channel then include it, set 'type' to value after conversion so it won't be converted back
  494. if(lock())
  495. {
  496. if(highPrecision())
  497. {
  498. REPD(z, T.d())
  499. REPD(y, T.h())
  500. REPD(x, T.w()){Vec4 c=color3DF(x, y, z); c.w=c.xyz.max(); color3DF(x, y, z, c);}
  501. }else
  502. {
  503. REPD(z, T.d())
  504. REPD(y, T.h())
  505. REPD(x, T.w()){Color c=color3D(x, y, z); c.a=c.lum(); color3D(x, y, z, c);}
  506. }
  507. unlock().updateMipMaps();
  508. Compress(T, type, mode, mip_maps);
  509. }
  510. }
  511. return T;
  512. }
  513. /******************************************************************************/
  514. Image& Image::divRgbByAlpha()
  515. {
  516. if(ImageTI[type()].a)
  517. {
  518. IMAGE_TYPE type;
  519. IMAGE_MODE mode;
  520. Int mip_maps;
  521. if(Decompress(T, type, mode, mip_maps) && lock())
  522. {
  523. if(highPrecision())
  524. {
  525. REPD(z, T.d())
  526. REPD(y, T.h())
  527. REPD(x, T.w()){Vec4 c=color3DF(x, y, z); if(c.w){c.xyz/=c.w; color3DF(x, y, z, c);}}
  528. }else
  529. {
  530. REPD(z, T.d())
  531. REPD(y, T.h())
  532. REPD(x, T.w()){Color c=color3D(x, y, z); if(c.a){c.r=Min(c.r*255/c.a, 255); c.g=Min(c.g*255/c.a, 255); c.b=Min(c.b*255/c.a, 255); color3D(x, y, z, c);}}
  533. }
  534. unlock().updateMipMaps();
  535. Compress(T, type, mode, mip_maps);
  536. }
  537. }
  538. return T;
  539. }
  540. /******************************************************************************/
  541. // DOWNSAMPLE
  542. /******************************************************************************/
  543. Image& Image::downSample(FILTER_TYPE filter, Bool clamp, Bool alpha_weight)
  544. {
  545. if(w()>1 || h()>1 || d()>1)copyTry(T, Max(1, w()>>1), Max(1, h()>>1), Max(1, d()>>1), -1, -1, -1, filter, clamp, alpha_weight);
  546. return T;
  547. }
  548. /******************************************************************************
  549. Image& Image::downSampleNormal()
  550. {
  551. if((w()>1 || h()>1) && d()==1)
  552. {
  553. IMAGE_TYPE type;
  554. IMAGE_MODE mode;
  555. Int mip_maps;
  556. if(Decompress(T, type, mode, mip_maps)) // try to preserve number of mip-maps
  557. {
  558. if(lockRead())
  559. {
  560. Image temp(Max(1, w()>>1), Max(1, h()>>1), Max(1, d()>>1), T.type(), T.mode(), ImageTI[type].compressed ? 1 : mip_maps);
  561. if( temp.lock(LOCK_WRITE))
  562. {
  563. REPD(y, temp.h())
  564. REPD(x, temp.w())
  565. {
  566. UInt x2=x*2,
  567. y2=y*2;
  568. Color lu=color(x2+0, y2+0),
  569. ru=color(x2+1, y2+0),
  570. ld=color(x2+0, y2+1),
  571. rd=color(x2+1, y2+1);
  572. Vec nrm;
  573. nrm.x = lu.r+ru.r+ld.r+rd.r - 4*128;
  574. nrm.y = lu.g+ru.g+ld.g+rd.g - 4*128;
  575. nrm.z = lu.b+ru.b+ld.b+rd.b - 4*128;
  576. nrm.normalize();
  577. Color color;
  578. color.r=Round(nrm.x*127+128);
  579. color.g=Round(nrm.y*127+128);
  580. color.b=Round(nrm.z*127+128);
  581. color.a=((lu.a+ru.a+ld.a+rd.a+2)>>2);
  582. temp.color(x, y, color);
  583. }
  584. Swap(unlock(), temp.unlock().updateMipMaps());
  585. Compress(T, type, mode, mip_maps);
  586. }else
  587. {
  588. unlock();
  589. }
  590. }
  591. }
  592. }
  593. return T;
  594. }
  595. /******************************************************************************/
  596. // BLUR
  597. /******************************************************************************/
  598. struct BlurContext
  599. {
  600. typedef void(BlurContext::*Blur)(Int x, Int y, Int z)C; // pointer to BlurContext method
  601. Bool high_prec, clamp;
  602. Byte func;
  603. Int rangei, rangei_2_1;
  604. Flt range;
  605. VecI size;
  606. C Image *src;
  607. Image *dest;
  608. Threads *threads;
  609. MemtN<Byte, 256> weight_b;
  610. MemtN<Flt , 256> weight_f;
  611. Blur blur_ptr;
  612. BlurContext(C Image &src, Bool clamp, Threads *threads)
  613. {
  614. if(T.high_prec=src.highPrecision())
  615. {
  616. if(src.hwType()==IMAGE_F32 )func=0;else // HP 1F, here check 'hwType' because we will access memory directly using 'pixF' method
  617. if(ImageTI[src.type()].channels==1)func=1;else // HP 1C
  618. func=2; // HP MC
  619. }else
  620. {
  621. C ImageTypeInfo &ti=ImageTI[src.hwType()]; // here check 'hwType' because we will access memory directly using 'pixB' method
  622. if(ti.bit_pp==8 && ti.channels==1)func=3; // LP 1byte
  623. else func=4; // LP MC
  624. }
  625. T.clamp=clamp; T.size=src.size3(); T.rangei=0; T.rangei_2_1=1; T.range=0; T.threads=threads; T.src=&src;
  626. }
  627. void setRange(Flt range) // this is used for blur
  628. {
  629. MAX(range, 0); if(T.range!=range)
  630. {
  631. T.range = range;
  632. T.rangei =Ceil(range);
  633. T.rangei_2_1=rangei*2+1;
  634. Flt range_1=range+1;
  635. if(high_prec){weight_f.setNum(rangei_2_1); REPAO(weight_f)= BlendSmoothCube(Flt(i-rangei)/range_1) ;}
  636. else {weight_b.setNum(rangei_2_1); REPAO(weight_b)=RoundU(255*BlendSmoothCube(Flt(i-rangei)/range_1));}
  637. }
  638. }
  639. void setRange(Int range) // this is used for average
  640. {
  641. T.rangei=range;
  642. T.rangei_2_1=rangei*2+1;
  643. }
  644. void setX()
  645. {
  646. switch(func)
  647. {
  648. case 0: blur_ptr=clamp ? &BlurContext::blur_X_HP_1F_CLAMP : &BlurContext::blur_X_HP_1F_WRAP; break;
  649. case 1: blur_ptr=clamp ? &BlurContext::blur_X_HP_1C_CLAMP : &BlurContext::blur_X_HP_1C_WRAP; break;
  650. case 2: blur_ptr=clamp ? &BlurContext::blur_X_HP_MC_CLAMP : &BlurContext::blur_X_HP_MC_WRAP; break;
  651. case 3: blur_ptr=clamp ? &BlurContext::blur_X_LP_1B_CLAMP : &BlurContext::blur_X_LP_1B_WRAP; break;
  652. case 4: blur_ptr=clamp ? &BlurContext::blur_X_LP_MC_CLAMP : &BlurContext::blur_X_LP_MC_WRAP; break;
  653. }
  654. }
  655. void setY()
  656. {
  657. switch(func)
  658. {
  659. case 0: blur_ptr=clamp ? &BlurContext::blur_Y_HP_1F_CLAMP : &BlurContext::blur_Y_HP_1F_WRAP; break;
  660. case 1: blur_ptr=clamp ? &BlurContext::blur_Y_HP_1C_CLAMP : &BlurContext::blur_Y_HP_1C_WRAP; break;
  661. case 2: blur_ptr=clamp ? &BlurContext::blur_Y_HP_MC_CLAMP : &BlurContext::blur_Y_HP_MC_WRAP; break;
  662. case 3: blur_ptr=clamp ? &BlurContext::blur_Y_LP_1B_CLAMP : &BlurContext::blur_Y_LP_1B_WRAP; break;
  663. case 4: blur_ptr=clamp ? &BlurContext::blur_Y_LP_MC_CLAMP : &BlurContext::blur_Y_LP_MC_WRAP; break;
  664. }
  665. }
  666. void setZ()
  667. {
  668. switch(func)
  669. {
  670. case 0: blur_ptr=clamp ? &BlurContext::blur_Z_HP_1F_CLAMP : &BlurContext::blur_Z_HP_1F_WRAP; break;
  671. case 1: blur_ptr=clamp ? &BlurContext::blur_Z_HP_1C_CLAMP : &BlurContext::blur_Z_HP_1C_WRAP; break;
  672. case 2: blur_ptr=clamp ? &BlurContext::blur_Z_HP_MC_CLAMP : &BlurContext::blur_Z_HP_MC_WRAP; break;
  673. case 3: blur_ptr=clamp ? &BlurContext::blur_Z_LP_1B_CLAMP : &BlurContext::blur_Z_LP_1B_WRAP; break;
  674. case 4: blur_ptr=clamp ? &BlurContext::blur_Z_LP_MC_CLAMP : &BlurContext::blur_Z_LP_MC_WRAP; break;
  675. }
  676. }
  677. void setAvgX()
  678. {
  679. switch(func)
  680. {
  681. case 0: blur_ptr=clamp ? &BlurContext::avg_X_HP_1F_CLAMP : &BlurContext::avg_X_HP_1F_WRAP; break;
  682. case 1: blur_ptr=clamp ? &BlurContext::avg_X_HP_1C_CLAMP : &BlurContext::avg_X_HP_1C_WRAP; break;
  683. case 2: blur_ptr=clamp ? &BlurContext::avg_X_HP_MC_CLAMP : &BlurContext::avg_X_HP_MC_WRAP; break;
  684. case 3: blur_ptr=clamp ? &BlurContext::avg_X_LP_1B_CLAMP : &BlurContext::avg_X_LP_1B_WRAP; break;
  685. case 4: blur_ptr=clamp ? &BlurContext::avg_X_LP_MC_CLAMP : &BlurContext::avg_X_LP_MC_WRAP; break;
  686. }
  687. }
  688. void setAvgY()
  689. {
  690. switch(func)
  691. {
  692. case 0: blur_ptr=clamp ? &BlurContext::avg_Y_HP_1F_CLAMP : &BlurContext::avg_Y_HP_1F_WRAP; break;
  693. case 1: blur_ptr=clamp ? &BlurContext::avg_Y_HP_1C_CLAMP : &BlurContext::avg_Y_HP_1C_WRAP; break;
  694. case 2: blur_ptr=clamp ? &BlurContext::avg_Y_HP_MC_CLAMP : &BlurContext::avg_Y_HP_MC_WRAP; break;
  695. case 3: blur_ptr=clamp ? &BlurContext::avg_Y_LP_1B_CLAMP : &BlurContext::avg_Y_LP_1B_WRAP; break;
  696. case 4: blur_ptr=clamp ? &BlurContext::avg_Y_LP_MC_CLAMP : &BlurContext::avg_Y_LP_MC_WRAP; break;
  697. }
  698. }
  699. void setAvgZ()
  700. {
  701. switch(func)
  702. {
  703. case 0: blur_ptr=clamp ? &BlurContext::avg_Z_HP_1F_CLAMP : &BlurContext::avg_Z_HP_1F_WRAP; break;
  704. case 1: blur_ptr=clamp ? &BlurContext::avg_Z_HP_1C_CLAMP : &BlurContext::avg_Z_HP_1C_WRAP; break;
  705. case 2: blur_ptr=clamp ? &BlurContext::avg_Z_HP_MC_CLAMP : &BlurContext::avg_Z_HP_MC_WRAP; break;
  706. case 3: blur_ptr=clamp ? &BlurContext::avg_Z_LP_1B_CLAMP : &BlurContext::avg_Z_LP_1B_WRAP; break;
  707. case 4: blur_ptr=clamp ? &BlurContext::avg_Z_LP_MC_CLAMP : &BlurContext::avg_Z_LP_MC_WRAP; break;
  708. }
  709. }
  710. inline void blur(Int x, Int y, Int z)C {(T.*blur_ptr)(x, y, z);}
  711. static void BlurFunc(IntPtr elm_index, BlurContext &bc, Int thread_index)
  712. {
  713. Int z=Unsigned(elm_index)/Unsigned(bc.size.y),
  714. y=Unsigned(elm_index)%Unsigned(bc.size.y);
  715. REPD(x, bc.size.x)bc.blur(x, y, z);
  716. }
  717. void blur()
  718. {
  719. if(threads)threads->process1(size.z*size.y, BlurFunc, T);else
  720. REPD(z, size.z)
  721. REPD(y, size.y)
  722. REPD(x, size.x)blur(x, y, z);
  723. }
  724. // X
  725. void blur_X_HP_1F_CLAMP(Int x, Int y, Int z)C
  726. {
  727. Flt color=0, power=0;
  728. Int i=0, min=x-rangei, max=Min(x+rangei, size.x-1); if(min<0){i=-min; min=0;}
  729. C Flt *data=&src->pixF(0, y, z);
  730. for(; min<=max; min++, i++)
  731. {
  732. Flt c=data[min], w=weight_f[i];
  733. color+=w*c;
  734. power+=w;
  735. }
  736. if(power)color/=power;
  737. dest->pixF(x, y, z)=color;
  738. }
  739. void blur_X_HP_1F_WRAP(Int x, Int y, Int z)C
  740. {
  741. Flt color=0, power=0;
  742. C Flt *data=&src->pixF(0, y, z);
  743. REP(rangei_2_1)
  744. {
  745. Flt c=data[Mod(x+i-rangei, size.x)], w=weight_f[i];
  746. color+=w*c;
  747. power+=w;
  748. }
  749. if(power)color/=power;
  750. dest->pixF(x, y, z)=color;
  751. }
  752. void blur_X_HP_1C_CLAMP(Int x, Int y, Int z)C
  753. {
  754. Flt color=0, power=0;
  755. Int i=0, min=x-rangei, max=Min(x+rangei, size.x-1); if(min<0){i=-min; min=0;}
  756. for(; min<=max; min++, i++)
  757. {
  758. Flt c=src->pixel3DF(min, y, z), w=weight_f[i];
  759. color+=w*c;
  760. power+=w;
  761. }
  762. if(power)color/=power;
  763. dest->pixel3DF(x, y, z, color);
  764. }
  765. void blur_X_HP_1C_WRAP(Int x, Int y, Int z)C
  766. {
  767. Flt color=0, power=0;
  768. REP(rangei_2_1)
  769. {
  770. Flt c=src->pixel3DF(Mod(x+i-rangei, size.x), y, z), w=weight_f[i];
  771. color+=w*c;
  772. power+=w;
  773. }
  774. if(power)color/=power;
  775. dest->pixel3DF(x, y, z, color);
  776. }
  777. void blur_X_HP_MC_CLAMP(Int x, Int y, Int z)C
  778. {
  779. Vec4 color=0;
  780. Flt power=0;
  781. Int i=0, min=x-rangei, max=Min(x+rangei, size.x-1); if(min<0){i=-min; min=0;}
  782. for(; min<=max; min++, i++)
  783. {
  784. Vec4 c=src->color3DF(min, y, z);
  785. Flt w=weight_f[i];
  786. color+=w*c;
  787. power+=w;
  788. }
  789. if(power)color/=power;
  790. dest->color3DF(x, y, z, color);
  791. }
  792. void blur_X_HP_MC_WRAP(Int x, Int y, Int z)C
  793. {
  794. Vec4 color=0;
  795. Flt power=0;
  796. REP(rangei_2_1)
  797. {
  798. Vec4 c=src->color3DF(Mod(x+i-rangei, size.x), y, z);
  799. Flt w=weight_f[i];
  800. color+=w*c;
  801. power+=w;
  802. }
  803. if(power)color/=power;
  804. dest->color3DF(x, y, z, color);
  805. }
  806. void blur_X_LP_1B_CLAMP(Int x, Int y, Int z)C
  807. {
  808. UInt value=0, power=0;
  809. Int i=0, min=x-rangei, max=Min(x+rangei, size.x-1); if(min<0){i=-min; min=0;}
  810. C Byte *data=&src->pixB(0, y, z);
  811. for(; min<=max; min++, i++)
  812. {
  813. Byte c=data[min], w=weight_b[i];
  814. value+=w*c;
  815. power+=w;
  816. }
  817. if(power){UInt p_2=(power>>1); value=(value+p_2)/power;}
  818. dest->pixB(x, y, z)=value;
  819. }
  820. void blur_X_LP_1B_WRAP(Int x, Int y, Int z)C
  821. {
  822. UInt value=0, power=0;
  823. C Byte *data=&src->pixB(0, y, z);
  824. REP(rangei_2_1)
  825. {
  826. Byte c=data[Mod(x+i-rangei, size.x)], w=weight_b[i];
  827. value+=w*c;
  828. power+=w;
  829. }
  830. if(power){UInt p_2=(power>>1); value=(value+p_2)/power;}
  831. dest->pixB(x, y, z)=value;
  832. }
  833. void blur_X_LP_MC_CLAMP(Int x, Int y, Int z)C
  834. {
  835. UInt r=0, g=0, b=0, a=0, power=0;
  836. Int i=0, min=x-rangei, max=Min(x+rangei, size.x-1); if(min<0){i=-min; min=0;}
  837. for(; min<=max; min++, i++)
  838. {
  839. Color c=src->color3D(min, y, z);
  840. Byte w=weight_b[i];
  841. r+=c.r*w; g+=c.g*w; b+=c.b*w; a+=c.a*w;
  842. power+=w;
  843. }
  844. if(power){UInt p_2=(power>>1); r=(r+p_2)/power; g=(g+p_2)/power; b=(b+p_2)/power; a=(a+p_2)/power;}
  845. dest->color3D(x, y, z, Color(r, g, b, a));
  846. }
  847. void blur_X_LP_MC_WRAP(Int x, Int y, Int z)C
  848. {
  849. UInt r=0, g=0, b=0, a=0, power=0;
  850. REP(rangei_2_1)
  851. {
  852. Color c=src->color3D(Mod(x+i-rangei, size.x), y, z);
  853. Byte w=weight_b[i];
  854. r+=c.r*w; g+=c.g*w; b+=c.b*w; a+=c.a*w;
  855. power+=w;
  856. }
  857. if(power){UInt p_2=(power>>1); r=(r+p_2)/power; g=(g+p_2)/power; b=(b+p_2)/power; a=(a+p_2)/power;}
  858. dest->color3D(x, y, z, Color(r, g, b, a));
  859. }
  860. // Y
  861. void blur_Y_HP_1F_CLAMP(Int x, Int y, Int z)C
  862. {
  863. Flt color=0, power=0;
  864. Int i=0, min=y-rangei, max=Min(y+rangei, size.y-1); if(min<0){i=-min; min=0;}
  865. UInt pitch=src->pitch();
  866. C Flt *data=&src->pixF(x, 0, z);
  867. for(; min<=max; min++, i++)
  868. {
  869. Flt c=*(Flt*)((Byte*)data+min*pitch), w=weight_f[i];
  870. color+=w*c;
  871. power+=w;
  872. }
  873. if(power)color/=power;
  874. dest->pixF(x, y, z)=color;
  875. }
  876. void blur_Y_HP_1F_WRAP(Int x, Int y, Int z)C
  877. {
  878. Flt color=0, power=0;
  879. UInt pitch=src->pitch();
  880. C Flt *data=&src->pixF(x, 0, z);
  881. REP(rangei_2_1)
  882. {
  883. Flt c=*(Flt*)((Byte*)data+Mod(y+i-rangei, size.y)*pitch), w=weight_f[i];
  884. color+=w*c;
  885. power+=w;
  886. }
  887. if(power)color/=power;
  888. dest->pixF(x, y, z)=color;
  889. }
  890. void blur_Y_HP_1C_CLAMP(Int x, Int y, Int z)C
  891. {
  892. Flt color=0, power=0;
  893. Int i=0, min=y-rangei, max=Min(y+rangei, size.y-1); if(min<0){i=-min; min=0;}
  894. for(; min<=max; min++, i++)
  895. {
  896. Flt c=src->pixel3DF(x, min, z), w=weight_f[i];
  897. color+=w*c;
  898. power+=w;
  899. }
  900. if(power)color/=power;
  901. dest->pixel3DF(x, y, z, color);
  902. }
  903. void blur_Y_HP_1C_WRAP(Int x, Int y, Int z)C
  904. {
  905. Flt color=0, power=0;
  906. REP(rangei_2_1)
  907. {
  908. Flt c=src->pixel3DF(x, Mod(y+i-rangei, size.y), z), w=weight_f[i];
  909. color+=w*c;
  910. power+=w;
  911. }
  912. if(power)color/=power;
  913. dest->pixel3DF(x, y, z, color);
  914. }
  915. void blur_Y_HP_MC_CLAMP(Int x, Int y, Int z)C
  916. {
  917. Vec4 color=0;
  918. Flt power=0;
  919. Int i=0, min=y-rangei, max=Min(y+rangei, size.y-1); if(min<0){i=-min; min=0;}
  920. for(; min<=max; min++, i++)
  921. {
  922. Vec4 c=src->color3DF(x, min, z);
  923. Flt w=weight_f[i];
  924. color+=w*c;
  925. power+=w;
  926. }
  927. if(power)color/=power;
  928. dest->color3DF(x, y, z, color);
  929. }
  930. void blur_Y_HP_MC_WRAP(Int x, Int y, Int z)C
  931. {
  932. Vec4 color=0;
  933. Flt power=0;
  934. REP(rangei_2_1)
  935. {
  936. Vec4 c=src->color3DF(x, Mod(y+i-rangei, size.y), z);
  937. Flt w=weight_f[i];
  938. color+=w*c;
  939. power+=w;
  940. }
  941. if(power)color/=power;
  942. dest->color3DF(x, y, z, color);
  943. }
  944. void blur_Y_LP_1B_CLAMP(Int x, Int y, Int z)C
  945. {
  946. UInt value=0, power=0, pitch=src->pitch();
  947. Int i=0, min=y-rangei, max=Min(y+rangei, size.y-1); if(min<0){i=-min; min=0;}
  948. C Byte *data=&src->pixB(x, 0, z);
  949. for(; min<=max; min++, i++)
  950. {
  951. Byte c=data[min*pitch], w=weight_b[i];
  952. value+=w*c;
  953. power+=w;
  954. }
  955. if(power){UInt p_2=(power>>1); value=(value+p_2)/power;}
  956. dest->pixB(x, y, z)=value;
  957. }
  958. void blur_Y_LP_1B_WRAP(Int x, Int y, Int z)C
  959. {
  960. UInt value=0, power=0, pitch=src->pitch();
  961. C Byte *data=&src->pixB(x, 0, z);
  962. REP(rangei_2_1)
  963. {
  964. Byte c=data[Mod(y+i-rangei, size.y)*pitch], w=weight_b[i];
  965. value+=w*c;
  966. power+=w;
  967. }
  968. if(power){UInt p_2=(power>>1); value=(value+p_2)/power;}
  969. dest->pixB(x, y, z)=value;
  970. }
  971. void blur_Y_LP_MC_CLAMP(Int x, Int y, Int z)C
  972. {
  973. UInt r=0, g=0, b=0, a=0, power=0;
  974. Int i=0, min=y-rangei, max=Min(y+rangei, size.y-1); if(min<0){i=-min; min=0;}
  975. for(; min<=max; min++, i++)
  976. {
  977. Color c=src->color3D(x, min, z);
  978. Byte w=weight_b[i];
  979. r+=c.r*w; g+=c.g*w; b+=c.b*w; a+=c.a*w;
  980. power+=w;
  981. }
  982. if(power){UInt p_2=(power>>1); r=(r+p_2)/power; g=(g+p_2)/power; b=(b+p_2)/power; a=(a+p_2)/power;}
  983. dest->color3D(x, y, z, Color(r, g, b, a));
  984. }
  985. void blur_Y_LP_MC_WRAP(Int x, Int y, Int z)C
  986. {
  987. UInt r=0, g=0, b=0, a=0, power=0;
  988. REP(rangei_2_1)
  989. {
  990. Color c=src->color3D(x, Mod(y+i-rangei, size.y), z);
  991. Byte w=weight_b[i];
  992. r+=c.r*w; g+=c.g*w; b+=c.b*w; a+=c.a*w;
  993. power+=w;
  994. }
  995. if(power){UInt p_2=(power>>1); r=(r+p_2)/power; g=(g+p_2)/power; b=(b+p_2)/power; a=(a+p_2)/power;}
  996. dest->color3D(x, y, z, Color(r, g, b, a));
  997. }
  998. // Z
  999. void blur_Z_HP_1F_CLAMP(Int x, Int y, Int z)C
  1000. {
  1001. Flt color=0, power=0;
  1002. Int i=0, min=z-rangei, max=Min(z+rangei, size.z-1); if(min<0){i=-min; min=0;}
  1003. UInt pitch=src->pitch2();
  1004. C Flt *data=&src->pixF(x, y, 0);
  1005. for(; min<=max; min++, i++)
  1006. {
  1007. Flt c=*(Flt*)((Byte*)data+min*pitch), w=weight_f[i];
  1008. color+=w*c;
  1009. power+=w;
  1010. }
  1011. if(power)color/=power;
  1012. dest->pixF(x, y, z)=color;
  1013. }
  1014. void blur_Z_HP_1F_WRAP(Int x, Int y, Int z)C
  1015. {
  1016. Flt color=0, power=0;
  1017. UInt pitch=src->pitch2();
  1018. C Flt *data=&src->pixF(x, y, 0);
  1019. REP(rangei_2_1)
  1020. {
  1021. Flt c=*(Flt*)((Byte*)data+Mod(z+i-rangei, size.z)*pitch), w=weight_f[i];
  1022. color+=w*c;
  1023. power+=w;
  1024. }
  1025. if(power)color/=power;
  1026. dest->pixF(x, y, z)=color;
  1027. }
  1028. void blur_Z_HP_1C_CLAMP(Int x, Int y, Int z)C
  1029. {
  1030. Flt color=0, power=0;
  1031. Int i=0, min=z-rangei, max=Min(z+rangei, size.z-1); if(min<0){i=-min; min=0;}
  1032. for(; min<=max; min++, i++)
  1033. {
  1034. Flt c=src->pixel3DF(x, y, min), w=weight_f[i];
  1035. color+=w*c;
  1036. power+=w;
  1037. }
  1038. if(power)color/=power;
  1039. dest->pixel3DF(x, y, z, color);
  1040. }
  1041. void blur_Z_HP_1C_WRAP(Int x, Int y, Int z)C
  1042. {
  1043. Flt color=0, power=0;
  1044. REP(rangei_2_1)
  1045. {
  1046. Flt c=src->pixel3DF(x, y, Mod(z+i-rangei, size.z)), w=weight_f[i];
  1047. color+=w*c;
  1048. power+=w;
  1049. }
  1050. if(power)color/=power;
  1051. dest->pixel3DF(x, y, z, color);
  1052. }
  1053. void blur_Z_HP_MC_CLAMP(Int x, Int y, Int z)C
  1054. {
  1055. Vec4 color=0;
  1056. Flt power=0;
  1057. Int i=0, min=z-rangei, max=Min(z+rangei, size.z-1); if(min<0){i=-min; min=0;}
  1058. for(; min<=max; min++, i++)
  1059. {
  1060. Vec4 c=src->color3DF(x, y, min);
  1061. Flt w=weight_f[i];
  1062. color+=w*c;
  1063. power+=w;
  1064. }
  1065. if(power)color/=power;
  1066. dest->color3DF(x, y, z, color);
  1067. }
  1068. void blur_Z_HP_MC_WRAP(Int x, Int y, Int z)C
  1069. {
  1070. Vec4 color=0;
  1071. Flt power=0;
  1072. REP(rangei_2_1)
  1073. {
  1074. Vec4 c=src->color3DF(x, y, Mod(z+i-rangei, size.z));
  1075. Flt w=weight_f[i];
  1076. color+=w*c;
  1077. power+=w;
  1078. }
  1079. if(power)color/=power;
  1080. dest->color3DF(x, y, z, color);
  1081. }
  1082. void blur_Z_LP_1B_CLAMP(Int x, Int y, Int z)C
  1083. {
  1084. UInt value=0, power=0, pitch=src->pitch2();
  1085. Int i=0, min=z-rangei, max=Min(z+rangei, size.z-1); if(min<0){i=-min; min=0;}
  1086. C Byte *data=&src->pixB(x, y, 0);
  1087. for(; min<=max; min++, i++)
  1088. {
  1089. Byte c=data[min*pitch], w=weight_b[i];
  1090. value+=w*c;
  1091. power+=w;
  1092. }
  1093. if(power){UInt p_2=(power>>1); value=(value+p_2)/power;}
  1094. dest->pixB(x, y, z)=value;
  1095. }
  1096. void blur_Z_LP_1B_WRAP(Int x, Int y, Int z)C
  1097. {
  1098. UInt value=0, power=0, pitch=src->pitch2();
  1099. C Byte *data=&src->pixB(x, y, 0);
  1100. REP(rangei_2_1)
  1101. {
  1102. Byte c=data[Mod(z+i-rangei, size.z)*pitch], w=weight_b[i];
  1103. value+=w*c;
  1104. power+=w;
  1105. }
  1106. if(power){UInt p_2=(power>>1); value=(value+p_2)/power;}
  1107. dest->pixB(x, y, z)=value;
  1108. }
  1109. void blur_Z_LP_MC_CLAMP(Int x, Int y, Int z)C
  1110. {
  1111. UInt r=0, g=0, b=0, a=0, power=0;
  1112. Int i=0, min=z-rangei, max=Min(z+rangei, size.z-1); if(min<0){i=-min; min=0;}
  1113. for(; min<=max; min++, i++)
  1114. {
  1115. Color c=src->color3D(x, y, min);
  1116. Byte w=weight_b[i];
  1117. r+=c.r*w; g+=c.g*w; b+=c.b*w; a+=c.a*w;
  1118. power+=w;
  1119. }
  1120. if(power){UInt p_2=(power>>1); r=(r+p_2)/power; g=(g+p_2)/power; b=(b+p_2)/power; a=(a+p_2)/power;}
  1121. dest->color3D(x, y, z, Color(r, g, b, a));
  1122. }
  1123. void blur_Z_LP_MC_WRAP(Int x, Int y, Int z)C
  1124. {
  1125. UInt r=0, g=0, b=0, a=0, power=0;
  1126. REP(rangei_2_1)
  1127. {
  1128. Color c=src->color3D(x, y, Mod(z+i-rangei, size.z));
  1129. Byte w=weight_b[i];
  1130. r+=c.r*w; g+=c.g*w; b+=c.b*w; a+=c.a*w;
  1131. power+=w;
  1132. }
  1133. if(power){UInt p_2=(power>>1); r=(r+p_2)/power; g=(g+p_2)/power; b=(b+p_2)/power; a=(a+p_2)/power;}
  1134. dest->color3D(x, y, z, Color(r, g, b, a));
  1135. }
  1136. // AVERAGE
  1137. // X
  1138. void avg_X_HP_1F_CLAMP(Int x, Int y, Int z)C
  1139. {
  1140. Flt color=0;
  1141. Int i=0, min=x-rangei, max=Min(x+rangei, size.x-1); if(min<0){i=-min; min=0;} UInt power=max-min+1;
  1142. C Flt *data=&src->pixF(0, y, z);
  1143. for(; min<=max; min++, i++)color+=data[min];
  1144. color/=power; // always non-zero
  1145. dest->pixF(x, y, z)=color;
  1146. }
  1147. void avg_X_HP_1F_WRAP(Int x, Int y, Int z)C
  1148. {
  1149. Flt color=0;
  1150. C Flt *data=&src->pixF(0, y, z);
  1151. REP(rangei_2_1)color+=data[Mod(x+i-rangei, size.x)];
  1152. color/=rangei_2_1; // always non-zero
  1153. dest->pixF(x, y, z)=color;
  1154. }
  1155. void avg_X_HP_1C_CLAMP(Int x, Int y, Int z)C
  1156. {
  1157. Flt color=0;
  1158. Int i=0, min=x-rangei, max=Min(x+rangei, size.x-1); if(min<0){i=-min; min=0;} UInt power=max-min+1;
  1159. for(; min<=max; min++, i++)color+=src->pixel3DF(min, y, z);
  1160. color/=power; // always non-zero
  1161. dest->pixel3DF(x, y, z, color);
  1162. }
  1163. void avg_X_HP_1C_WRAP(Int x, Int y, Int z)C
  1164. {
  1165. Flt color=0;
  1166. REP(rangei_2_1)color+=src->pixel3DF(Mod(x+i-rangei, size.x), y, z);
  1167. color/=rangei_2_1; // always non-zero
  1168. dest->pixel3DF(x, y, z, color);
  1169. }
  1170. void avg_X_HP_MC_CLAMP(Int x, Int y, Int z)C
  1171. {
  1172. Vec4 color=0;
  1173. Int i=0, min=x-rangei, max=Min(x+rangei, size.x-1); if(min<0){i=-min; min=0;} UInt power=max-min+1;
  1174. for(; min<=max; min++, i++)color+=src->color3DF(min, y, z);
  1175. color/=power; // always non-zero
  1176. dest->color3DF(x, y, z, color);
  1177. }
  1178. void avg_X_HP_MC_WRAP(Int x, Int y, Int z)C
  1179. {
  1180. Vec4 color=0;
  1181. REP(rangei_2_1)color+=src->color3DF(Mod(x+i-rangei, size.x), y, z);
  1182. color/=rangei_2_1; // always non-zero
  1183. dest->color3DF(x, y, z, color);
  1184. }
  1185. void avg_X_LP_1B_CLAMP(Int x, Int y, Int z)C
  1186. {
  1187. UInt value=0;
  1188. Int i=0, min=x-rangei, max=Min(x+rangei, size.x-1); if(min<0){i=-min; min=0;} UInt power=max-min+1;
  1189. C Byte *data=&src->pixB(0, y, z);
  1190. for(; min<=max; min++, i++)value+=data[min];
  1191. UInt p_2=(power>>1); value=(value+p_2)/power; // always non-zero
  1192. dest->pixB(x, y, z)=value;
  1193. }
  1194. void avg_X_LP_1B_WRAP(Int x, Int y, Int z)C
  1195. {
  1196. UInt value=0;
  1197. C Byte *data=&src->pixB(0, y, z);
  1198. REP(rangei_2_1)value+=data[Mod(x+i-rangei, size.x)];
  1199. UInt p_2=(rangei_2_1>>1); value=(value+p_2)/rangei_2_1; // always non-zero
  1200. dest->pixB(x, y, z)=value;
  1201. }
  1202. void avg_X_LP_MC_CLAMP(Int x, Int y, Int z)C
  1203. {
  1204. UInt r=0, g=0, b=0, a=0;
  1205. Int i=0, min=x-rangei, max=Min(x+rangei, size.x-1); if(min<0){i=-min; min=0;} UInt power=max-min+1;
  1206. for(; min<=max; min++, i++){Color c=src->color3D(min, y, z); r+=c.r; g+=c.g; b+=c.b; a+=c.a;}
  1207. UInt p_2=(power>>1); r=(r+p_2)/power; g=(g+p_2)/power; b=(b+p_2)/power; a=(a+p_2)/power; // always non-zero
  1208. dest->color3D(x, y, z, Color(r, g, b, a));
  1209. }
  1210. void avg_X_LP_MC_WRAP(Int x, Int y, Int z)C
  1211. {
  1212. UInt r=0, g=0, b=0, a=0;
  1213. REP(rangei_2_1){Color c=src->color3D(Mod(x+i-rangei, size.x), y, z); r+=c.r; g+=c.g; b+=c.b; a+=c.a;}
  1214. UInt p_2=(rangei_2_1>>1); r=(r+p_2)/rangei_2_1; g=(g+p_2)/rangei_2_1; b=(b+p_2)/rangei_2_1; a=(a+p_2)/rangei_2_1; // always non-zero
  1215. dest->color3D(x, y, z, Color(r, g, b, a));
  1216. }
  1217. // Y
  1218. void avg_Y_HP_1F_CLAMP(Int x, Int y, Int z)C
  1219. {
  1220. Flt color=0;
  1221. Int i=0, min=y-rangei, max=Min(y+rangei, size.y-1); if(min<0){i=-min; min=0;} UInt power=max-min+1, pitch=src->pitch();
  1222. C Flt *data=&src->pixF(x, 0, z);
  1223. for(; min<=max; min++, i++)color+=*(Flt*)((Byte*)data+min*pitch);
  1224. color/=power; // always non-zero
  1225. dest->pixF(x, y, z)=color;
  1226. }
  1227. void avg_Y_HP_1F_WRAP(Int x, Int y, Int z)C
  1228. {
  1229. Flt color=0; UInt pitch=src->pitch();
  1230. C Flt *data=&src->pixF(x, 0, z);
  1231. REP(rangei_2_1)color+=*(Flt*)((Byte*)data+Mod(y+i-rangei, size.y)*pitch);
  1232. color/=rangei_2_1; // always non-zero
  1233. dest->pixF(x, y, z)=color;
  1234. }
  1235. void avg_Y_HP_1C_CLAMP(Int x, Int y, Int z)C
  1236. {
  1237. Flt color=0;
  1238. Int i=0, min=y-rangei, max=Min(y+rangei, size.y-1); if(min<0){i=-min; min=0;} UInt power=max-min+1;
  1239. for(; min<=max; min++, i++)color+=src->pixel3DF(x, min, z);
  1240. color/=power; // always non-zero
  1241. dest->pixel3DF(x, y, z, color);
  1242. }
  1243. void avg_Y_HP_1C_WRAP(Int x, Int y, Int z)C
  1244. {
  1245. Flt color=0;
  1246. REP(rangei_2_1)color+=src->pixel3DF(x, Mod(y+i-rangei, size.y), z);
  1247. color/=rangei_2_1; // always non-zero
  1248. dest->pixel3DF(x, y, z, color);
  1249. }
  1250. void avg_Y_HP_MC_CLAMP(Int x, Int y, Int z)C
  1251. {
  1252. Vec4 color=0;
  1253. Int i=0, min=y-rangei, max=Min(y+rangei, size.y-1); if(min<0){i=-min; min=0;} UInt power=max-min+1;
  1254. for(; min<=max; min++, i++)color+=src->color3DF(x, min, z);
  1255. color/=power; // always non-zero
  1256. dest->color3DF(x, y, z, color);
  1257. }
  1258. void avg_Y_HP_MC_WRAP(Int x, Int y, Int z)C
  1259. {
  1260. Vec4 color=0;
  1261. REP(rangei_2_1)color+=src->color3DF(x, Mod(y+i-rangei, size.y), z);
  1262. color/=rangei_2_1; // always non-zero
  1263. dest->color3DF(x, y, z, color);
  1264. }
  1265. void avg_Y_LP_1B_CLAMP(Int x, Int y, Int z)C
  1266. {
  1267. UInt value=0, pitch=src->pitch();
  1268. Int i=0, min=y-rangei, max=Min(y+rangei, size.y-1); if(min<0){i=-min; min=0;} UInt power=max-min+1;
  1269. C Byte *data=&src->pixB(x, 0, z);
  1270. for(; min<=max; min++, i++)value+=data[min*pitch];
  1271. UInt p_2=(power>>1); value=(value+p_2)/power; // always non-zero
  1272. dest->pixB(x, y, z)=value;
  1273. }
  1274. void avg_Y_LP_1B_WRAP(Int x, Int y, Int z)C
  1275. {
  1276. UInt value=0, pitch=src->pitch();
  1277. C Byte *data=&src->pixB(x, 0, z);
  1278. REP(rangei_2_1)value+=data[Mod(y+i-rangei, size.y)*pitch];
  1279. UInt p_2=(rangei_2_1>>1); value=(value+p_2)/rangei_2_1; // always non-zero
  1280. dest->pixB(x, y, z)=value;
  1281. }
  1282. void avg_Y_LP_MC_CLAMP(Int x, Int y, Int z)C
  1283. {
  1284. UInt r=0, g=0, b=0, a=0;
  1285. Int i=0, min=y-rangei, max=Min(y+rangei, size.y-1); if(min<0){i=-min; min=0;} UInt power=max-min+1;
  1286. for(; min<=max; min++, i++){Color c=src->color3D(x, min, z); r+=c.r; g+=c.g; b+=c.b; a+=c.a;}
  1287. UInt p_2=(power>>1); r=(r+p_2)/power; g=(g+p_2)/power; b=(b+p_2)/power; a=(a+p_2)/power; // always non-zero
  1288. dest->color3D(x, y, z, Color(r, g, b, a));
  1289. }
  1290. void avg_Y_LP_MC_WRAP(Int x, Int y, Int z)C
  1291. {
  1292. UInt r=0, g=0, b=0, a=0;
  1293. REP(rangei_2_1){Color c=src->color3D(x, Mod(y+i-rangei, size.y), z); r+=c.r; g+=c.g; b+=c.b; a+=c.a;}
  1294. UInt p_2=(rangei_2_1>>1); r=(r+p_2)/rangei_2_1; g=(g+p_2)/rangei_2_1; b=(b+p_2)/rangei_2_1; a=(a+p_2)/rangei_2_1; // always non-zero
  1295. dest->color3D(x, y, z, Color(r, g, b, a));
  1296. }
  1297. // Z
  1298. void avg_Z_HP_1F_CLAMP(Int x, Int y, Int z)C
  1299. {
  1300. Flt color=0;
  1301. Int i=0, min=z-rangei, max=Min(z+rangei, size.z-1); if(min<0){i=-min; min=0;} UInt power=max-min+1, pitch=src->pitch2();
  1302. C Flt *data=&src->pixF(x, y, 0);
  1303. for(; min<=max; min++, i++)color+=*(Flt*)((Byte*)data+min*pitch);
  1304. color/=power; // always non-zero
  1305. dest->pixF(x, y, z)=color;
  1306. }
  1307. void avg_Z_HP_1F_WRAP(Int x, Int y, Int z)C
  1308. {
  1309. Flt color=0; UInt pitch=src->pitch2();
  1310. C Flt *data=&src->pixF(x, y, 0);
  1311. REP(rangei_2_1)color+=*(Flt*)((Byte*)data+Mod(z+i-rangei, size.z)*pitch);
  1312. color/=rangei_2_1; // always non-zero
  1313. dest->pixF(x, y, z)=color;
  1314. }
  1315. void avg_Z_HP_1C_CLAMP(Int x, Int y, Int z)C
  1316. {
  1317. Flt color=0;
  1318. Int i=0, min=z-rangei, max=Min(z+rangei, size.z-1); if(min<0){i=-min; min=0;} UInt power=max-min+1;
  1319. for(; min<=max; min++, i++)color+=src->pixel3DF(x, y, min);
  1320. color/=power; // always non-zero
  1321. dest->pixel3DF(x, y, z, color);
  1322. }
  1323. void avg_Z_HP_1C_WRAP(Int x, Int y, Int z)C
  1324. {
  1325. Flt color=0;
  1326. REP(rangei_2_1)color+=src->pixel3DF(x, y, Mod(z+i-rangei, size.z));
  1327. color/=rangei_2_1; // always non-zero
  1328. dest->pixel3DF(x, y, z, color);
  1329. }
  1330. void avg_Z_HP_MC_CLAMP(Int x, Int y, Int z)C
  1331. {
  1332. Vec4 color=0;
  1333. Int i=0, min=z-rangei, max=Min(z+rangei, size.z-1); if(min<0){i=-min; min=0;} UInt power=max-min+1;
  1334. for(; min<=max; min++, i++)color+=src->color3DF(x, y, min);
  1335. color/=power; // always non-zero
  1336. dest->color3DF(x, y, z, color);
  1337. }
  1338. void avg_Z_HP_MC_WRAP(Int x, Int y, Int z)C
  1339. {
  1340. Vec4 color=0;
  1341. REP(rangei_2_1)color+=src->color3DF(x, y, Mod(z+i-rangei, size.z));
  1342. color/=rangei_2_1; // always non-zero
  1343. dest->color3DF(x, y, z, color);
  1344. }
  1345. void avg_Z_LP_1B_CLAMP(Int x, Int y, Int z)C
  1346. {
  1347. UInt value=0, pitch=src->pitch2();
  1348. Int i=0, min=z-rangei, max=Min(z+rangei, size.z-1); if(min<0){i=-min; min=0;} UInt power=max-min+1;
  1349. C Byte *data=&src->pixB(x, y, 0);
  1350. for(; min<=max; min++, i++)value+=data[min*pitch];
  1351. UInt p_2=(power>>1); value=(value+p_2)/power; // always non-zero
  1352. dest->pixB(x, y, z)=value;
  1353. }
  1354. void avg_Z_LP_1B_WRAP(Int x, Int y, Int z)C
  1355. {
  1356. UInt value=0, pitch=src->pitch2();
  1357. C Byte *data=&src->pixB(x, y, 0);
  1358. REP(rangei_2_1)value+=data[Mod(z+i-rangei, size.z)*pitch];
  1359. UInt p_2=(rangei_2_1>>1); value=(value+p_2)/rangei_2_1; // always non-zero
  1360. dest->pixB(x, y, z)=value;
  1361. }
  1362. void avg_Z_LP_MC_CLAMP(Int x, Int y, Int z)C
  1363. {
  1364. UInt r=0, g=0, b=0, a=0;
  1365. Int i=0, min=z-rangei, max=Min(z+rangei, size.z-1); if(min<0){i=-min; min=0;} UInt power=max-min+1;
  1366. for(; min<=max; min++, i++){Color c=src->color3D(x, y, min); r+=c.r; g+=c.g; b+=c.b; a+=c.a;}
  1367. UInt p_2=(power>>1); r=(r+p_2)/power; g=(g+p_2)/power; b=(b+p_2)/power; a=(a+p_2)/power; // always non-zero
  1368. dest->color3D(x, y, z, Color(r, g, b, a));
  1369. }
  1370. void avg_Z_LP_MC_WRAP(Int x, Int y, Int z)C
  1371. {
  1372. UInt r=0, g=0, b=0, a=0;
  1373. REP(rangei_2_1){Color c=src->color3D(x, y, Mod(z+i-rangei, size.z)); r+=c.r; g+=c.g; b+=c.b; a+=c.a;}
  1374. UInt p_2=(rangei_2_1>>1); r=(r+p_2)/rangei_2_1; g=(g+p_2)/rangei_2_1; b=(b+p_2)/rangei_2_1; a=(a+p_2)/rangei_2_1; // always non-zero
  1375. dest->color3D(x, y, z, Color(r, g, b, a));
  1376. }
  1377. };
  1378. /******************************************************************************/
  1379. Bool Image::averageX(Image &dest, Int range, Bool clamp, Threads *threads)C
  1380. {
  1381. if(range<=0 || w()<=1)return copyTry(dest);
  1382. IMAGE_TYPE type =T.type ();
  1383. IMAGE_MODE mode =T.mode ();
  1384. Int mip_maps=T.mipMaps();
  1385. C Image *src=this; Image temp; if(src->compressed())if(src->copyTry(temp, -1, -1, -1, IMAGE_R8G8B8A8, IMAGE_SOFT, 1))src=&temp;else return false;
  1386. Image &work=((src==&dest) ? temp : dest); // this makes &work!=src
  1387. if(!work.createTry(src->w(), src->h(), src->d(), src->type(), src->mode(), src->mipMaps()))return false; // use 'src.mode' and 'src.mipMaps' because if(src.compressed) then we will get IMAGE_SOFT 1 (what we want, because we will compress below in 'copyTry'), and if not compressed then we will create the target as what we want the dest to be
  1388. if(!src->lockRead())return false;
  1389. if(!work.lock (LOCK_WRITE)){src->unlock(); return false;}
  1390. BlurContext bc(*src, clamp, threads); bc.setRange(range); bc.setAvgX(); bc.dest=&work; bc.blur();
  1391. work.unlock(); src->unlock();
  1392. if(work.type()==type && work.mode()==mode && work.mipMaps()==mip_maps){work.updateMipMaps(FILTER_BEST, clamp); if(&work!=&dest)Swap(work, dest); return true;} // if we have desired type mode and mip maps, then all we need is to update mip maps and possibly Swap if needed, remember that after Swap we should operate on 'dest' and not 'work'
  1393. return work.copyTry(dest, -1, -1, -1, type, mode, mip_maps, FILTER_BEST, clamp);
  1394. }
  1395. Bool Image::averageY(Image &dest, Int range, Bool clamp, Threads *threads)C
  1396. {
  1397. if(range<=0 || h()<=1)return copyTry(dest);
  1398. IMAGE_TYPE type =T.type ();
  1399. IMAGE_MODE mode =T.mode ();
  1400. Int mip_maps=T.mipMaps();
  1401. C Image *src=this; Image temp; if(src->compressed())if(src->copyTry(temp, -1, -1, -1, IMAGE_R8G8B8A8, IMAGE_SOFT, 1))src=&temp;else return false;
  1402. Image &work=((src==&dest) ? temp : dest); // this makes &work!=src
  1403. if(!work.createTry(src->w(), src->h(), src->d(), src->type(), src->mode(), src->mipMaps()))return false; // use 'src.mode' and 'src.mipMaps' because if(src.compressed) then we will get IMAGE_SOFT 1 (what we want, because we will compress below in 'copyTry'), and if not compressed then we will create the target as what we want the dest to be
  1404. if(!src->lockRead())return false;
  1405. if(!work.lock (LOCK_WRITE)){src->unlock(); return false;}
  1406. BlurContext bc(*src, clamp, threads); bc.setRange(range); bc.setAvgY(); bc.dest=&work; bc.blur();
  1407. work.unlock(); src->unlock();
  1408. if(work.type()==type && work.mode()==mode && work.mipMaps()==mip_maps){work.updateMipMaps(FILTER_BEST, clamp); if(&work!=&dest)Swap(work, dest); return true;} // if we have desired type mode and mip maps, then all we need is to update mip maps and possibly Swap if needed, remember that after Swap we should operate on 'dest' and not 'work'
  1409. return work.copyTry(dest, -1, -1, -1, type, mode, mip_maps, FILTER_BEST, clamp);
  1410. }
  1411. Bool Image::averageZ(Image &dest, Int range, Bool clamp, Threads *threads)C
  1412. {
  1413. if(range<=0 || d()<=1)return copyTry(dest);
  1414. IMAGE_TYPE type =T.type ();
  1415. IMAGE_MODE mode =T.mode ();
  1416. Int mip_maps=T.mipMaps();
  1417. C Image *src=this; Image temp; if(src->compressed())if(src->copyTry(temp, -1, -1, -1, IMAGE_R8G8B8A8, IMAGE_SOFT, 1))src=&temp;else return false;
  1418. Image &work=((src==&dest) ? temp : dest); // this makes &work!=src
  1419. if(!work.createTry(src->w(), src->h(), src->d(), src->type(), src->mode(), src->mipMaps()))return false; // use 'src.mode' and 'src.mipMaps' because if(src.compressed) then we will get IMAGE_SOFT 1 (what we want, because we will compress below in 'copyTry'), and if not compressed then we will create the target as what we want the dest to be
  1420. if(!src->lockRead())return false;
  1421. if(!work.lock (LOCK_WRITE)){src->unlock(); return false;}
  1422. BlurContext bc(*src, clamp, threads); bc.setRange(range); bc.setAvgZ(); bc.dest=&work; bc.blur();
  1423. work.unlock(); src->unlock();
  1424. if(work.type()==type && work.mode()==mode && work.mipMaps()==mip_maps){work.updateMipMaps(FILTER_BEST, clamp); if(&work!=&dest)Swap(work, dest); return true;} // if we have desired type mode and mip maps, then all we need is to update mip maps and possibly Swap if needed, remember that after Swap we should operate on 'dest' and not 'work'
  1425. return work.copyTry(dest, -1, -1, -1, type, mode, mip_maps, FILTER_BEST, clamp);
  1426. }
  1427. /******************************************************************************/
  1428. Bool Image::blurX(Image &dest, Flt range, Bool clamp, Threads *threads)C
  1429. {
  1430. if(range<=0 || w()<=1)return copyTry(dest);
  1431. IMAGE_TYPE type =T.type ();
  1432. IMAGE_MODE mode =T.mode ();
  1433. Int mip_maps=T.mipMaps();
  1434. C Image *src=this; Image temp; if(src->compressed())if(src->copyTry(temp, -1, -1, -1, IMAGE_R8G8B8A8, IMAGE_SOFT, 1))src=&temp;else return false;
  1435. Image &work=((src==&dest) ? temp : dest); // this makes &work!=src
  1436. if(!work.createTry(src->w(), src->h(), src->d(), src->type(), src->mode(), src->mipMaps()))return false; // use 'src.mode' and 'src.mipMaps' because if(src.compressed) then we will get IMAGE_SOFT 1 (what we want, because we will compress below in 'copyTry'), and if not compressed then we will create the target as what we want the dest to be
  1437. if(!src->lockRead())return false;
  1438. if(!work.lock (LOCK_WRITE)){src->unlock(); return false;}
  1439. BlurContext bc(*src, clamp, threads); bc.setRange(range); bc.setX(); bc.dest=&work; bc.blur();
  1440. work.unlock(); src->unlock();
  1441. if(work.type()==type && work.mode()==mode && work.mipMaps()==mip_maps){work.updateMipMaps(FILTER_BEST, clamp); if(&work!=&dest)Swap(work, dest); return true;} // if we have desired type mode and mip maps, then all we need is to update mip maps and possibly Swap if needed, remember that after Swap we should operate on 'dest' and not 'work'
  1442. return work.copyTry(dest, -1, -1, -1, type, mode, mip_maps, FILTER_BEST, clamp);
  1443. }
  1444. Bool Image::blurY(Image &dest, Flt range, Bool clamp, Threads *threads)C
  1445. {
  1446. if(range<=0 || h()<=1)return copyTry(dest);
  1447. IMAGE_TYPE type =T.type ();
  1448. IMAGE_MODE mode =T.mode ();
  1449. Int mip_maps=T.mipMaps();
  1450. C Image *src=this; Image temp; if(src->compressed())if(src->copyTry(temp, -1, -1, -1, IMAGE_R8G8B8A8, IMAGE_SOFT, 1))src=&temp;else return false;
  1451. Image &work=((src==&dest) ? temp : dest); // this makes &work!=src
  1452. if(!work.createTry(src->w(), src->h(), src->d(), src->type(), src->mode(), src->mipMaps()))return false; // use 'src.mode' and 'src.mipMaps' because if(src.compressed) then we will get IMAGE_SOFT 1 (what we want, because we will compress below in 'copyTry'), and if not compressed then we will create the target as what we want the dest to be
  1453. if(!src->lockRead())return false;
  1454. if(!work.lock (LOCK_WRITE)){src->unlock(); return false;}
  1455. BlurContext bc(*src, clamp, threads); bc.setRange(range); bc.setY(); bc.dest=&work; bc.blur();
  1456. work.unlock(); src->unlock();
  1457. if(work.type()==type && work.mode()==mode && work.mipMaps()==mip_maps){work.updateMipMaps(FILTER_BEST, clamp); if(&work!=&dest)Swap(work, dest); return true;} // if we have desired type mode and mip maps, then all we need is to update mip maps and possibly Swap if needed, remember that after Swap we should operate on 'dest' and not 'work'
  1458. return work.copyTry(dest, -1, -1, -1, type, mode, mip_maps, FILTER_BEST, clamp);
  1459. }
  1460. Bool Image::blurZ(Image &dest, Flt range, Bool clamp, Threads *threads)C
  1461. {
  1462. if(range<=0 || d()<=1)return copyTry(dest);
  1463. IMAGE_TYPE type =T.type ();
  1464. IMAGE_MODE mode =T.mode ();
  1465. Int mip_maps=T.mipMaps();
  1466. C Image *src=this; Image temp; if(src->compressed())if(src->copyTry(temp, -1, -1, -1, IMAGE_R8G8B8A8, IMAGE_SOFT, 1))src=&temp;else return false;
  1467. Image &work=((src==&dest) ? temp : dest); // this makes &work!=src
  1468. if(!work.createTry(src->w(), src->h(), src->d(), src->type(), src->mode(), src->mipMaps()))return false; // use 'src.mode' and 'src.mipMaps' because if(src.compressed) then we will get IMAGE_SOFT 1 (what we want, because we will compress below in 'copyTry'), and if not compressed then we will create the target as what we want the dest to be
  1469. if(!src->lockRead())return false;
  1470. if(!work.lock (LOCK_WRITE)){src->unlock(); return false;}
  1471. BlurContext bc(*src, clamp, threads); bc.setRange(range); bc.setZ(); bc.dest=&work; bc.blur();
  1472. work.unlock(); src->unlock();
  1473. if(work.type()==type && work.mode()==mode && work.mipMaps()==mip_maps){work.updateMipMaps(FILTER_BEST, clamp); if(&work!=&dest)Swap(work, dest); return true;} // if we have desired type mode and mip maps, then all we need is to update mip maps and possibly Swap if needed, remember that after Swap we should operate on 'dest' and not 'work'
  1474. return work.copyTry(dest, -1, -1, -1, type, mode, mip_maps, FILTER_BEST, clamp);
  1475. }
  1476. /******************************************************************************/
  1477. Image& Image::average( C VecI &range, Bool clamp, Threads *threads) {average(T, range, clamp, threads); return T;}
  1478. Bool Image::average(Image &dest, C VecI &range, Bool clamp, Threads *threads)C
  1479. {
  1480. Bool blur[]={range.x>0 && w()>1, range.y>0 && h()>1, range.z>0 && d()>1};
  1481. Int blurs =blur[0]+blur[1]+blur[2];
  1482. if( !blurs)return copyTry(dest);
  1483. IMAGE_TYPE type =T.type ();
  1484. IMAGE_MODE mode =T.mode ();
  1485. Int mip_maps=T.mipMaps();
  1486. C Image *src=this; Image temp; if(src->compressed())if(src->copyTry(temp, -1, -1, -1, IMAGE_R8G8B8A8, IMAGE_SOFT, 1))src=&temp;else return false;
  1487. BlurContext bc(*src, clamp, threads);
  1488. if(src==&dest) // this also means !T.compressed && 'temp' is empty
  1489. {
  1490. if(blurs&1)temp.createTry (w(), h(), d(), type, mode, mip_maps); // we will end up in 'temp' so create 'temp' as target mode and mip maps
  1491. else temp.createSoftTry(w(), h(), d(), type, 1); // we will end up in 'dest' so create 'temp' as SOFT
  1492. if(!temp.is())return false;
  1493. bc.dest=&temp;
  1494. if(!temp.lock(LOCK_WRITE ))return false;
  1495. if(!dest.lock(LOCK_READ_WRITE))return false; // we read from src and write to dest, and src==dest
  1496. // we already locked 'src' by locking 'dest'
  1497. }else
  1498. if(src==&temp) // this also means T.compressed && 'temp' is created
  1499. {
  1500. if(!dest.createSoftTry(w(), h(), d(), IMAGE_R8G8B8A8, 1))return false; // always create as soft, because T.compressed so we will have to compress it anyway
  1501. bc.dest=&dest;
  1502. // no need to lock, because src==temp (which is SOFT) and dest is SOFT
  1503. }else // src!=&dest && src!=&temp && !T.compressed
  1504. {
  1505. if( !dest.createTry (w(), h(), d(), type, mode, mip_maps))return false; // we will end up in 'dest' so create it as target mode and mip maps
  1506. if(blurs>1 && !temp.createSoftTry(w(), h(), d(), type, 1))return false; // create temp as SOFT
  1507. bc.dest=((blurs&1) ? &dest : &temp);
  1508. if(!src->lockRead() )return false;
  1509. if(!dest.lock(LOCK_WRITE))return false;
  1510. // 'temp' is SOFT so doesn't need lock
  1511. }
  1512. if(blur[0]){bc.setRange(range.x); bc.setAvgX(); bc.blur(); bc.src=bc.dest; bc.dest=((bc.src==&dest) ? &temp : &dest);}
  1513. if(blur[1]){bc.setRange(range.y); bc.setAvgY(); bc.blur(); bc.src=bc.dest; bc.dest=((bc.src==&dest) ? &temp : &dest);}
  1514. if(blur[2]){bc.setRange(range.z); bc.setAvgZ(); bc.blur(); bc.src=bc.dest; bc.dest=((bc.src==&dest) ? &temp : &dest);}
  1515. if(src==&dest)
  1516. {
  1517. temp.unlock(); // unlock in case we will Swap it
  1518. dest.unlock();
  1519. }else
  1520. if(src==&temp)
  1521. {
  1522. }else
  1523. {
  1524. src->unlock();
  1525. dest.unlock();
  1526. }
  1527. Image &img=ConstCast(*bc.src); // at this point 'bc.src' points to non const image ('temp' or 'dest'), so we can use 'ConstCast'
  1528. if(img.type()==type && img.mode()==mode && img.mipMaps()==mip_maps){img.updateMipMaps(FILTER_BEST, clamp); if(&img!=&dest)Swap(img, dest); return true;} // if we have desired type mode and mip maps, then all we need is to update mip maps and possibly Swap if needed, remember that after Swap we should operate on 'dest' and not 'img'
  1529. return img.copyTry(dest, -1, -1, -1, type, mode, mip_maps, FILTER_BEST, clamp);
  1530. }
  1531. Image& Image::blur( C Vec &range, Bool clamp, Threads *threads) {blur(T, range, clamp, threads); return T;}
  1532. Bool Image::blur(Image &dest, C Vec &range, Bool clamp, Threads *threads)C
  1533. {
  1534. Bool blur[]={range.x>0 && w()>1, range.y>0 && h()>1, range.z>0 && d()>1};
  1535. Int blurs =blur[0]+blur[1]+blur[2];
  1536. if( !blurs)return copyTry(dest);
  1537. IMAGE_TYPE type =T.type ();
  1538. IMAGE_MODE mode =T.mode ();
  1539. Int mip_maps=T.mipMaps();
  1540. C Image *src=this; Image temp; if(src->compressed())if(src->copyTry(temp, -1, -1, -1, IMAGE_R8G8B8A8, IMAGE_SOFT, 1))src=&temp;else return false;
  1541. BlurContext bc(*src, clamp, threads);
  1542. if(src==&dest) // this also means !T.compressed && 'temp' is empty
  1543. {
  1544. if(blurs&1)temp.createTry (w(), h(), d(), type, mode, mip_maps); // we will end up in 'temp' so create 'temp' as target mode and mip maps
  1545. else temp.createSoftTry(w(), h(), d(), type, 1); // we will end up in 'dest' so create 'temp' as SOFT
  1546. if(!temp.is())return false;
  1547. bc.dest=&temp;
  1548. if(!temp.lock(LOCK_WRITE ))return false;
  1549. if(!dest.lock(LOCK_READ_WRITE))return false; // we read from src and write to dest, and src==dest
  1550. // we already locked 'src' by locking 'dest'
  1551. }else
  1552. if(src==&temp) // this also means T.compressed && 'temp' is created
  1553. {
  1554. if(!dest.createSoftTry(w(), h(), d(), IMAGE_R8G8B8A8, 1))return false; // always create as soft, because T.compressed so we will have to compress it anyway
  1555. bc.dest=&dest;
  1556. // no need to lock, because src==temp (which is SOFT) and dest is SOFT
  1557. }else // src!=&dest && src!=&temp && !T.compressed
  1558. {
  1559. if( !dest.createTry (w(), h(), d(), type, mode, mip_maps))return false; // we will end up in 'dest' so create it as target mode and mip maps
  1560. if(blurs>1 && !temp.createSoftTry(w(), h(), d(), type, 1))return false; // create temp as SOFT
  1561. bc.dest=((blurs&1) ? &dest : &temp);
  1562. if(!src->lockRead() )return false;
  1563. if(!dest.lock(LOCK_WRITE))return false;
  1564. // 'temp' is SOFT so doesn't need lock
  1565. }
  1566. if(blur[0]){bc.setRange(range.x); bc.setX(); bc.blur(); bc.src=bc.dest; bc.dest=((bc.src==&dest) ? &temp : &dest);}
  1567. if(blur[1]){bc.setRange(range.y); bc.setY(); bc.blur(); bc.src=bc.dest; bc.dest=((bc.src==&dest) ? &temp : &dest);}
  1568. if(blur[2]){bc.setRange(range.z); bc.setZ(); bc.blur(); bc.src=bc.dest; bc.dest=((bc.src==&dest) ? &temp : &dest);}
  1569. if(src==&dest)
  1570. {
  1571. temp.unlock(); // unlock in case we will Swap it
  1572. dest.unlock();
  1573. }else
  1574. if(src==&temp)
  1575. {
  1576. }else
  1577. {
  1578. src->unlock();
  1579. dest.unlock();
  1580. }
  1581. Image &img=ConstCast(*bc.src); // at this point 'bc.src' points to non const image ('temp' or 'dest'), so we can use 'ConstCast'
  1582. if(img.type()==type && img.mode()==mode && img.mipMaps()==mip_maps){img.updateMipMaps(FILTER_BEST, clamp); if(&img!=&dest)Swap(img, dest); return true;} // if we have desired type mode and mip maps, then all we need is to update mip maps and possibly Swap if needed, remember that after Swap we should operate on 'dest' and not 'img'
  1583. return img.copyTry(dest, -1, -1, -1, type, mode, mip_maps, FILTER_BEST, clamp);
  1584. }
  1585. /******************************************************************************/
  1586. Image& Image::sharpen(Flt power, Byte range, Bool clamp, Bool blur)
  1587. {
  1588. IMAGE_TYPE type;
  1589. IMAGE_MODE mode;
  1590. Int mip_maps;
  1591. if(range && Decompress(T, type, mode, mip_maps) && lock())
  1592. {
  1593. Image temp(w(), h(), d(), T.type(), IMAGE_SOFT, 1); if(copySoft(temp))
  1594. {
  1595. if(blur)temp.blur (range, clamp);
  1596. else temp.average(range, clamp);
  1597. REPD(z, T.d())
  1598. REPD(y, T.h())
  1599. REPD(x, T.w())
  1600. {
  1601. Color col_src = color3D(x, y, z),
  1602. col_blur=temp.color3D(x, y, z), c;
  1603. c.a=Mid(col_src.a+Round((col_src.a-col_blur.a)*power), 0, 255);
  1604. c.r=Mid(col_src.r+Round((col_src.r-col_blur.r)*power), 0, 255);
  1605. c.g=Mid(col_src.g+Round((col_src.g-col_blur.g)*power), 0, 255);
  1606. c.b=Mid(col_src.b+Round((col_src.b-col_blur.b)*power), 0, 255);
  1607. color3D(x, y, z, c);
  1608. }
  1609. }
  1610. unlock().updateMipMaps(FILTER_BEST, clamp);
  1611. Compress(T, type, mode, mip_maps);
  1612. }
  1613. return T;
  1614. }
  1615. /******************************************************************************/
  1616. Image& Image::noise(Byte red, Byte green, Byte blue, Byte alpha)
  1617. {
  1618. IMAGE_TYPE type;
  1619. IMAGE_MODE mode;
  1620. Int mip_maps;
  1621. if(red || green || blue || alpha)
  1622. if(Decompress(T, type, mode, mip_maps) && lock())
  1623. {
  1624. switch(T.type())
  1625. {
  1626. case IMAGE_L8 :
  1627. case IMAGE_L8A8:
  1628. {
  1629. Byte noise=Max(red, green, blue);
  1630. REPD(z, T.d())
  1631. REPD(y, T.h())
  1632. REPD(x, T.w())
  1633. {
  1634. Color c=color3D(x, y, z);
  1635. c.r=c.g=c.b=Mid(c.r+Random(-noise, noise), 0, 255);
  1636. c.a=Mid(c.a+Random(-alpha, alpha), 0, 255);
  1637. color3D(x, y, z, c);
  1638. }
  1639. }break;
  1640. default:
  1641. REPD(z, T.d())
  1642. REPD(y, T.h())
  1643. REPD(x, T.w())
  1644. {
  1645. Color c=color3D(x, y, z);
  1646. c.r=Mid(c.r+Random(-red , red ), 0, 255);
  1647. c.g=Mid(c.g+Random(-green, green), 0, 255);
  1648. c.b=Mid(c.b+Random(-blue , blue ), 0, 255);
  1649. c.a=Mid(c.a+Random(-alpha, alpha), 0, 255);
  1650. color3D(x, y, z, c);
  1651. }
  1652. break;
  1653. }
  1654. unlock().updateMipMaps();
  1655. Compress(T, type, mode, mip_maps);
  1656. }
  1657. return T;
  1658. }
  1659. /******************************************************************************/
  1660. Image& Image::RGBToHSB()
  1661. {
  1662. IMAGE_TYPE type;
  1663. IMAGE_MODE mode;
  1664. Int mip_maps;
  1665. if(Decompress(T, type, mode, mip_maps) && lock())
  1666. {
  1667. REPD(z, T.d())
  1668. REPD(y, T.h())
  1669. REPD(x, T.w())
  1670. {
  1671. Vec4 c=color3DF(x, y, z);
  1672. c.xyz=RgbToHsb(c.xyz);
  1673. color3DF(x, y, z, c);
  1674. }
  1675. unlock().updateMipMaps();
  1676. Compress(T, type, mode, mip_maps);
  1677. }
  1678. return T;
  1679. }
  1680. Image& Image::HSBToRGB()
  1681. {
  1682. IMAGE_TYPE type;
  1683. IMAGE_MODE mode;
  1684. Int mip_maps;
  1685. if(Decompress(T, type, mode, mip_maps) && lock())
  1686. {
  1687. REPD(z, T.d())
  1688. REPD(y, T.h())
  1689. REPD(x, T.w())
  1690. {
  1691. Vec4 c=color3DF(x, y, z);
  1692. c.xyz=HsbToRgb(c.xyz);
  1693. color3DF(x, y, z, c);
  1694. }
  1695. unlock().updateMipMaps();
  1696. Compress(T, type, mode, mip_maps);
  1697. }
  1698. return T;
  1699. }
  1700. /******************************************************************************
  1701. Image& Image::lumFromAlphaAndLight(UInt light_dir, Flt density_factor)
  1702. {
  1703. IMAGE_MODE mode=T.mode();
  1704. if(type()!=IMAGE_L8A8)
  1705. {
  1706. Image temp;
  1707. if(temp.createTry(w(), h(), d(), IMAGE_L8A8, mode , mipMaps(), false)
  1708. || temp.createTry(w(), h(), d(), IMAGE_L8A8, IMAGE_SOFT, 1 , false)) // DX10+ doesn't support L8A8 so create in software
  1709. if(temp.lock(LOCK_WRITE))
  1710. if(lockRead())
  1711. {
  1712. REPD(z, T.d())
  1713. REPD(y, T.h())
  1714. REPD(x, T.w())temp.color3D(x, y, z, Color(0, 0, 0, color3D(x, y, z).a));
  1715. Swap(unlock(), temp.unlock());
  1716. }
  1717. }
  1718. if(hwType()==IMAGE_L8A8)
  1719. {
  1720. if(lock())
  1721. {
  1722. Int X=w(),
  1723. Y=h(),
  1724. Z=d();
  1725. UInt _Y=pitch (),
  1726. _Z=pitch2();
  1727. Flt a_mul=density_factor/255;
  1728. if(!light_dir)
  1729. {
  1730. REPD(z, Z)
  1731. REPD(y, Y)
  1732. REPD(x, X)_data[(x*2) + y*_Y + z*_Z]=0;
  1733. }else
  1734. if(light_dir==(DIRF_LEFT|DIRF_RIGHT|DIRF_DOWN|DIRF_UP|DIRF_BACK|DIRF_FORWARD))
  1735. {
  1736. REPD(z, Z)
  1737. REPD(y, Y)
  1738. REPD(x, X)_data[(x*2) + y*_Y + z*_Z]=255;
  1739. }else
  1740. if(IsPow2(X) && IsPow2(Y) && IsPow2(Z))
  1741. {
  1742. UInt X1=X-1,
  1743. Y1=Y-1,
  1744. Z1=Z-1;
  1745. switch(light_dir)
  1746. {
  1747. #define A(x, y, z) (((x)<<1) + (y)*_Y + (z)*_Z)
  1748. // down
  1749. case DIRF_LEFT |DIRF_DOWN|DIRF_FORWARD: REPD(z,Z)REPD(x,X){Flt l=255; REPD(y,Y){UInt i=A((x+y)&X1, y, (z-y)&Z1); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1750. case DIRF_DOWN|DIRF_FORWARD: REPD(z,Z)REPD(x,X){Flt l=255; REPD(y,Y){UInt i=A( x , y, (z-y)&Z1); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1751. case DIRF_RIGHT|DIRF_DOWN|DIRF_FORWARD: REPD(z,Z)REPD(x,X){Flt l=255; REPD(y,Y){UInt i=A((x-y)&X1, y, (z-y)&Z1); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1752. case DIRF_LEFT |DIRF_DOWN : REPD(z,Z)REPD(x,X){Flt l=255; REPD(y,Y){UInt i=A((x+y)&X1, y, z ); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1753. case DIRF_DOWN : REPD(z,Z)REPD(x,X){Flt l=255; REPD(y,Y){UInt i=A( x , y, z ); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1754. case DIRF_RIGHT|DIRF_DOWN : REPD(z,Z)REPD(x,X){Flt l=255; REPD(y,Y){UInt i=A((x-y)&X1, y, z ); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1755. case DIRF_LEFT |DIRF_DOWN|DIRF_BACK : REPD(z,Z)REPD(x,X){Flt l=255; REPD(y,Y){UInt i=A((x+y)&X1, y, (z+y)&Z1); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1756. case DIRF_DOWN|DIRF_BACK : REPD(z,Z)REPD(x,X){Flt l=255; REPD(y,Y){UInt i=A( x , y, (z+y)&Z1); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1757. case DIRF_RIGHT|DIRF_DOWN|DIRF_BACK : REPD(z,Z)REPD(x,X){Flt l=255; REPD(y,Y){UInt i=A((x-y)&X1, y, (z+y)&Z1); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1758. // up
  1759. case DIRF_LEFT |DIRF_UP |DIRF_FORWARD: REPD(z,Z)REPD(x,X){Flt l=255; FREPD(y,Y){UInt i=A((x-y)&X1, y, (z+y)&Z1); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1760. case DIRF_UP |DIRF_FORWARD: REPD(z,Z)REPD(x,X){Flt l=255; FREPD(y,Y){UInt i=A( x , y, (z+y)&Z1); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1761. case DIRF_RIGHT|DIRF_UP |DIRF_FORWARD: REPD(z,Z)REPD(x,X){Flt l=255; FREPD(y,Y){UInt i=A((x+y)&X1, y, (z+y)&Z1); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1762. case DIRF_LEFT |DIRF_UP : REPD(z,Z)REPD(x,X){Flt l=255; FREPD(y,Y){UInt i=A((x-y)&X1, y, z ); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1763. case DIRF_UP : REPD(z,Z)REPD(x,X){Flt l=255; FREPD(y,Y){UInt i=A( x , y, z ); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1764. case DIRF_RIGHT|DIRF_UP : REPD(z,Z)REPD(x,X){Flt l=255; FREPD(y,Y){UInt i=A((x+y)&X1, y, z ); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1765. case DIRF_LEFT |DIRF_UP |DIRF_BACK : REPD(z,Z)REPD(x,X){Flt l=255; FREPD(y,Y){UInt i=A((x-y)&X1, y, (z-y)&Z1); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1766. case DIRF_UP |DIRF_BACK : REPD(z,Z)REPD(x,X){Flt l=255; FREPD(y,Y){UInt i=A( x , y, (z-y)&Z1); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1767. case DIRF_RIGHT|DIRF_UP |DIRF_BACK : REPD(z,Z)REPD(x,X){Flt l=255; FREPD(y,Y){UInt i=A((x+y)&X1, y, (z-y)&Z1); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1768. // horizontal
  1769. case DIRF_LEFT : REPD(z,Z)REPD(y,Y){Flt l=255; REPD(x,X){UInt i=A( x , y, z ); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1770. case DIRF_LEFT |DIRF_FORWARD: REPD(z,Z)REPD(y,Y){Flt l=255; REPD(x,X){UInt i=A( x , y, (z-x)&Z1); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1771. case DIRF_LEFT |DIRF_BACK : REPD(z,Z)REPD(y,Y){Flt l=255; REPD(x,X){UInt i=A( x , y, (z+x)&Z1); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1772. case DIRF_RIGHT : REPD(z,Z)REPD(y,Y){Flt l=255; FREPD(x,X){UInt i=A( x , y, z ); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1773. case DIRF_RIGHT |DIRF_FORWARD: REPD(z,Z)REPD(y,Y){Flt l=255; FREPD(x,X){UInt i=A( x , y, (z+x)&Z1); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1774. case DIRF_RIGHT |DIRF_BACK : REPD(z,Z)REPD(y,Y){Flt l=255; FREPD(x,X){UInt i=A( x , y, (z-x)&Z1); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1775. #undef A
  1776. }
  1777. }else
  1778. {
  1779. switch(light_dir)
  1780. {
  1781. #define A(x, y, z) ((Mod(x,X)<<1) + (y)*_Y + Mod(z,Z)*_Z)
  1782. case DIRF_LEFT |DIRF_DOWN|DIRF_FORWARD: REPD(z,Z)REPD(x,X){Flt l=255; REPD(y,Y){UInt i=A(x+y, y, z-y); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1783. case DIRF_DOWN|DIRF_FORWARD: REPD(z,Z)REPD(x,X){Flt l=255; REPD(y,Y){UInt i=A(x , y, z-y); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1784. case DIRF_RIGHT|DIRF_DOWN|DIRF_FORWARD: REPD(z,Z)REPD(x,X){Flt l=255; REPD(y,Y){UInt i=A(x-y, y, z-y); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1785. case DIRF_LEFT |DIRF_DOWN : REPD(z,Z)REPD(x,X){Flt l=255; REPD(y,Y){UInt i=A(x+y, y, z ); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1786. case DIRF_DOWN : REPD(z,Z)REPD(x,X){Flt l=255; REPD(y,Y){UInt i=A(x , y, z ); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1787. case DIRF_RIGHT|DIRF_DOWN : REPD(z,Z)REPD(x,X){Flt l=255; REPD(y,Y){UInt i=A(x-y, y, z ); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1788. case DIRF_LEFT |DIRF_DOWN|DIRF_BACK : REPD(z,Z)REPD(x,X){Flt l=255; REPD(y,Y){UInt i=A(x+y, y, z+y); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1789. case DIRF_DOWN|DIRF_BACK : REPD(z,Z)REPD(x,X){Flt l=255; REPD(y,Y){UInt i=A(x , y, z+y); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1790. case DIRF_RIGHT|DIRF_DOWN|DIRF_BACK : REPD(z,Z)REPD(x,X){Flt l=255; REPD(y,Y){UInt i=A(x-y, y, z+y); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1791. // up
  1792. case DIRF_LEFT |DIRF_UP |DIRF_FORWARD: REPD(z,Z)REPD(x,X){Flt l=255; FREPD(y,Y){UInt i=A(x-y, y, z+y); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1793. case DIRF_UP |DIRF_FORWARD: REPD(z,Z)REPD(x,X){Flt l=255; FREPD(y,Y){UInt i=A(x , y, z+y); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1794. case DIRF_RIGHT|DIRF_UP |DIRF_FORWARD: REPD(z,Z)REPD(x,X){Flt l=255; FREPD(y,Y){UInt i=A(x+y, y, z+y); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1795. case DIRF_LEFT |DIRF_UP : REPD(z,Z)REPD(x,X){Flt l=255; FREPD(y,Y){UInt i=A(x-y, y, z ); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1796. case DIRF_UP : REPD(z,Z)REPD(x,X){Flt l=255; FREPD(y,Y){UInt i=A(x , y, z ); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1797. case DIRF_RIGHT|DIRF_UP : REPD(z,Z)REPD(x,X){Flt l=255; FREPD(y,Y){UInt i=A(x+y, y, z ); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1798. case DIRF_LEFT |DIRF_UP |DIRF_BACK : REPD(z,Z)REPD(x,X){Flt l=255; FREPD(y,Y){UInt i=A(x-y, y, z-y); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1799. case DIRF_UP |DIRF_BACK : REPD(z,Z)REPD(x,X){Flt l=255; FREPD(y,Y){UInt i=A(x , y, z-y); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1800. case DIRF_RIGHT|DIRF_UP |DIRF_BACK : REPD(z,Z)REPD(x,X){Flt l=255; FREPD(y,Y){UInt i=A(x+y, y, z-y); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1801. // horizontal
  1802. case DIRF_LEFT : REPD(z,Z)REPD(y,Y){Flt l=255; REPD(x,X){UInt i=A(x , y, z ); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1803. case DIRF_LEFT |DIRF_FORWARD: REPD(z,Z)REPD(y,Y){Flt l=255; REPD(x,X){UInt i=A(x , y, z-x); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1804. case DIRF_LEFT |DIRF_BACK : REPD(z,Z)REPD(y,Y){Flt l=255; REPD(x,X){UInt i=A(x , y, z+x); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1805. case DIRF_RIGHT : REPD(z,Z)REPD(y,Y){Flt l=255; FREPD(x,X){UInt i=A(x , y, z ); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1806. case DIRF_RIGHT |DIRF_FORWARD: REPD(z,Z)REPD(y,Y){Flt l=255; FREPD(x,X){UInt i=A(x , y, z+x); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1807. case DIRF_RIGHT |DIRF_BACK : REPD(z,Z)REPD(y,Y){Flt l=255; FREPD(x,X){UInt i=A(x , y, z-x); _data[i]=Round(l); l*=1-Sat(_data[i+1]*a_mul);}} break;
  1808. #undef A
  1809. }
  1810. }
  1811. unlock().updateMipMaps();
  1812. }
  1813. if(T.mode()!=mode)
  1814. {
  1815. Image temp;
  1816. if(temp.createTry(w(), h(), d(), type(), mode, 1))
  1817. if(temp.lock(LOCK_WRITE))
  1818. if(lockRead())
  1819. {
  1820. REPD(z, T.d())
  1821. REPD(y, T.h())
  1822. REPD(x, T.w())temp.color3D(x, y, z, color3D(x, y, z));
  1823. Swap(unlock(), temp.unlock());
  1824. }
  1825. }
  1826. }
  1827. return T;
  1828. }
  1829. /******************************************************************************
  1830. Image& fadeMipMaps(); // fade out mip maps, the smaller the mip map the more faded it will be
  1831. Image& Image::fadeMipMaps()
  1832. {
  1833. if(mode()==IMAGE_2D)
  1834. {
  1835. Image temp;
  1836. FREP(mipMaps())
  1837. {
  1838. Flt s;
  1839. Int mip_size=Max(w()>>i, h()>>i);
  1840. if( mip_size>=256)s=1.00f;else
  1841. if( mip_size>=128)s=0.66f;else
  1842. if( mip_size>= 64)s=0.33f;else
  1843. s=0.00f;
  1844. if(s<1)
  1845. {
  1846. Bool copy=compressed(); // if the image is compressed then operate on mip extracted to copy image
  1847. if( copy)extractMipMap(temp, IMAGE_R8G8B8A8, IMAGE_SOFT, i);
  1848. Image &image=(copy ? temp : T);
  1849. if(image.lock(LOCK_READ_WRITE, copy ? 0 : i))
  1850. {
  1851. REPD(y, image.lh())
  1852. REPD(x, image.lw())image.color(x, y, Lerp(Color(128, 128, 128, 128), image.color(x, y), s));
  1853. image.unlock();
  1854. if(copy)injectMipMap(temp, i);
  1855. }
  1856. }
  1857. }
  1858. }
  1859. return T;
  1860. }
  1861. /******************************************************************************/
  1862. Image& Image::tile(Int range, Bool horizontally, Bool vertically)
  1863. {
  1864. IMAGE_TYPE type;
  1865. IMAGE_MODE mode;
  1866. Int mip_maps;
  1867. Clamp(range, 0, Min(w(), h()));
  1868. if(range && (horizontally || vertically) && Decompress(T, type, mode, mip_maps) && lock())
  1869. {
  1870. #if 1
  1871. if(highPrecision())
  1872. {
  1873. const Flt mul=1.0f/(range+1);
  1874. if(horizontally)
  1875. FREPD(y, T.h())
  1876. FREPD(x, range)
  1877. {
  1878. Vec4 c0=colorF( x, y),
  1879. c1=colorF(T.w()-x-1, y);
  1880. colorF(T.w()-x-1, y, Lerp(c0, c1, (x+1)*mul));
  1881. }
  1882. if(vertically)
  1883. FREPD(x, T.w())
  1884. FREPD(y, range)
  1885. {
  1886. Vec4 c0=colorF(x, y),
  1887. c1=colorF(x, T.h()-y-1);
  1888. colorF(x, T.h()-y-1, Lerp(c0, c1, (y+1)*mul));
  1889. }
  1890. }else
  1891. {
  1892. const UInt div=range+1, div_2=(div+1)/2;
  1893. if(horizontally)
  1894. FREPD(y, T.h())
  1895. FREPD(x, range)
  1896. {
  1897. UInt s0=range-x,
  1898. s1=x+1;
  1899. Color c0=color( x, y),
  1900. c1=color(T.w()-x-1, y), c;
  1901. c.r=(c0.r*s0 + c1.r*s1 + div_2)/div;
  1902. c.g=(c0.g*s0 + c1.g*s1 + div_2)/div;
  1903. c.b=(c0.b*s0 + c1.b*s1 + div_2)/div;
  1904. c.a=(c0.a*s0 + c1.a*s1 + div_2)/div;
  1905. color(T.w()-x-1, y, c);
  1906. }
  1907. if(vertically)
  1908. FREPD(x, T.w())
  1909. FREPD(y, range)
  1910. {
  1911. UInt s0=range-y,
  1912. s1=y+1;
  1913. Color c0=color(x, y),
  1914. c1=color(x, T.h()-y-1), c;
  1915. c.r=(c0.r*s0 + c1.r*s1 + div_2)/div;
  1916. c.g=(c0.g*s0 + c1.g*s1 + div_2)/div;
  1917. c.b=(c0.b*s0 + c1.b*s1 + div_2)/div;
  1918. c.a=(c0.a*s0 + c1.a*s1 + div_2)/div;
  1919. color(x, T.h()-y-1, c);
  1920. }
  1921. }
  1922. #else
  1923. if(horizontally)
  1924. FREPD(y, T.h())
  1925. FREPD(x, range)
  1926. {
  1927. UInt s0 =x+1,
  1928. s1 =range-x,
  1929. sum =s0+s1,
  1930. sum_2=(sum+1)/2;
  1931. Color c0 =color( x, y),
  1932. c1 =color(T.w()-range+x, y), c;
  1933. c.a=(c0.a*s0 + c1.a*s1 + sum_2)/sum;
  1934. c.r=(c0.r*s0 + c1.r*s1 + sum_2)/sum;
  1935. c.g=(c0.g*s0 + c1.g*s1 + sum_2)/sum;
  1936. c.b=(c0.b*s0 + c1.b*s1 + sum_2)/sum;
  1937. color(T.w()-range+x, y, c);
  1938. }
  1939. if(vertically)
  1940. FREPD(x, T.w())
  1941. FREPD(y, range)
  1942. {
  1943. UInt s0 =y+1,
  1944. s1 =range-y,
  1945. sum =s0+s1,
  1946. sum_2=(sum+1)/2;
  1947. Color c0 =color(x, y),
  1948. c1 =color(x, T.h()-range+y), c;
  1949. c.a=(c0.a*s0 + c1.a*s1 + sum_2)/sum;
  1950. c.r=(c0.r*s0 + c1.r*s1 + sum_2)/sum;
  1951. c.g=(c0.g*s0 + c1.g*s1 + sum_2)/sum;
  1952. c.b=(c0.b*s0 + c1.b*s1 + sum_2)/sum;
  1953. color(x, T.h()-range+y, c);
  1954. }
  1955. #endif
  1956. unlock().updateMipMaps(FILTER_BEST, false);
  1957. Compress(T, type, mode, mip_maps);
  1958. }
  1959. return T;
  1960. }
  1961. /******************************************************************************/
  1962. // MIN / MAX
  1963. /******************************************************************************/
  1964. static Flt Median(Long (&v)[256], Long p, Bool exact)
  1965. {
  1966. Long n=0; FREPA(v)
  1967. {
  1968. n+=v[i]; if(n>p)
  1969. {
  1970. if(exact || n>p+1)return i/255.0f; // if exact, or the next value is still the same value (we use histogram, so we can just check if from "v[i]" we've accumulated 1 more than needed, this is OK)
  1971. // find next value
  1972. Int j=i+1; for(; j<255 && !v[j]; j++); // 255 instead of 256, because we always want to stop on the last element
  1973. return (i+j)/(255.0f*2); // return average of 'i' and 'j'
  1974. }
  1975. }
  1976. return 1;
  1977. }
  1978. Bool Image::stats(Vec4 *min, Vec4 *max, Vec4 *avg, Vec4 *med, Vec4 *mod, Vec *avg_alpha_weight, C BoxI *box_ptr)C
  1979. {
  1980. if(!min && !max && !avg && !med && !mod && !avg_alpha_weight)return true; // nothing to calculate
  1981. if(is())
  1982. {
  1983. C Image *src=this; Image temp;
  1984. if(compressed())
  1985. if(copyTry(temp, -1, -1, -1, IMAGE_R8G8B8A8, IMAGE_SOFT, 1))src=&temp;else goto error;
  1986. if(src->lockRead())
  1987. {
  1988. VecD4 sum=0, sum_alpha_weight=0;
  1989. Vec4 _avg, _med;
  1990. Long r[256], g[256], b[256], a[256]; // channel 8-bit histogram
  1991. Flt *rf, *gf, *bf, *af;
  1992. Memt<Flt> hist; // channels are listed separately, all reds, followed by all greens, followed by all blues, followed by all alphas
  1993. if(mod) // to calculate mode, we need to have average and median
  1994. { // if not specified then operate on temp
  1995. if(!avg)avg=&_avg;
  1996. if(!med)med=&_med;
  1997. }
  1998. if(min)*min= FLT_MAX;
  1999. if(max)*max=-FLT_MAX;
  2000. Bool med_fast=!src->highPrecision(); // calculate median using fast method (8-bit histogram)
  2001. BoxI box(0, src->size3()); if(box_ptr)box&=*box_ptr;
  2002. Long pixels=box.volumeL();
  2003. if(med)
  2004. {
  2005. if(med_fast)
  2006. {
  2007. Zero(r); Zero(g); Zero(b); Zero(a);
  2008. }else
  2009. {
  2010. hist.setNum(pixels*4); // 4 channels
  2011. rf=hist.data();
  2012. gf=rf+pixels;
  2013. bf=gf+pixels;
  2014. af=bf+pixels;
  2015. }
  2016. }
  2017. for(Int z=box.min.z; z<box.max.z; z++)
  2018. for(Int y=box.min.y; y<box.max.y; y++)
  2019. for(Int x=box.min.x; x<box.max.x; x++)
  2020. {
  2021. Vec4 color=src->color3DF(x, y, z);
  2022. if(min)*min =Min(*min, color);
  2023. if(max)*max =Max(*max, color);
  2024. if(avg) sum+=VecD4(color);
  2025. if(avg_alpha_weight){sum_alpha_weight.xyz+=color.xyz*color.w; sum_alpha_weight.w+=color.w;}
  2026. if(med)
  2027. {
  2028. if(med_fast)
  2029. {
  2030. r[FltToByte(color.x)]++;
  2031. g[FltToByte(color.y)]++;
  2032. b[FltToByte(color.z)]++;
  2033. a[FltToByte(color.w)]++;
  2034. }else
  2035. {
  2036. *rf++=color.x;
  2037. *gf++=color.y;
  2038. *bf++=color.z;
  2039. *af++=color.w;
  2040. }
  2041. }
  2042. }
  2043. src->unlock();
  2044. if(avg)*avg=sum/Dbl(pixels);
  2045. if(avg_alpha_weight){if(sum_alpha_weight.w)sum_alpha_weight.xyz/=sum_alpha_weight.w; *avg_alpha_weight=sum_alpha_weight.xyz;}
  2046. if(med)
  2047. {
  2048. Long p=Unsigned(pixels)/2;
  2049. Bool exact=pixels&1;
  2050. if(med_fast)
  2051. {
  2052. p-=!exact; // for not exact (average) we need to stop at 1 before, and average it with the next one, alternatively we could change 'Median' to find previous and not next value, however that would require 1 more iteration, so it's faster to just do this 1 time, instead of 1 iteration in each call
  2053. med->set(Median(r, p, exact), Median(g, p, exact), Median(b, p, exact), Median(a, p, exact));
  2054. }else
  2055. {
  2056. // go back at the start of the array, and sort
  2057. rf-=pixels; Sort(rf, pixels);
  2058. gf-=pixels; Sort(gf, pixels);
  2059. bf-=pixels; Sort(bf, pixels);
  2060. af-=pixels; Sort(af, pixels);
  2061. if(exact)med->set(rf[p], gf[p], bf[p], af[p]);else
  2062. {
  2063. Long q=p-1; med->set(Avg(rf[q], rf[p]), Avg(gf[q], gf[p]), Avg(bf[q], bf[p]), Avg(af[q], af[p]));
  2064. }
  2065. }
  2066. }
  2067. if(mod)*mod=3*(*med) - 2*(*avg); // Mode = 3*Median - 2*Mean
  2068. return true;
  2069. }
  2070. }
  2071. error:
  2072. if(min)min->zero();
  2073. if(max)max->zero();
  2074. if(avg)avg->zero();
  2075. if(med)med->zero();
  2076. if(mod)mod->zero();
  2077. if(avg_alpha_weight)avg_alpha_weight->zero();
  2078. return false;
  2079. }
  2080. Bool Image::statsSat(Flt *min, Flt *max, Flt *avg, Flt *med, Flt *mod, Flt *avg_alpha_weight, C BoxI *box_ptr)C
  2081. {
  2082. if(!min && !max && !avg && !med && !mod && !avg_alpha_weight)return true; // nothing to calculate
  2083. if(is())
  2084. {
  2085. C Image *src=this; Image temp;
  2086. if(compressed())
  2087. if(copyTry(temp, -1, -1, -1, IMAGE_R8G8B8, IMAGE_SOFT, 1))src=&temp;else goto error;
  2088. if(src->lockRead())
  2089. {
  2090. Dbl sum=0; Vec2 sum_alpha_weight=0;
  2091. Flt _avg, _med;
  2092. Long v[256]; // 8-bit histogram
  2093. Flt *vf;
  2094. Memt<Flt> hist;
  2095. if(mod) // to calculate mode, we need to have average and median
  2096. { // if not specified then operate on temp
  2097. if(!avg)avg=&_avg;
  2098. if(!med)med=&_med;
  2099. }
  2100. if(min)*min= FLT_MAX;
  2101. if(max)*max=-FLT_MAX;
  2102. Bool med_fast=!src->highPrecision(); // calculate median using fast method (8-bit histogram)
  2103. BoxI box(0, src->size3()); if(box_ptr)box&=*box_ptr;
  2104. Long pixels=box.volumeL();
  2105. if(med)
  2106. {
  2107. if(med_fast)Zero(v);else vf=hist.setNum(pixels).data();
  2108. }
  2109. for(Int z=box.min.z; z<box.max.z; z++)
  2110. for(Int y=box.min.y; y<box.max.y; y++)
  2111. for(Int x=box.min.x; x<box.max.x; x++)
  2112. {
  2113. Vec4 c=src->color3DF(x, y, z);
  2114. Flt s=RgbToHsb(c.xyz).y;
  2115. if(min)MIN(*min, s);
  2116. if(max)MAX(*max, s);
  2117. if(avg)sum+=s;
  2118. if(avg_alpha_weight){sum_alpha_weight.x+=s*c.w; sum_alpha_weight.y+=c.w;}
  2119. if(med)
  2120. {
  2121. if(med_fast)v[FltToByte(s)]++;
  2122. else *vf++=s;
  2123. }
  2124. }
  2125. src->unlock();
  2126. if(avg)*avg=sum/Dbl(pixels);
  2127. if(avg_alpha_weight){if(sum_alpha_weight.y)sum_alpha_weight.x/=sum_alpha_weight.y; *avg_alpha_weight=sum_alpha_weight.x;}
  2128. if(med)
  2129. {
  2130. Long p=Unsigned(pixels)/2;
  2131. Bool exact=pixels&1;
  2132. if(med_fast)
  2133. {
  2134. p-=!exact; // for not exact (average) we need to stop at 1 before, and average it with the next one, alternatively we could change 'Median' to find previous and not next value, however that would require 1 more iteration, so it's faster to just do this 1 time, instead of 1 iteration in each call
  2135. *med=Median(v, p, exact);
  2136. }else
  2137. {
  2138. // go back at the start of the array, and sort
  2139. vf-=pixels; Sort(vf, pixels);
  2140. *med=(exact ? vf[p] : Avg(vf[p-1], vf[p]));
  2141. }
  2142. }
  2143. if(mod)*mod=3*(*med) - 2*(*avg); // Mode = 3*Median - 2*Mean
  2144. return true;
  2145. }
  2146. }
  2147. error:
  2148. if(min)*min=0;
  2149. if(max)*max=0;
  2150. if(avg)*avg=0;
  2151. if(med)*med=0;
  2152. if(mod)*mod=0;
  2153. if(avg_alpha_weight)*avg_alpha_weight=0;
  2154. return false;
  2155. }
  2156. /******************************************************************************/
  2157. Bool Image::monochromatic()C
  2158. {
  2159. if(ImageTI[type()].channels==1 || type()==IMAGE_L8A8)return true;
  2160. C Image *src =this; Image temp; if(compressed())if(src->copyTry(temp, -1, -1, -1, IMAGE_R8G8B8A8, IMAGE_SOFT, 1))src=&temp;else return false;
  2161. Bool mono=true;
  2162. if(src->lockRead())
  2163. {
  2164. REPD(z, src->ld())
  2165. REPD(y, src->lh())
  2166. REPD(x, src->lw())
  2167. {
  2168. Color c=src->color3D(x, y, z); if(c.r!=c.g || c.r!=c.b){mono=false; goto stop;}
  2169. }
  2170. stop:
  2171. src->unlock();
  2172. }
  2173. return mono;
  2174. }
  2175. /******************************************************************************/
  2176. Image& Image::minimum(Flt distance)
  2177. {
  2178. IMAGE_TYPE type;
  2179. IMAGE_MODE mode;
  2180. Int mip_maps;
  2181. SAT(distance);
  2182. if (distance>EPS && Decompress(T, type, mode, mip_maps))
  2183. {
  2184. Image temp(w(), h(), 1, T.type(), T.mode(), (T.type()!=type || T.mode()!=mode) ? 1 : mip_maps);
  2185. if(temp.lock(LOCK_WRITE))
  2186. {
  2187. if(lockRead())
  2188. {
  2189. REPD(y, T.h())
  2190. REPD(x, T.w())
  2191. {
  2192. Vec4 c=T.colorF(x, y);
  2193. for(Int sy=-1; sy<=1; sy++)if(InRange(y+sy, T.h()))
  2194. for(Int sx=-1; sx<=1; sx++)if(InRange(x+sx, T.w()) && (sx || sy))
  2195. {
  2196. Vec2 o(sx, sy); o.setLength(distance);
  2197. Vec4 t=colorFCubicFast(x+o.x, y+o.y, true);
  2198. MIN(c.x, t.x);
  2199. MIN(c.y, t.y);
  2200. MIN(c.z, t.z);
  2201. MIN(c.w, t.w);
  2202. }
  2203. temp.colorF(x, y, c);
  2204. }
  2205. unlock();
  2206. }
  2207. temp.unlock();
  2208. }
  2209. Swap(temp.updateMipMaps(), T);
  2210. Compress(T, type, mode, mip_maps);
  2211. }
  2212. return T;
  2213. }
  2214. /******************************************************************************/
  2215. Image& Image::maximum(Flt distance)
  2216. {
  2217. IMAGE_TYPE type;
  2218. IMAGE_MODE mode;
  2219. Int mip_maps;
  2220. SAT(distance);
  2221. if (distance>EPS && Decompress(T, type, mode, mip_maps))
  2222. {
  2223. Image temp(w(), h(), 1, T.type(), T.mode(), (T.type()!=type || T.mode()!=mode) ? 1 : mip_maps);
  2224. if(temp.lock(LOCK_WRITE))
  2225. {
  2226. if(lockRead())
  2227. {
  2228. REPD(y, T.h())
  2229. REPD(x, T.w())
  2230. {
  2231. Vec4 c=T.colorF(x, y);
  2232. for(Int sy=-1; sy<=1; sy++)if(InRange(y+sy, T.h()))
  2233. for(Int sx=-1; sx<=1; sx++)if(InRange(x+sx, T.w()) && (sx || sy))
  2234. {
  2235. Vec2 o(sx, sy); o.setLength(distance);
  2236. Vec4 t=T.colorFLinear(x+o.x, y+o.y, true);
  2237. MAX(c.x, t.x);
  2238. MAX(c.y, t.y);
  2239. MAX(c.z, t.z);
  2240. MAX(c.w, t.w);
  2241. }
  2242. temp.colorF(x, y, c);
  2243. }
  2244. unlock();
  2245. }
  2246. temp.unlock();
  2247. }
  2248. Swap(temp.updateMipMaps(), T);
  2249. Compress(T, type, mode, mip_maps);
  2250. }
  2251. return T;
  2252. }
  2253. /******************************************************************************/
  2254. Image& Image::transparentToNeighbor(Bool clamp, Flt step)
  2255. {
  2256. #if 1 // new method
  2257. Int mips=TotalMipMaps(w(), h(), d(), IMAGE_F32_4); if(mips<=1)return T;//true;
  2258. Image *src =this, temp; if(!src->highPrecision() || src->compressed())if(src->copyTry(temp, -1, -1, -1, IMAGE_F32_4, IMAGE_SOFT, 1))src=&temp;else return T;//false; // first we have to copy to IMAGE_F32_4 to make sure we have floating point, so that downsizing will not use ALPHA_LIMIT, this is absolutely critical
  2259. Bool ok =false;
  2260. if(src->lock())
  2261. {
  2262. Memt<Image> mip; mip.setNum(mips-1);
  2263. Image *s=src; VecI size=s->size3();
  2264. SAT(step); Bool lerp=(step!=1);
  2265. FREPA(mip)
  2266. {
  2267. size>>=1;
  2268. if(!s->copyTry(mip[i], size.x, size.y, size.z, IMAGE_F32_4, IMAGE_SOFT, 1, FILTER_CUBIC_FAST_SMOOTH, clamp, true))goto error; // we need a non-sharpening filter and one that spreads in all directions (more than 2x2 samples)
  2269. s=&mip[i];
  2270. }
  2271. REPD(y, h())
  2272. REPD(x, w())
  2273. {
  2274. Vec4 s=src->colorF(x, y); if(!s.w)FREPA(mip) // if transparent, then iterate all mip maps starting from the biggest
  2275. {
  2276. Image &m=mip[i];
  2277. Vec2 x_mul_add; x_mul_add.x=Flt(m.w())/w(); x_mul_add.y=x_mul_add.x*0.5f-0.5f;
  2278. Vec2 y_mul_add; y_mul_add.x=Flt(m.h())/h(); y_mul_add.y=y_mul_add.x*0.5f-0.5f;
  2279. //Vec2 z_mul_add; z_mul_add.x=Flt(m.d())/d(); z_mul_add.y=z_mul_add.x*0.5f-0.5f;
  2280. Vec4 c=m.colorFLinearTTNF32_4(x*x_mul_add.x + x_mul_add.y, y*y_mul_add.x + y_mul_add.y, clamp);
  2281. //Vec4 c=m.colorFCubicFastSmooth(x*x_mul_add.x + x_mul_add.y, y*y_mul_add.x + y_mul_add.y, clamp);
  2282. if(c.w) // if we've found some valid color value
  2283. {
  2284. c.w=0; // remember to clear alpha to zero to preserve original transparency, but we keep the new RGB values
  2285. if(lerp)c.xyz=Lerp(s.xyz, c.xyz, step);
  2286. src->colorF(x, y, c);
  2287. break; // finished looking for a sample
  2288. }
  2289. }
  2290. }
  2291. ok=true;
  2292. error:;
  2293. src->unlock();
  2294. if(ok)if(src==this)src->updateMipMaps(FILTER_BEST, clamp, true);else ok=src->copyTry(T, -1, -1, -1, type(), mode(), mipMaps(), FILTER_BEST, clamp, true);
  2295. }
  2296. return T;//ok;
  2297. #else // old method
  2298. IMAGE_TYPE type;
  2299. IMAGE_MODE mode;
  2300. Int mip_maps;
  2301. if(Decompress(T, type, mode, mip_maps))
  2302. {
  2303. if(lock())
  2304. {
  2305. Image used(w(), h(), 1, IMAGE_I8, IMAGE_SOFT, 1); used.clear();
  2306. Memt<VecI2> opn, opn_next; // coordinates of transparent pixel which has opaque neighbor
  2307. // iterate all pixels
  2308. REPD(y, T.h())
  2309. REPD(x, T.w())if(color(x, y).a)used.pixB(x, y)=1;else
  2310. {
  2311. // iterate all neighbors
  2312. for(Int sy=y-1; sy<=y+1; sy++)if(InRange(sy, T.h()))
  2313. for(Int sx=x-1; sx<=x+1; sx++)if(InRange(sx, T.w()))if(color(sx, sy).a) // if at least one neighbor has alpha
  2314. {
  2315. used.pixB(x, y)=2; opn.New().set(x, y); goto added;
  2316. }
  2317. added:;
  2318. }
  2319. for(Byte prev_step=1, cur_step=2; opn.elms(); )
  2320. {
  2321. Byte next_step=(cur_step+1)&0xFF; if(!next_step)next_step=1; // set next step, and make sure to skip '0' which is used for "not yet set"
  2322. REPA(opn)
  2323. {
  2324. VecI2 p=opn[i]; opn.removeLast();
  2325. VecI4 sum=0;
  2326. for(Int sy=p.y-1; sy<=p.y+1; sy++)if(InRange(sy, T.h()))
  2327. for(Int sx=p.x-1; sx<=p.x+1; sx++)if(InRange(sx, T.w()))
  2328. {
  2329. Byte &u=used.pixB(sx, sy);
  2330. if(u==prev_step) // was set in previous frame
  2331. {
  2332. Color c=T.color(sx, sy);
  2333. Int a=c.a+1; if(sy==p.y || sx==p.x)a*=2; // make horizontal/vertical neighbors 2x more significant, and thus making corner neighbors 2x less
  2334. sum.x+=c.r*a;
  2335. sum.y+=c.g*a;
  2336. sum.z+=c.b*a;
  2337. sum.w+= a;
  2338. }else
  2339. if(!u) // if not yet added
  2340. {
  2341. u=next_step; // set as already added
  2342. opn_next.New().set(sx, sy); // add to next list of pixels
  2343. }
  2344. }
  2345. Int h=sum.w/2; // half
  2346. T.color(p.x, p.y, sum.w ? Color((sum.x+h)/sum.w, (sum.y+h)/sum.w, (sum.z+h)/sum.w, 0) : TRANSPARENT);
  2347. }
  2348. prev_step=cur_step; cur_step=next_step;
  2349. Swap(opn, opn_next);
  2350. }
  2351. unlock().updateMipMaps();
  2352. }
  2353. Compress(T, type, mode, mip_maps);
  2354. }
  2355. return T;
  2356. #endif
  2357. }
  2358. static VecI2 move[8]=
  2359. {
  2360. VecI2( 0, 1),
  2361. VecI2( 0,-1),
  2362. VecI2( 1, 0),
  2363. VecI2(-1, 0),
  2364. VecI2( 1, 1),
  2365. VecI2( 1,-1),
  2366. VecI2(-1, 1),
  2367. VecI2(-1,-1),
  2368. };
  2369. Bool Image::getSameColorNeighbors(Int x, Int y, MemPtr<VecI2> pixels, Bool diagonal)C
  2370. {
  2371. pixels.clear();
  2372. if(InRange(x, w())
  2373. && InRange(y, h()))
  2374. {
  2375. C Image *src=this;
  2376. Image temp;
  2377. if(compressed()){if(!copyTry(temp, -1, -1, -1, IMAGE_R8G8B8A8, IMAGE_SOFT, 1))return false; src=&temp;}
  2378. if(src->lockRead())
  2379. {
  2380. const Int moves=(diagonal ? 8 : 4);
  2381. Image used(w(), h(), 1, IMAGE_I8, IMAGE_SOFT, 1); used.clear().pixel(x, y, 1);
  2382. Color color=src->color(x, y);
  2383. pixels.add(VecI2(x, y));
  2384. FREPAD(processed, pixels)
  2385. {
  2386. VecI2 pos=pixels[processed]; // don't use reference because 'pixels' may get modified later
  2387. REP(moves)
  2388. {
  2389. VecI2 next=pos+move[i];
  2390. if(InRange(next.x, w())
  2391. && InRange(next.y, h()))
  2392. {
  2393. Byte &b=used.pixB(next.x, next.y); if(!b)
  2394. {
  2395. b=1;
  2396. if(src->color(next.x, next.y)==color)pixels.add(next);
  2397. }
  2398. }
  2399. }
  2400. }
  2401. src->unlock();
  2402. return true;
  2403. }
  2404. }
  2405. return false;
  2406. }
  2407. Image& Image::fill(Int x, Int y, C Color &color, Bool diagonal)
  2408. {
  2409. if(InRange(x, w())
  2410. && InRange(y, h()))
  2411. {
  2412. IMAGE_TYPE type;
  2413. IMAGE_MODE mode;
  2414. Int mip_maps;
  2415. if(Decompress(T, type, mode, mip_maps))
  2416. {
  2417. if(lock())
  2418. {
  2419. const Int moves=(diagonal ? 8 : 4);
  2420. Image used(w(), h(), 1, IMAGE_I8, IMAGE_SOFT, 1); used.clear().pixel(x, y, 1);
  2421. Color src=T.color(x, y);
  2422. Memt<VecI2> pixels; for(pixels.add(VecI2(x, y)); pixels.elms(); )
  2423. {
  2424. VecI2 pos=pixels.pop();
  2425. T.color(pos.x, pos.y, color);
  2426. REP(moves)
  2427. {
  2428. VecI2 next=pos+move[i];
  2429. if(InRange(next.x, w())
  2430. && InRange(next.y, h()))
  2431. {
  2432. Byte &b=used.pixB(next.x, next.y); if(!b)
  2433. {
  2434. b=1;
  2435. if(T.color(next.x, next.y)==src)pixels.add(next);
  2436. }
  2437. }
  2438. }
  2439. }
  2440. unlock().updateMipMaps();
  2441. }
  2442. Compress(T, type, mode, mip_maps);
  2443. }
  2444. }
  2445. return T;
  2446. }
  2447. /******************************************************************************/
  2448. // SHADOWS
  2449. /******************************************************************************/
  2450. Image& Image::createShadow(C Image &src, Int blur, Flt shadow_opacity, Flt shadow_spread, Bool border_padd)
  2451. {
  2452. MAX(blur, 0);
  2453. Clamp(shadow_opacity, 0, 1);
  2454. Clamp(shadow_spread , 0, 1); shadow_spread=1-shadow_spread; if(shadow_spread)shadow_spread=1/shadow_spread;
  2455. Int padd=blur+border_padd;
  2456. C Image *s=&src;
  2457. Image decompressed; if(s->compressed())if(s->copyTry(decompressed, -1, -1, -1, IMAGE_R8G8B8A8, IMAGE_SOFT, 1))s=&decompressed;else return T;
  2458. Image temp(s->w()+padd*2, s->h()+padd*2, 1, IMAGE_A8, IMAGE_SOFT, 1);
  2459. // copy
  2460. if(s->lockRead())
  2461. {
  2462. FREPD(y, temp.h())
  2463. FREPD(x, temp.w())temp.pixB(x, y)=s->color(x-padd, y-padd).a;
  2464. s->unlock();
  2465. }
  2466. // blur
  2467. temp.blur(blur, true);
  2468. // normalize
  2469. Int max=0; REPD(y, temp.h())REPD(x, temp.w())MAX(max, temp.pixB(x, y));
  2470. if( max )REPD(y, temp.h())REPD(x, temp.w())
  2471. {
  2472. Flt s=Flt(temp.pixB(x, y))/max*shadow_opacity;
  2473. if(shadow_spread)s*=shadow_spread;else if(s)s=1;
  2474. MIN(s, shadow_opacity);
  2475. temp.pixB(x, y)=RoundPos(s*255);
  2476. }
  2477. Swap(T, temp); return T;
  2478. }
  2479. Image& Image::applyShadow(C Image &shadow, C Color &shadow_color, C VecI2 &offset, Int image_type, Bool combine)
  2480. {
  2481. IMAGE_TYPE type;
  2482. IMAGE_MODE mode;
  2483. Int mip_maps;
  2484. if(shadow_color.a && Decompress(T, type, mode, mip_maps))
  2485. {
  2486. Int l=Max(0, -offset.x),
  2487. u=Max(0, -offset.y);
  2488. if( image_type<0)image_type=type; // get original
  2489. if(!image_type ) // auto-detect
  2490. {
  2491. if(ImageTI[type].a)image_type=type;else // if original type has alpha channel then use it
  2492. switch(type) // if not then manually specify the type
  2493. {
  2494. case IMAGE_A8 :
  2495. case IMAGE_L8 :
  2496. case IMAGE_I8 :
  2497. case IMAGE_I16:
  2498. case IMAGE_I24:
  2499. case IMAGE_I32: image_type=IMAGE_L8A8 ; break;
  2500. default : image_type=IMAGE_R8G8B8A8; break;
  2501. }
  2502. }
  2503. if(!ImageTI[image_type].a)return T; // no alpha available
  2504. Image temp(Max(w(), shadow.w()+offset.x)-Min(0, offset.x), Max(h(), shadow.h()+offset.y)-Min(0, offset.y), 1, ImageTI[image_type].compressed ? IMAGE_R8G8B8A8 : IMAGE_TYPE(image_type), IMAGE_SOFT, 1);
  2505. if(shadow.lockRead())
  2506. {
  2507. if(lockRead())
  2508. {
  2509. Color sc=shadow_color;
  2510. FREPD(y, temp.h())
  2511. FREPD(x, temp.w())
  2512. {
  2513. Color col= color(x-l , y-u );
  2514. Byte shd=shadow.pixel(x-l-offset.x, y-u-offset.y); sc.a=(shadow_color.a*shd+128)/255;
  2515. if(combine)temp.color(x, y, Blend(sc, col));else
  2516. {
  2517. col.a=sc.a;
  2518. temp.color(x, y, col);
  2519. }
  2520. }
  2521. unlock();
  2522. }
  2523. shadow.unlock();
  2524. }
  2525. temp.copyTry(temp, -1, -1, -1, IMAGE_TYPE(image_type), mode, mip_maps);
  2526. Swap(T, temp);
  2527. }
  2528. return T;
  2529. }
  2530. Image& Image::setShadow(Int blur, Flt shadow_opacity, Flt shadow_spread, Bool border_padd, C VecI2 &offset, Int image_type, Bool combine)
  2531. {
  2532. if(shadow_opacity>0)applyShadow(Image().createShadow(T, blur, shadow_opacity, shadow_spread, border_padd), BLACK, offset-blur-border_padd, image_type, combine);
  2533. return T;
  2534. }
  2535. /******************************************************************************/
  2536. Bool Image::raycast(C Vec &start, C Vec &move, C Matrix *image_matrix, Flt *hit_frac, Vec *hit_pos, Flt precision)C
  2537. {
  2538. Bool hit=false;
  2539. Vec s=start, m=move;
  2540. if(image_matrix)
  2541. {
  2542. s/=*image_matrix;
  2543. m/= image_matrix->orn();
  2544. }
  2545. Rect rect(0, 0, 1, 1);
  2546. Flt frac_start, frac_end;
  2547. if(SweepPointRect( s.xy , m.xy, rect, &frac_start)
  2548. && SweepPointRect((s.xy+m.xy), -m.xy, rect, &frac_end )) // 'frac_end' is frac from end position towards start, "1-frac_end" is frac from start towards end
  2549. if(lockRead())
  2550. {
  2551. Int w1=w()-1, h1=h()-1;
  2552. s.x*=w1; m.x*=w1;
  2553. s.y*=h1; m.y*=h1;
  2554. Vec pos =s+frac_start*m , // start clipped
  2555. m_clip=m*(1-frac_end-frac_start); // move clipped
  2556. Int steps =Max(1, RoundPos(m_clip.length()*precision)); m_clip/=steps;
  2557. Flt image_pos=pixelFLinear(pos.x, pos.y);
  2558. REP(steps)
  2559. {
  2560. Vec next=pos+m_clip;
  2561. Flt image_next=pixelFLinear(next.x, next.y);
  2562. if(pos.z>=image_pos && next.z<=image_next // current is above and next is below
  2563. || pos.z<=image_pos && next.z>=image_next) // current is below and next is above
  2564. {
  2565. hit=true;
  2566. if(hit_frac || hit_pos)
  2567. {
  2568. // X Y1 Y2
  2569. // 0 pos .z image_pos
  2570. // 1 next.z image_next
  2571. // Y1(x) = pos.z + m_clip.z *x
  2572. // Y2(x) = image_pos + (image_next-image_pos)*x
  2573. // Y1(x) = Y2(x)
  2574. // pos.z + m_clip.z*x = image_pos + (image_next-image_pos)*x
  2575. // (m_clip.z-(image_next-image_pos))*x = image_pos - pos.z
  2576. // (m_clip.z+image_pos-image_next)*x = image_pos - pos.z
  2577. // x = image_pos - pos.z / (m_clip.z+image_pos-image_next)
  2578. if(Flt div=m_clip.z+image_pos-image_next)
  2579. {
  2580. Flt x=(image_pos-pos.z)/div;
  2581. pos+=m_clip*x;
  2582. }
  2583. Int maxi=Abs(m).maxI();
  2584. Flt frac=m.c[maxi]; if(frac)frac=(pos.c[maxi]-s.c[maxi])/frac;
  2585. if(hit_frac)*hit_frac=frac;
  2586. if(hit_pos )*hit_pos =start+move*frac;
  2587. }
  2588. break;
  2589. }
  2590. pos=next;
  2591. image_pos=image_next;
  2592. }
  2593. unlock();
  2594. }
  2595. return hit;
  2596. }
  2597. /******************************************************************************
  2598. void normalToBump(Image &dest, Bool high_quality); // convert normal map to bump map
  2599. static inline Flt DX(Vec nrm, Bool rescale)
  2600. {
  2601. if(rescale)nrm=nrm*(255.0f/127)-128.0f/127.0f; // (nrm*255-128)/127
  2602. else nrm=nrm*2-1;
  2603. Vec2 n2(nrm.z, -nrm.x);
  2604. n2.y/=Max(0.1f, Abs(n2.x));
  2605. return n2.y;
  2606. }
  2607. static inline Flt DY(Vec nrm, Bool rescale)
  2608. {
  2609. if(rescale)nrm=nrm*(255.0f/127)-128.0f/127.0f; // (nrm*255-128)/127
  2610. else nrm=nrm*2-1;
  2611. Vec2 n2(nrm.z, -nrm.y);
  2612. n2.y/=Max(0.1f, Abs(n2.x));
  2613. return n2.y;
  2614. }
  2615. void Image::normalToBump(Image &dest, Bool high_quality)
  2616. {
  2617. Bool high=true;
  2618. // obliczaj lokalnie w blokach (przykladowo dla kazdego 8x8 obliczaj lokalna heightmape)
  2619. // roznice w poziomach pomiedzy blokami obliczaj na podstawie sredniej roznicy wspolnych pikseli (krawedzi)
  2620. // roznice pomiedzy blokami nalezy obliczac od srodka obrazka, promieniscie na zewnatrz (jaka rozchodzaca sie fala)
  2621. if(high_quality)
  2622. {
  2623. if(lockRead())
  2624. {
  2625. Image delta, bump, temp;
  2626. if(delta.createTry(_x, _y, 1, IMAGE_F32_2, IMAGE_SOFT, 1))
  2627. if(bump .createTry(_x, _y, 1, IMAGE_F32_2, IMAGE_SOFT, 1))
  2628. if(temp .createTry(_x, _y, 1, IMAGE_F32 , IMAGE_SOFT, 1))
  2629. {
  2630. // set bump delta's from normals
  2631. Bool rescale=(bytePP()<=4); // rescale imprecisions of integer types resulting in 128/255 != 0.5
  2632. REPD(y, T._y)
  2633. REPD(x, T._x)
  2634. {
  2635. Vec nrm=colorF(x, y);
  2636. delta.pixF2(x, y).set(DX(nrm, rescale), DY(nrm, rescale));
  2637. }
  2638. // create blended temp
  2639. temp.clear();
  2640. Int res =Max(1, Min(64, T._x, T._y)),
  2641. blend=(res>>1);
  2642. for(Int my=0; my<T._y; my+=res)
  2643. for(Int mx=0; mx<T._x; mx+=res)
  2644. {
  2645. bump.pixF2(mx, my)=0; // set starting heights to zero
  2646. // fill horizontal middle line
  2647. for(Int x=mx-1; x>=0; x--)bump.pixF2(x, my).x=bump.pixF2(x+1, my).x-delta.pixF2(x, my).x;
  2648. for(Int x=mx+1; x<_x; x++)bump.pixF2(x, my).x=bump.pixF2(x-1, my).x+delta.pixF2(x, my).x;
  2649. // fill vertical lines from source horizontal middle line
  2650. REPD(x, T._x)
  2651. {
  2652. for(Int y=my-1; y>=0; y--)bump.pixF2(x, y).x=bump.pixF2(x, y+1).x-delta.pixF2(x, y).y;
  2653. for(Int y=my+1; y<_y; y++)bump.pixF2(x, y).x=bump.pixF2(x, y-1).x+delta.pixF2(x, y).y;
  2654. }
  2655. // copy vertical middle line to Y channel
  2656. REPD(y, T._y){Vec2 &b=bump.pixF2(mx, y); b.y=b.x;}
  2657. // fill horizontal lines from source vertical middle line
  2658. REPD(y, T._y)
  2659. {
  2660. for(Int x=mx-1; x>=0; x--)bump.pixF2(x, y).y=bump.pixF2(x+1, y).y-delta.pixF2(x, y).x;
  2661. for(Int x=mx+1; x<_x; x++)bump.pixF2(x, y).y=bump.pixF2(x-1, y).y+delta.pixF2(x, y).x;
  2662. }
  2663. // merge X and Y channels
  2664. REPD(y, T._y)
  2665. REPD(x, T._x){Vec2 &b=bump.pixF2(x, y); b.x+=b.y;}
  2666. Vec4 min, max; bump.getMinMax(min, max);
  2667. Flt mul, add; if(min.x==max.x){mul=1; add=0;}else{mul=1.0f/(max.x-min.x); add=-min.x*mul;}
  2668. Int y_from=Max(0, my-blend), y_to=Min(my+res+blend, T._y), y_min_blend=(my ? (my+blend) : 0), y_max_blend=((my+res<T._y) ? (my+res-blend-1) : _y),
  2669. x_from=Max(0, mx-blend), x_to=Min(mx+res+blend, T._x), x_min_blend=(mx ? (mx+blend) : 0), x_max_blend=((mx+res<T._x) ? (mx+res-blend-1) : _x);
  2670. for(Int y=y_from; y<y_to; y++)
  2671. for(Int x=x_from; x<x_to; x++)
  2672. {
  2673. Flt b=1;
  2674. if(blend)
  2675. {
  2676. if(x<x_min_blend)b*=Flt(blend*2+1 + x-x_min_blend)/(blend*2+1);else
  2677. if(x>x_max_blend)b*=Flt(blend*2+1 + x_max_blend-x)/(blend*2+1);
  2678. if(y<y_min_blend)b*=Flt(blend*2+1 + y-y_min_blend)/(blend*2+1);else
  2679. if(y>y_max_blend)b*=Flt(blend*2+1 + y_max_blend-y)/(blend*2+1);
  2680. }
  2681. temp.pixF(x, y)+=(bump.pixF2(x, y).x*mul + add)*b;
  2682. }
  2683. }
  2684. // create destination
  2685. if(dest.createTry(_x, _y, 1, IMAGE_L8, mode()))
  2686. if(dest.lock())
  2687. {
  2688. REPD(y, _y)
  2689. REPD(x, _x)dest.pixB(x, y)=Round(Sat(temp.pixF(x, y))*255);
  2690. dest.unlock();
  2691. }
  2692. }
  2693. unlock();
  2694. }
  2695. }else
  2696. {
  2697. if(lockRead())
  2698. {
  2699. Image bump;
  2700. if( bump.createTry(_x, _y, 1, IMAGE_F32_2, IMAGE_SOFT, 1))
  2701. {
  2702. Bool rescale=(bytePP()<=4); // rescale imprecisions of integer types resulting in 128/255 != 0.5
  2703. Int mx =(T._x>>1), // middle x
  2704. my =(T._y>>1); // middle y
  2705. bump.pixF2(mx, my)=0; // set starting heights to zero
  2706. // fill horizontal middle line
  2707. for(Int x=mx-1; x>=0; x--)bump.pixF2(x, my).x=bump.pixF2(x+1, my).x-DX(colorF(x, my), rescale);
  2708. for(Int x=mx+1; x<_x; x++)bump.pixF2(x, my).x=bump.pixF2(x-1, my).x+DX(colorF(x, my), rescale);
  2709. // fill vertical lines from source horizontal middle line
  2710. REPD(x, T._x)
  2711. {
  2712. for(Int y=my-1; y>=0; y--)bump.pixF2(x, y).x=bump.pixF2(x, y+1).x-DY(colorF(x, y), rescale);
  2713. for(Int y=my+1; y<_y; y++)bump.pixF2(x, y).x=bump.pixF2(x, y-1).x+DY(colorF(x, y), rescale);
  2714. }
  2715. // copy vertical middle line to Y channel
  2716. REPD(y, T._y){Vec2 &b=bump.pixF2(mx, y); b.y=b.x;}
  2717. // fill horizontal lines from source vertical middle line
  2718. REPD(y, T._y)
  2719. {
  2720. for(Int x=mx-1; x>=0; x--)bump.pixF2(x, y).y=bump.pixF2(x+1, y).y-DX(colorF(x, y), rescale);
  2721. for(Int x=mx+1; x<_x; x++)bump.pixF2(x, y).y=bump.pixF2(x-1, y).y+DX(colorF(x, y), rescale);
  2722. }
  2723. // merge X and Y channels
  2724. REPD(y, T._y)
  2725. REPD(x, T._x){Vec2 &b=bump.pixF2(x, y); b.x+=b.y;}
  2726. bump.normalize();
  2727. // create dest
  2728. dest.create(T._x, T._y, 1, IMAGE_L8, mode(), 1);
  2729. if(dest.lock())
  2730. {
  2731. REPD(y, T._y)
  2732. REPD(x, T._x)dest.pixB(x, y)=Round(bump.pixF2(x, y).x*255);
  2733. dest.unlock();
  2734. }
  2735. }
  2736. unlock();
  2737. }
  2738. }
  2739. }
  2740. /******************************************************************************/
  2741. static Bool CanDecompress(IMAGE_TYPE type)
  2742. {
  2743. return type!=IMAGE_PVRTC1_2 && type!=IMAGE_PVRTC1_4;
  2744. }
  2745. void (*DecompressBlock(IMAGE_TYPE type))(C Byte *b, Color (&block)[4][4])
  2746. {
  2747. switch(type)
  2748. {
  2749. default : return null;
  2750. case IMAGE_BC1 : return DecompressBlockBC1 ; break;
  2751. case IMAGE_BC2 : return DecompressBlockBC2 ; break;
  2752. case IMAGE_BC3 : return DecompressBlockBC3 ; break;
  2753. case IMAGE_BC7 : return DecompressBlockBC7 ; break;
  2754. case IMAGE_ETC1 : return DecompressBlockETC1 ; break;
  2755. case IMAGE_ETC2 : return DecompressBlockETC2 ; break;
  2756. case IMAGE_ETC2_A1: return DecompressBlockETC2A1; break;
  2757. case IMAGE_ETC2_A8: return DecompressBlockETC2A8; break;
  2758. }
  2759. }
  2760. Bool ImageCompare::compare(C Image &a, C Image &b, Flt similar_dif, Bool alpha_weight, Int a_mip, Flt skip_dif)
  2761. {
  2762. // clear
  2763. skipped =false;
  2764. max_dif =0;
  2765. avg_dif =0;
  2766. avg_dif2=0;
  2767. similar =0;
  2768. psnr =0;
  2769. Bool ok=false;
  2770. if(InRange(a_mip, a.mipMaps())) // if 'a' has requested mip map
  2771. {
  2772. // get dimensions of mip map
  2773. Int aw=Max(1, a.w()>>a_mip),
  2774. ah=Max(1, a.h()>>a_mip);
  2775. // if 'b' has the same size
  2776. FREPD(b_mip, b.mipMaps())
  2777. {
  2778. // get dimensions of mip map
  2779. Int bw=Max(1, b.w()>>b_mip),
  2780. bh=Max(1, b.h()>>b_mip);
  2781. if(aw==bw && ah==bh) // if match
  2782. {
  2783. Image temp_a, temp_b;
  2784. C Image *sa=&a, *sb=&b;
  2785. if(!CanDecompress(sa->hwType())){if(!sa->extractMipMap(temp_a, IMAGE_R8G8B8A8, IMAGE_SOFT, a_mip))return false; sa=&temp_a; a_mip=0;}
  2786. if(!CanDecompress(sb->hwType())){if(!sb->extractMipMap(temp_b, IMAGE_R8G8B8A8, IMAGE_SOFT, b_mip))return false; sb=&temp_b; b_mip=0;}
  2787. if(sa->lockRead(a_mip))
  2788. {
  2789. if(sb->lockRead(b_mip))
  2790. {
  2791. if(sa->lockSize()==sb->lockSize())
  2792. {
  2793. ok=true;
  2794. const Bool per_channel =false; // false=faster
  2795. const UInt channels=4,
  2796. max_value=255,
  2797. scale=channels*max_value;
  2798. UInt _skip_dif =Max(0, RoundPos( skip_dif*scale)),
  2799. _similar_dif =Max(0, RoundPos(similar_dif*scale)),
  2800. max_dif =0;
  2801. ULong total_dif =0,
  2802. total_dif2=0,
  2803. similar_pixels=0,
  2804. processed_pixels=0;
  2805. Int a_x_mul=ImageTI[sa->hwType()].bit_pp*2, // *2 because (4*4 colors / 8 bits)
  2806. b_x_mul=ImageTI[sb->hwType()].bit_pp*2; // *2 because (4*4 colors / 8 bits)
  2807. Color a_block[4][4], b_block[4][4];
  2808. void (*decompress_a)(C Byte *b, Color (&block)[4][4])=DecompressBlock(sa->hwType());
  2809. void (*decompress_b)(C Byte *b, Color (&block)[4][4])=DecompressBlock(sb->hwType());
  2810. REPD(by, DivCeil4(sa->lh()))
  2811. {
  2812. const Int py=by*4, ys=Min(4, sa->lh()-py); Int yo[4]; REP(ys)yo[i]=py+i;
  2813. REPD(bx, DivCeil4(sa->lw()))
  2814. {
  2815. const Int px=bx*4, xs=Min(4, sa->lw()-px); Int xo[4]; REPAO(xo)=Min(px+i, sa->lw()-1); // set all 'xo' and clamp, because we may read more, read below why
  2816. // it's okay to call decompress on partial blocks, because if source is compressed then its size will always fit the entire block, and we're decompressing to temporary memory
  2817. if(decompress_a)decompress_a(sa->data() + bx*a_x_mul + by*sa->pitch(), a_block);else sa->gather(a_block[0], xo, 4, yo, ys); // always read 4 xs because we need correct alignment for y's (for example if we would read only 2, then the next row would not be set for block[1][0] but for block[0][2])
  2818. if(decompress_b)decompress_b(sb->data() + bx*b_x_mul + by*sb->pitch(), b_block);else sb->gather(b_block[0], xo, 4, yo, ys); // always read 4 xs because we need correct alignment for y's (for example if we would read only 2, then the next row would not be set for block[1][0] but for block[0][2])
  2819. REPD(y, ys)
  2820. REPD(x, xs)
  2821. {
  2822. C Color &ca=a_block[y][x],
  2823. &cb=b_block[y][x];
  2824. UInt avg_a =AvgI(UInt(ca.a), UInt(cb.a)),
  2825. dif_r =Abs (ca.r-cb.r),
  2826. dif_g =Abs (ca.g-cb.g),
  2827. dif_b =Abs (ca.b-cb.b),
  2828. dif_a =Abs (ca.a-cb.a),
  2829. dif_rgb=dif_r+dif_g+dif_b,
  2830. dif =(alpha_weight ? DivRound(dif_rgb*avg_a, 255u) : dif_rgb)+dif_a;
  2831. MAX(max_dif, dif);
  2832. total_dif+=dif;
  2833. if(per_channel)
  2834. total_dif2+=(alpha_weight ? Sqr(DivRound(dif_r*avg_a, 255u)) + Sqr(DivRound(dif_g*avg_a, 255u)) + Sqr(DivRound(dif_b*avg_a, 255u))
  2835. : Sqr( dif_r ) + Sqr( dif_g ) + Sqr( dif_b )) + Sqr(dif_a);
  2836. else total_dif2+=dif*dif;
  2837. if(dif<=_similar_dif)similar_pixels++;
  2838. processed_pixels++;
  2839. if(dif>_skip_dif){skipped=true; goto finish;} // skip after setting other parameters, especially 'max_diff'
  2840. }
  2841. }
  2842. }
  2843. finish:
  2844. Dbl MSE= total_dif2 /Dbl(processed_pixels*(per_channel ? channels*max_value*max_value : scale*scale));
  2845. T.max_dif= max_dif /Flt( scale);
  2846. T.avg_dif= total_dif /Dbl(processed_pixels*scale); T.avg_dif2=Sqrt(MSE);
  2847. T.similar=similar_pixels/Dbl(processed_pixels ); T.psnr =10*log10(1.0/MSE);
  2848. }
  2849. sb->unlock();
  2850. }
  2851. sa->unlock();
  2852. }
  2853. break;
  2854. }
  2855. }
  2856. }
  2857. return ok;
  2858. }
  2859. /******************************************************************************/
  2860. #if WINDOWS_OLD
  2861. HICON CreateIcon(C Image &image, C VecI2 *cursor_hot_spot)
  2862. {
  2863. HICON icon=null;
  2864. Image temp; C Image *src=&image;
  2865. if(src->compressed())if(src->copyTry(temp, -1, -1, 1, IMAGE_B8G8R8A8, IMAGE_SOFT, 1))src=&temp;else src=null;
  2866. if(src && src->is() && src->lockRead())
  2867. {
  2868. BITMAPV5HEADER bi; Zero(bi);
  2869. bi.bV5Size =SIZE(bi);
  2870. bi.bV5Width =src->w();
  2871. bi.bV5Height =src->h();
  2872. bi.bV5Planes =1;
  2873. bi.bV5BitCount =32;
  2874. bi.bV5Compression=BI_BITFIELDS;
  2875. bi.bV5RedMask =0x00FF0000;
  2876. bi.bV5GreenMask =0x0000FF00;
  2877. bi.bV5BlueMask =0x000000FF;
  2878. bi.bV5AlphaMask =0xFF000000;
  2879. VecB4 *data=null;
  2880. HBITMAP hBitmap =CreateDIBSection(null, (BITMAPINFO*)&bi, DIB_RGB_COLORS, (Ptr*)&data, null, 0),
  2881. hMonoBitmap=CreateBitmap (src->w(), src->h(), 1, 1, null);
  2882. if(data)
  2883. {
  2884. REPD(y, src->h())
  2885. FREPD(x, src->w())
  2886. {
  2887. Color c=src->color(x, y);
  2888. (data++)->set(c.b, c.g, c.r, c.a);
  2889. }
  2890. ICONINFO ii;
  2891. if(cursor_hot_spot)
  2892. {
  2893. ii.fIcon =false;
  2894. ii.xHotspot=cursor_hot_spot->x;
  2895. ii.yHotspot=cursor_hot_spot->y;
  2896. }else
  2897. {
  2898. ii.fIcon =true;
  2899. ii.xHotspot=0; // this is ignored for icons
  2900. ii.yHotspot=0; // this is ignored for icons
  2901. }
  2902. ii.hbmMask =hMonoBitmap;
  2903. ii.hbmColor=hBitmap;
  2904. icon=CreateIconIndirect(&ii);
  2905. }
  2906. DeleteObject(hBitmap);
  2907. DeleteObject(hMonoBitmap);
  2908. src->unlock();
  2909. }
  2910. return icon;
  2911. }
  2912. #endif
  2913. /******************************************************************************/
  2914. }
  2915. /******************************************************************************/