crn_image_utils.cpp 46 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367
  1. // File: crn_image_utils.cpp
  2. // See Copyright Notice and license at the end of inc/crnlib.h
  3. #include "crn_core.h"
  4. #include "crn_image_utils.h"
  5. #include "crn_console.h"
  6. #include "crn_resampler.h"
  7. #include "crn_threaded_resampler.h"
  8. #include "crn_strutils.h"
  9. #include "crn_file_utils.h"
  10. #include "crn_threading.h"
  11. #include "crn_miniz.h"
  12. #include "crn_jpge.h"
  13. #include "crn_cfile_stream.h"
  14. #include "crn_mipmapped_texture.h"
  15. #include "crn_buffer_stream.h"
  16. #define STBI_HEADER_FILE_ONLY
  17. #include "crn_stb_image.cpp"
  18. #include "crn_jpgd.h"
  19. #include "crn_pixel_format.h"
  20. namespace crnlib
  21. {
  22. const float cInfinitePSNR = 999999.0f;
  23. const uint CRNLIB_LARGEST_SUPPORTED_IMAGE_DIMENSION = 16384;
  24. namespace image_utils
  25. {
  26. bool read_from_stream_stb(data_stream_serializer &serializer, image_u8& img)
  27. {
  28. uint8_vec buf;
  29. if (!serializer.read_entire_file(buf))
  30. return false;
  31. int x = 0, y = 0, n = 0;
  32. unsigned char* pData = stbi_load_from_memory(buf.get_ptr(), buf.size_in_bytes(), &x, &y, &n, 4);
  33. if (!pData)
  34. return false;
  35. if ((x > (int)CRNLIB_LARGEST_SUPPORTED_IMAGE_DIMENSION) || (y > (int)CRNLIB_LARGEST_SUPPORTED_IMAGE_DIMENSION))
  36. {
  37. stbi_image_free(pData);
  38. return false;
  39. }
  40. const bool has_alpha = ((n == 2) || (n == 4));
  41. img.resize(x, y);
  42. bool grayscale = true;
  43. for (int py = 0; py < y; py++)
  44. {
  45. const color_quad_u8* pSrc = reinterpret_cast<const color_quad_u8*>(pData) + (py * x);
  46. color_quad_u8* pDst = img.get_scanline(py);
  47. color_quad_u8* pDst_end = pDst + x;
  48. while (pDst != pDst_end)
  49. {
  50. color_quad_u8 c(*pSrc++);
  51. if (!has_alpha)
  52. c.a = 255;
  53. if (!c.is_grayscale())
  54. grayscale = false;
  55. *pDst++ = c;
  56. }
  57. }
  58. stbi_image_free(pData);
  59. img.reset_comp_flags();
  60. img.set_grayscale(grayscale);
  61. img.set_component_valid(3, has_alpha);
  62. return true;
  63. }
  64. bool read_from_stream_jpgd(data_stream_serializer &serializer, image_u8& img)
  65. {
  66. uint8_vec buf;
  67. if (!serializer.read_entire_file(buf))
  68. return false;
  69. int width = 0, height = 0, actual_comps = 0;
  70. unsigned char *pSrc_img = jpgd::decompress_jpeg_image_from_memory(buf.get_ptr(), buf.size_in_bytes(), &width, &height, &actual_comps, 4);
  71. if (!pSrc_img)
  72. return false;
  73. if (math::maximum(width, height) > (int)CRNLIB_LARGEST_SUPPORTED_IMAGE_DIMENSION)
  74. {
  75. crnlib_free(pSrc_img);
  76. return false;
  77. }
  78. if (!img.grant_ownership(reinterpret_cast<color_quad_u8*>(pSrc_img), width, height))
  79. {
  80. crnlib_free(pSrc_img);
  81. return false;
  82. }
  83. img.reset_comp_flags();
  84. img.set_grayscale(actual_comps == 1);
  85. img.set_component_valid(3, false);
  86. return true;
  87. }
  88. bool read_from_stream(image_u8& dest, data_stream_serializer& serializer, uint read_flags)
  89. {
  90. if (read_flags > cReadFlagsAllFlags)
  91. {
  92. CRNLIB_ASSERT(0);
  93. return false;
  94. }
  95. if (!serializer.get_stream())
  96. {
  97. CRNLIB_ASSERT(0);
  98. return false;
  99. }
  100. dynamic_string ext(serializer.get_name());
  101. file_utils::get_extension(ext);
  102. if ((ext == "jpg") || (ext == "jpeg"))
  103. {
  104. // Use my jpeg decoder by default because it supports progressive jpeg's.
  105. if ((read_flags & cReadFlagForceSTB) == 0)
  106. {
  107. return image_utils::read_from_stream_jpgd(serializer, dest);
  108. }
  109. }
  110. return image_utils::read_from_stream_stb(serializer, dest);
  111. }
  112. bool read_from_file(image_u8& dest, const char* pFilename, uint read_flags)
  113. {
  114. if (read_flags > cReadFlagsAllFlags)
  115. {
  116. CRNLIB_ASSERT(0);
  117. return false;
  118. }
  119. cfile_stream file_stream;
  120. if (!file_stream.open(pFilename))
  121. return false;
  122. data_stream_serializer serializer(file_stream);
  123. return read_from_stream(dest, serializer, read_flags);
  124. }
  125. bool write_to_file(const char* pFilename, const image_u8& img, uint write_flags, int grayscale_comp_index)
  126. {
  127. if ((grayscale_comp_index < -1) || (grayscale_comp_index > 3))
  128. {
  129. CRNLIB_ASSERT(0);
  130. return false;
  131. }
  132. if (!img.get_width())
  133. {
  134. CRNLIB_ASSERT(0);
  135. return false;
  136. }
  137. dynamic_string ext(pFilename);
  138. bool is_jpeg = false;
  139. if (file_utils::get_extension(ext))
  140. {
  141. is_jpeg = ((ext == "jpg") || (ext == "jpeg"));
  142. if ((ext != "png") && (ext != "bmp") && (ext != "tga") && (!is_jpeg))
  143. {
  144. console::error("crnlib::image_utils::write_to_file: Can only write .BMP, .TGA, .PNG, or .JPG files!\n");
  145. return false;
  146. }
  147. }
  148. crnlib::vector<uint8> temp;
  149. uint num_src_chans = 0;
  150. const void *pSrc_img = NULL;
  151. if (is_jpeg)
  152. {
  153. write_flags |= cWriteFlagIgnoreAlpha;
  154. }
  155. if ((img.get_comp_flags() & pixel_format_helpers::cCompFlagGrayscale) || (write_flags & image_utils::cWriteFlagGrayscale))
  156. {
  157. CRNLIB_ASSERT(grayscale_comp_index < 4);
  158. if (grayscale_comp_index > 3) grayscale_comp_index = 3;
  159. temp.resize(img.get_total_pixels());
  160. for (uint y = 0; y < img.get_height(); y++)
  161. {
  162. const color_quad_u8* pSrc = img.get_scanline(y);
  163. const color_quad_u8* pSrc_end = pSrc + img.get_width();
  164. uint8* pDst = &temp[y * img.get_width()];
  165. if (img.get_comp_flags() & pixel_format_helpers::cCompFlagGrayscale)
  166. {
  167. while (pSrc != pSrc_end)
  168. *pDst++ = (*pSrc++)[1];
  169. }
  170. else if (grayscale_comp_index < 0)
  171. {
  172. while (pSrc != pSrc_end)
  173. *pDst++ = static_cast<uint8>((*pSrc++).get_luma());
  174. }
  175. else
  176. {
  177. while (pSrc != pSrc_end)
  178. *pDst++ = (*pSrc++)[grayscale_comp_index];
  179. }
  180. }
  181. pSrc_img = &temp[0];
  182. num_src_chans = 1;
  183. }
  184. else if ((!img.is_component_valid(3)) || (write_flags & cWriteFlagIgnoreAlpha))
  185. {
  186. temp.resize(img.get_total_pixels() * 3);
  187. for (uint y = 0; y < img.get_height(); y++)
  188. {
  189. const color_quad_u8* pSrc = img.get_scanline(y);
  190. const color_quad_u8* pSrc_end = pSrc + img.get_width();
  191. uint8* pDst = &temp[y * img.get_width() * 3];
  192. while (pSrc != pSrc_end)
  193. {
  194. const color_quad_u8 c(*pSrc++);
  195. pDst[0] = c.r;
  196. pDst[1] = c.g;
  197. pDst[2] = c.b;
  198. pDst += 3;
  199. }
  200. }
  201. num_src_chans = 3;
  202. pSrc_img = &temp[0];
  203. }
  204. else
  205. {
  206. num_src_chans = 4;
  207. pSrc_img = img.get_ptr();
  208. }
  209. bool success = false;
  210. if (ext == "png")
  211. {
  212. size_t png_image_size = 0;
  213. void *pPNG_image_data = tdefl_write_image_to_png_file_in_memory(pSrc_img, img.get_width(), img.get_height(), num_src_chans, &png_image_size);
  214. if (!pPNG_image_data)
  215. return false;
  216. success = file_utils::write_buf_to_file(pFilename, pPNG_image_data, png_image_size);
  217. mz_free(pPNG_image_data);
  218. }
  219. else if (is_jpeg)
  220. {
  221. jpge::params params;
  222. if (write_flags & cWriteFlagJPEGQualityLevelMask)
  223. params.m_quality = math::clamp<uint>((write_flags & cWriteFlagJPEGQualityLevelMask) >> cWriteFlagJPEGQualityLevelShift, 1U, 100U);
  224. params.m_two_pass_flag = (write_flags & cWriteFlagJPEGTwoPass) != 0;
  225. params.m_no_chroma_discrim_flag = (write_flags & cWriteFlagJPEGNoChromaDiscrim) != 0;
  226. if (write_flags & cWriteFlagJPEGH1V1)
  227. params.m_subsampling = jpge::H1V1;
  228. else if (write_flags & cWriteFlagJPEGH2V1)
  229. params.m_subsampling = jpge::H2V1;
  230. else if (write_flags & cWriteFlagJPEGH2V2)
  231. params.m_subsampling = jpge::H2V2;
  232. success = jpge::compress_image_to_jpeg_file(pFilename, img.get_width(), img.get_height(), num_src_chans, (const jpge::uint8*)pSrc_img, params);
  233. }
  234. else
  235. {
  236. success = ((ext == "bmp" ? stbi_write_bmp : stbi_write_tga)(pFilename, img.get_width(), img.get_height(), num_src_chans, pSrc_img) == CRNLIB_TRUE);
  237. }
  238. return success;
  239. }
  240. bool has_alpha(const image_u8& img)
  241. {
  242. for (uint y = 0; y < img.get_height(); y++)
  243. for (uint x = 0; x < img.get_width(); x++)
  244. if (img(x, y).a < 255)
  245. return true;
  246. return false;
  247. }
  248. void renorm_normal_map(image_u8& img)
  249. {
  250. for (uint y = 0; y < img.get_height(); y++)
  251. {
  252. for (uint x = 0; x < img.get_width(); x++)
  253. {
  254. color_quad_u8& c = img(x, y);
  255. if ((c.r == 128) && (c.g == 128) && (c.b == 128))
  256. continue;
  257. vec3F v(c.r, c.g, c.b);
  258. v *= 1.0f/255.0f;
  259. v *= 2.0f;
  260. v -= vec3F(1.0f);
  261. v.clamp(-1.0f, 1.0f);
  262. float length = v.length();
  263. if (length < .077f)
  264. c.set(128, 128, 128, c.a);
  265. else if (fabs(length - 1.0f) > .077f)
  266. {
  267. if (length)
  268. v /= length;
  269. for (uint i = 0; i < 3; i++)
  270. c[i] = static_cast<uint8>(math::clamp<float>(floor((v[i] + 1.0f) * .5f * 255.0f + .5f), 0.0f, 255.0f));
  271. if ((c.r == 128) && (c.g == 128))
  272. {
  273. if (c.b < 128)
  274. c.b = 0;
  275. else
  276. c.b = 255;
  277. }
  278. }
  279. }
  280. }
  281. }
  282. bool is_normal_map(const image_u8& img, const char* pFilename)
  283. {
  284. float score = 0.0f;
  285. uint num_invalid_pixels = 0;
  286. // TODO: Derive better score from pixel mean, eigenvecs/vals
  287. //crnlib::vector<vec3F> pixels;
  288. for (uint y = 0; y < img.get_height(); y++)
  289. {
  290. for (uint x = 0; x < img.get_width(); x++)
  291. {
  292. const color_quad_u8& c = img(x, y);
  293. if (c.b < 123)
  294. {
  295. num_invalid_pixels++;
  296. continue;
  297. }
  298. else if ((c.r != 128) || (c.g != 128) || (c.b != 128))
  299. {
  300. vec3F v(c.r, c.g, c.b);
  301. v -= vec3F(128.0f);
  302. v /= vec3F(127.0f);
  303. //pixels.push_back(v);
  304. v.clamp(-1.0f, 1.0f);
  305. float norm = v.norm();
  306. if ((norm < 0.83f) || (norm > 1.29f))
  307. num_invalid_pixels++;
  308. }
  309. }
  310. }
  311. score -= math::clamp(float(num_invalid_pixels) / (img.get_width() * img.get_height()) - .026f, 0.0f, 1.0f) * 5.0f;
  312. if (pFilename)
  313. {
  314. dynamic_string str(pFilename);
  315. str.tolower();
  316. if (str.contains("normal") || str.contains("local") || str.contains("nmap"))
  317. score += 1.0f;
  318. if (str.contains("diffuse") || str.contains("spec") || str.contains("gloss"))
  319. score -= 1.0f;
  320. }
  321. return score >= 0.0f;
  322. }
  323. bool resample_single_thread(const image_u8& src, image_u8& dst, const resample_params& params)
  324. {
  325. const uint src_width = src.get_width();
  326. const uint src_height = src.get_height();
  327. if (math::maximum(src_width, src_height) > CRNLIB_RESAMPLER_MAX_DIMENSION)
  328. {
  329. printf("Image is too large!\n");
  330. return EXIT_FAILURE;
  331. }
  332. const int cMaxComponents = 4;
  333. if (((int)params.m_num_comps < 1) || ((int)params.m_num_comps > (int)cMaxComponents))
  334. return false;
  335. const uint dst_width = params.m_dst_width;
  336. const uint dst_height = params.m_dst_height;
  337. if ((math::minimum(dst_width, dst_height) < 1) || (math::maximum(dst_width, dst_height) > CRNLIB_RESAMPLER_MAX_DIMENSION))
  338. {
  339. printf("Image is too large!\n");
  340. return EXIT_FAILURE;
  341. }
  342. if ((src_width == dst_width) && (src_height == dst_height))
  343. {
  344. dst = src;
  345. return true;
  346. }
  347. dst.clear();
  348. dst.resize(params.m_dst_width, params.m_dst_height);
  349. // Partial gamma correction looks better on mips. Set to 1.0 to disable gamma correction.
  350. const float source_gamma = params.m_source_gamma;//1.75f;
  351. float srgb_to_linear[256];
  352. if (params.m_srgb)
  353. {
  354. for (int i = 0; i < 256; ++i)
  355. srgb_to_linear[i] = (float)pow(i * 1.0f/255.0f, source_gamma);
  356. }
  357. const int linear_to_srgb_table_size = 8192;
  358. unsigned char linear_to_srgb[linear_to_srgb_table_size];
  359. const float inv_linear_to_srgb_table_size = 1.0f / linear_to_srgb_table_size;
  360. const float inv_source_gamma = 1.0f / source_gamma;
  361. if (params.m_srgb)
  362. {
  363. for (int i = 0; i < linear_to_srgb_table_size; ++i)
  364. {
  365. int k = (int)(255.0f * pow(i * inv_linear_to_srgb_table_size, inv_source_gamma) + .5f);
  366. if (k < 0) k = 0; else if (k > 255) k = 255;
  367. linear_to_srgb[i] = (unsigned char)k;
  368. }
  369. }
  370. Resampler* resamplers[cMaxComponents];
  371. crnlib::vector<float> samples[cMaxComponents];
  372. resamplers[0] = crnlib_new<Resampler>(src_width, src_height, dst_width, dst_height,
  373. params.m_wrapping ? Resampler::BOUNDARY_WRAP : Resampler::BOUNDARY_CLAMP, 0.0f, 1.0f,
  374. params.m_pFilter, (Resampler::Contrib_List*)NULL, (Resampler::Contrib_List*)NULL, params.m_filter_scale, params.m_filter_scale);
  375. samples[0].resize(src_width);
  376. for (uint i = 1; i < params.m_num_comps; i++)
  377. {
  378. resamplers[i] = crnlib_new<Resampler>(src_width, src_height, dst_width, dst_height,
  379. params.m_wrapping ? Resampler::BOUNDARY_WRAP : Resampler::BOUNDARY_CLAMP, 0.0f, 1.0f,
  380. params.m_pFilter, resamplers[0]->get_clist_x(), resamplers[0]->get_clist_y(), params.m_filter_scale, params.m_filter_scale);
  381. samples[i].resize(src_width);
  382. }
  383. uint dst_y = 0;
  384. for (uint src_y = 0; src_y < src_height; src_y++)
  385. {
  386. const color_quad_u8* pSrc = src.get_scanline(src_y);
  387. for (uint x = 0; x < src_width; x++)
  388. {
  389. for (uint c = 0; c < params.m_num_comps; c++)
  390. {
  391. const uint comp_index = params.m_first_comp + c;
  392. const uint8 v = (*pSrc)[comp_index];
  393. if (!params.m_srgb || (comp_index == 3))
  394. samples[c][x] = v * (1.0f/255.0f);
  395. else
  396. samples[c][x] = srgb_to_linear[v];
  397. }
  398. pSrc++;
  399. }
  400. for (uint c = 0; c < params.m_num_comps; c++)
  401. {
  402. if (!resamplers[c]->put_line(&samples[c][0]))
  403. {
  404. for (uint i = 0; i < params.m_num_comps; i++)
  405. crnlib_delete(resamplers[i]);
  406. return false;
  407. }
  408. }
  409. for ( ; ; )
  410. {
  411. uint c;
  412. for (c = 0; c < params.m_num_comps; c++)
  413. {
  414. const uint comp_index = params.m_first_comp + c;
  415. const float* pOutput_samples = resamplers[c]->get_line();
  416. if (!pOutput_samples)
  417. break;
  418. const bool linear = !params.m_srgb || (comp_index == 3);
  419. CRNLIB_ASSERT(dst_y < dst_height);
  420. color_quad_u8* pDst = dst.get_scanline(dst_y);
  421. for (uint x = 0; x < dst_width; x++)
  422. {
  423. if (linear)
  424. {
  425. int c = (int)(255.0f * pOutput_samples[x] + .5f);
  426. if (c < 0) c = 0; else if (c > 255) c = 255;
  427. (*pDst)[comp_index] = (unsigned char)c;
  428. }
  429. else
  430. {
  431. int j = (int)(linear_to_srgb_table_size * pOutput_samples[x] + .5f);
  432. if (j < 0) j = 0; else if (j >= linear_to_srgb_table_size) j = linear_to_srgb_table_size - 1;
  433. (*pDst)[comp_index] = linear_to_srgb[j];
  434. }
  435. pDst++;
  436. }
  437. }
  438. if (c < params.m_num_comps)
  439. break;
  440. dst_y++;
  441. }
  442. }
  443. for (uint i = 0; i < params.m_num_comps; i++)
  444. crnlib_delete(resamplers[i]);
  445. return true;
  446. }
  447. bool resample_multithreaded(const image_u8& src, image_u8& dst, const resample_params& params)
  448. {
  449. const uint src_width = src.get_width();
  450. const uint src_height = src.get_height();
  451. if (math::maximum(src_width, src_height) > CRNLIB_RESAMPLER_MAX_DIMENSION)
  452. {
  453. printf("Image is too large!\n");
  454. return EXIT_FAILURE;
  455. }
  456. const int cMaxComponents = 4;
  457. if (((int)params.m_num_comps < 1) || ((int)params.m_num_comps > (int)cMaxComponents))
  458. return false;
  459. const uint dst_width = params.m_dst_width;
  460. const uint dst_height = params.m_dst_height;
  461. if ((math::minimum(dst_width, dst_height) < 1) || (math::maximum(dst_width, dst_height) > CRNLIB_RESAMPLER_MAX_DIMENSION))
  462. {
  463. printf("Image is too large!\n");
  464. return EXIT_FAILURE;
  465. }
  466. if ((src_width == dst_width) && (src_height == dst_height))
  467. {
  468. dst = src;
  469. return true;
  470. }
  471. dst.clear();
  472. // Partial gamma correction looks better on mips. Set to 1.0 to disable gamma correction.
  473. const float source_gamma = params.m_source_gamma;//1.75f;
  474. float srgb_to_linear[256];
  475. if (params.m_srgb)
  476. {
  477. for (int i = 0; i < 256; ++i)
  478. srgb_to_linear[i] = (float)pow(i * 1.0f/255.0f, source_gamma);
  479. }
  480. const int linear_to_srgb_table_size = 8192;
  481. unsigned char linear_to_srgb[linear_to_srgb_table_size];
  482. const float inv_linear_to_srgb_table_size = 1.0f / linear_to_srgb_table_size;
  483. const float inv_source_gamma = 1.0f / source_gamma;
  484. if (params.m_srgb)
  485. {
  486. for (int i = 0; i < linear_to_srgb_table_size; ++i)
  487. {
  488. int k = (int)(255.0f * pow(i * inv_linear_to_srgb_table_size, inv_source_gamma) + .5f);
  489. if (k < 0) k = 0; else if (k > 255) k = 255;
  490. linear_to_srgb[i] = (unsigned char)k;
  491. }
  492. }
  493. task_pool tp;
  494. tp.init(g_number_of_processors - 1);
  495. threaded_resampler resampler(tp);
  496. threaded_resampler::params p;
  497. p.m_src_width = src_width;
  498. p.m_src_height = src_height;
  499. p.m_dst_width = dst_width;
  500. p.m_dst_height = dst_height;
  501. p.m_sample_low = 0.0f;
  502. p.m_sample_high = 1.0f;
  503. p.m_boundary_op = params.m_wrapping ? Resampler::BOUNDARY_WRAP : Resampler::BOUNDARY_CLAMP;
  504. p.m_Pfilter_name = params.m_pFilter;
  505. p.m_filter_x_scale = params.m_filter_scale;
  506. p.m_filter_y_scale = params.m_filter_scale;
  507. uint resampler_comps = 4;
  508. if (params.m_num_comps == 1)
  509. {
  510. p.m_fmt = threaded_resampler::cPF_Y_F32;
  511. resampler_comps = 1;
  512. }
  513. else if (params.m_num_comps <= 3)
  514. p.m_fmt = threaded_resampler::cPF_RGBX_F32;
  515. else
  516. p.m_fmt = threaded_resampler::cPF_RGBA_F32;
  517. crnlib::vector<float> src_samples;
  518. crnlib::vector<float> dst_samples;
  519. if (!src_samples.try_resize(src_width * src_height * resampler_comps))
  520. return false;
  521. if (!dst_samples.try_resize(dst_width * dst_height * resampler_comps))
  522. return false;
  523. p.m_pSrc_pixels = src_samples.get_ptr();
  524. p.m_src_pitch = src_width * resampler_comps * sizeof(float);
  525. p.m_pDst_pixels = dst_samples.get_ptr();
  526. p.m_dst_pitch = dst_width * resampler_comps * sizeof(float);
  527. for (uint src_y = 0; src_y < src_height; src_y++)
  528. {
  529. const color_quad_u8* pSrc = src.get_scanline(src_y);
  530. float* pDst = src_samples.get_ptr() + src_width * resampler_comps * src_y;
  531. for (uint x = 0; x < src_width; x++)
  532. {
  533. for (uint c = 0; c < params.m_num_comps; c++)
  534. {
  535. const uint comp_index = params.m_first_comp + c;
  536. const uint8 v = (*pSrc)[comp_index];
  537. if (!params.m_srgb || (comp_index == 3))
  538. pDst[c] = v * (1.0f/255.0f);
  539. else
  540. pDst[c] = srgb_to_linear[v];
  541. }
  542. pSrc++;
  543. pDst += resampler_comps;
  544. }
  545. }
  546. if (!resampler.resample(p))
  547. return false;
  548. src_samples.clear();
  549. if (!dst.resize(params.m_dst_width, params.m_dst_height))
  550. return false;
  551. for (uint dst_y = 0; dst_y < dst_height; dst_y++)
  552. {
  553. const float* pSrc = dst_samples.get_ptr() + dst_width * resampler_comps * dst_y;
  554. color_quad_u8* pDst = dst.get_scanline(dst_y);
  555. for (uint x = 0; x < dst_width; x++)
  556. {
  557. color_quad_u8 dst(0, 0, 0, 255);
  558. for (uint c = 0; c < params.m_num_comps; c++)
  559. {
  560. const uint comp_index = params.m_first_comp + c;
  561. const float v = pSrc[c];
  562. if ((!params.m_srgb) || (comp_index == 3))
  563. {
  564. int c = static_cast<int>(255.0f * v + .5f);
  565. if (c < 0) c = 0; else if (c > 255) c = 255;
  566. dst[comp_index] = (unsigned char)c;
  567. }
  568. else
  569. {
  570. int j = static_cast<int>(linear_to_srgb_table_size * v + .5f);
  571. if (j < 0) j = 0; else if (j >= linear_to_srgb_table_size) j = linear_to_srgb_table_size - 1;
  572. dst[comp_index] = linear_to_srgb[j];
  573. }
  574. }
  575. *pDst++ = dst;
  576. pSrc += resampler_comps;
  577. }
  578. }
  579. return true;
  580. }
  581. bool resample(const image_u8& src, image_u8& dst, const resample_params& params)
  582. {
  583. if ((params.m_multithreaded) && (g_number_of_processors > 1))
  584. return resample_multithreaded(src, dst, params);
  585. else
  586. return resample_single_thread(src, dst, params);
  587. }
  588. bool compute_delta(image_u8& dest, image_u8& a, image_u8& b, uint scale)
  589. {
  590. if ( (a.get_width() != b.get_width()) || (a.get_height() != b.get_height()) )
  591. return false;
  592. dest.resize(a.get_width(), b.get_height());
  593. for (uint y = 0; y < a.get_height(); y++)
  594. {
  595. for (uint x = 0; x < a.get_width(); x++)
  596. {
  597. const color_quad_u8& ca = a(x, y);
  598. const color_quad_u8& cb = b(x, y);
  599. color_quad_u8 cd;
  600. for (uint c = 0; c < 4; c++)
  601. {
  602. int d = (ca[c] - cb[c]) * scale + 128;
  603. d = math::clamp(d, 0, 255);
  604. cd[c] = static_cast<uint8>(d);
  605. }
  606. dest(x, y) = cd;
  607. }
  608. }
  609. return true;
  610. }
  611. // FIXME: Totally hack-ass computation.
  612. // Perhaps port http://www.lomont.org/Software/Misc/SSIM/SSIM.html?
  613. double compute_block_ssim(uint t, const uint8* pX, const uint8* pY)
  614. {
  615. double ave_x = 0.0f;
  616. double ave_y = 0.0f;
  617. for (uint i = 0; i < t; i++)
  618. {
  619. ave_x += pX[i];
  620. ave_y += pY[i];
  621. }
  622. ave_x /= t;
  623. ave_y /= t;
  624. double var_x = 0.0f;
  625. double var_y = 0.0f;
  626. for (uint i = 0; i < t; i++)
  627. {
  628. var_x += math::square(pX[i] - ave_x);
  629. var_y += math::square(pY[i] - ave_y);
  630. }
  631. var_x = sqrt(var_x / (t - 1));
  632. var_y = sqrt(var_y / (t - 1));
  633. double covar_xy = 0.0f;
  634. for (uint i = 0; i < t; i++)
  635. covar_xy += (pX[i] - ave_x) * (pY[i] - ave_y);
  636. covar_xy /= (t - 1);
  637. const double c1 = 6.5025; //(255*.01)^2
  638. const double c2 = 58.5225; //(255*.03)^2
  639. double n = (2.0f * ave_x * ave_y + c1) * (2.0f * covar_xy + c2);
  640. double d = (ave_x * ave_x + ave_y * ave_y + c1) * (var_x * var_x + var_y * var_y + c2);
  641. return n / d;
  642. }
  643. double compute_ssim(const image_u8& a, const image_u8& b, int channel_index)
  644. {
  645. const uint N = 6;
  646. uint8 sx[N*N], sy[N*N];
  647. double total_ssim = 0.0f;
  648. uint total_blocks = 0;
  649. //image_u8 yimg((a.get_width() + N - 1) / N, (a.get_height() + N - 1) / N);
  650. for (uint y = 0; y < a.get_height(); y += N)
  651. {
  652. for (uint x = 0; x < a.get_width(); x += N)
  653. {
  654. for (uint iy = 0; iy < N; iy++)
  655. {
  656. for (uint ix = 0; ix < N; ix++)
  657. {
  658. if (channel_index < 0)
  659. sx[ix+iy*N] = (uint8)a.get_clamped(x+ix, y+iy).get_luma();
  660. else
  661. sx[ix+iy*N] = (uint8)a.get_clamped(x+ix, y+iy)[channel_index];
  662. if (channel_index < 0)
  663. sy[ix+iy*N] = (uint8)b.get_clamped(x+ix, y+iy).get_luma();
  664. else
  665. sy[ix+iy*N] = (uint8)b.get_clamped(x+ix, y+iy)[channel_index];
  666. }
  667. }
  668. double ssim = compute_block_ssim(N*N, sx, sy);
  669. total_ssim += ssim;
  670. total_blocks++;
  671. //uint ssim_c = (uint)math::clamp<double>(ssim * 127.0f + 128.0f, 0, 255);
  672. //yimg(x / N, y / N).set(ssim_c, ssim_c, ssim_c, 255);
  673. }
  674. }
  675. if (!total_blocks)
  676. return 0.0f;
  677. //save_to_file_stb_or_miniz("ssim.tga", yimg, cWriteFlagGrayscale);
  678. return total_ssim / total_blocks;
  679. }
  680. void print_ssim(const image_u8& src_img, const image_u8& dst_img)
  681. {
  682. src_img;
  683. dst_img;
  684. //double y_ssim = compute_ssim(src_img, dst_img, -1);
  685. //console::printf("Luma MSSIM: %f, Scaled: %f", y_ssim, (y_ssim - .8f) / .2f);
  686. //double r_ssim = compute_ssim(src_img, dst_img, 0);
  687. //console::printf(" R MSSIM: %f", r_ssim);
  688. //double g_ssim = compute_ssim(src_img, dst_img, 1);
  689. //console::printf(" G MSSIM: %f", g_ssim);
  690. //double b_ssim = compute_ssim(src_img, dst_img, 2);
  691. //console::printf(" B MSSIM: %f", b_ssim);
  692. }
  693. void error_metrics::print(const char* pName) const
  694. {
  695. if (mPeakSNR >= cInfinitePSNR)
  696. console::printf("%s Error: Max: %3u, Mean: %3.3f, MSE: %3.3f, RMSE: %3.3f, PSNR: Infinite", pName, mMax, mMean, mMeanSquared, mRootMeanSquared);
  697. else
  698. console::printf("%s Error: Max: %3u, Mean: %3.3f, MSE: %3.3f, RMSE: %3.3f, PSNR: %3.3f", pName, mMax, mMean, mMeanSquared, mRootMeanSquared, mPeakSNR);
  699. }
  700. bool error_metrics::compute(const image_u8& a, const image_u8& b, uint first_channel, uint num_channels, bool average_component_error)
  701. {
  702. //if ( (!a.get_width()) || (!b.get_height()) || (a.get_width() != b.get_width()) || (a.get_height() != b.get_height()) )
  703. // return false;
  704. const uint width = math::minimum(a.get_width(), b.get_width());
  705. const uint height = math::minimum(a.get_height(), b.get_height());
  706. CRNLIB_ASSERT((first_channel < 4U) && (first_channel + num_channels <= 4U));
  707. // Histogram approach due to Charles Bloom.
  708. double hist[256];
  709. utils::zero_object(hist);
  710. for (uint y = 0; y < height; y++)
  711. {
  712. for (uint x = 0; x < width; x++)
  713. {
  714. const color_quad_u8& ca = a(x, y);
  715. const color_quad_u8& cb = b(x, y);
  716. if (!num_channels)
  717. hist[labs(ca.get_luma() - cb.get_luma())]++;
  718. else
  719. {
  720. for (uint c = 0; c < num_channels; c++)
  721. hist[labs(ca[first_channel + c] - cb[first_channel + c])]++;
  722. }
  723. }
  724. }
  725. mMax = 0;
  726. double sum = 0.0f, sum2 = 0.0f;
  727. for (uint i = 0; i < 256; i++)
  728. {
  729. if (!hist[i])
  730. continue;
  731. mMax = math::maximum(mMax, i);
  732. double x = i * hist[i];
  733. sum += x;
  734. sum2 += i * x;
  735. }
  736. // See http://bmrc.berkeley.edu/courseware/cs294/fall97/assignment/psnr.html
  737. double total_values = width * height;
  738. if (average_component_error)
  739. total_values *= math::clamp<uint>(num_channels, 1, 4);
  740. mMean = math::clamp<double>(sum / total_values, 0.0f, 255.0f);
  741. mMeanSquared = math::clamp<double>(sum2 / total_values, 0.0f, 255.0f*255.0f);
  742. mRootMeanSquared = sqrt(mMeanSquared);
  743. if (!mRootMeanSquared)
  744. mPeakSNR = cInfinitePSNR;
  745. else
  746. mPeakSNR = math::clamp<double>(log10(255.0f / mRootMeanSquared) * 20.0f, 0.0f, 500.0f);
  747. return true;
  748. }
  749. void print_image_metrics(const image_u8& src_img, const image_u8& dst_img)
  750. {
  751. if ( (!src_img.get_width()) || (!dst_img.get_height()) || (src_img.get_width() != dst_img.get_width()) || (src_img.get_height() != dst_img.get_height()) )
  752. console::printf("print_image_metrics: Image resolutions don't match exactly (%ux%u) vs. (%ux%u)", src_img.get_width(), src_img.get_height(), dst_img.get_width(), dst_img.get_height());
  753. image_utils::error_metrics error_metrics;
  754. if (src_img.has_rgb() || dst_img.has_rgb())
  755. {
  756. error_metrics.compute(src_img, dst_img, 0, 3, false);
  757. error_metrics.print("RGB Total ");
  758. error_metrics.compute(src_img, dst_img, 0, 3, true);
  759. error_metrics.print("RGB Average");
  760. error_metrics.compute(src_img, dst_img, 0, 0);
  761. error_metrics.print("Luma ");
  762. error_metrics.compute(src_img, dst_img, 0, 1);
  763. error_metrics.print("Red ");
  764. error_metrics.compute(src_img, dst_img, 1, 1);
  765. error_metrics.print("Green ");
  766. error_metrics.compute(src_img, dst_img, 2, 1);
  767. error_metrics.print("Blue ");
  768. }
  769. if (src_img.has_alpha() || dst_img.has_alpha())
  770. {
  771. error_metrics.compute(src_img, dst_img, 3, 1);
  772. error_metrics.print("Alpha ");
  773. }
  774. }
  775. static uint8 regen_z(uint x, uint y)
  776. {
  777. float vx = math::clamp((x - 128.0f) * 1.0f/127.0f, -1.0f, 1.0f);
  778. float vy = math::clamp((y - 128.0f) * 1.0f/127.0f, -1.0f, 1.0f);
  779. float vz = sqrt(math::clamp(1.0f - vx * vx - vy * vy, 0.0f, 1.0f));
  780. vz = vz * 127.0f + 128.0f;
  781. if (vz < 128.0f)
  782. vz -= .5f;
  783. else
  784. vz += .5f;
  785. int ib = math::float_to_int(vz);
  786. return static_cast<uint8>(math::clamp(ib, 0, 255));
  787. }
  788. void convert_image(image_u8& img, image_utils::conversion_type conv_type)
  789. {
  790. switch (conv_type)
  791. {
  792. case image_utils::cConversion_To_CCxY:
  793. {
  794. img.set_comp_flags(static_cast<pixel_format_helpers::component_flags>(pixel_format_helpers::cCompFlagRValid | pixel_format_helpers::cCompFlagGValid | pixel_format_helpers::cCompFlagAValid | pixel_format_helpers::cCompFlagLumaChroma));
  795. break;
  796. }
  797. case image_utils::cConversion_From_CCxY:
  798. {
  799. img.set_comp_flags(static_cast<pixel_format_helpers::component_flags>(pixel_format_helpers::cCompFlagRValid | pixel_format_helpers::cCompFlagGValid | pixel_format_helpers::cCompFlagBValid));
  800. break;
  801. }
  802. case image_utils::cConversion_To_xGxR:
  803. {
  804. img.set_comp_flags(static_cast<pixel_format_helpers::component_flags>(pixel_format_helpers::cCompFlagGValid | pixel_format_helpers::cCompFlagAValid | pixel_format_helpers::cCompFlagNormalMap));
  805. break;
  806. }
  807. case image_utils::cConversion_From_xGxR:
  808. {
  809. img.set_comp_flags(static_cast<pixel_format_helpers::component_flags>(pixel_format_helpers::cCompFlagRValid | pixel_format_helpers::cCompFlagGValid | pixel_format_helpers::cCompFlagBValid | pixel_format_helpers::cCompFlagNormalMap));
  810. break;
  811. }
  812. case image_utils::cConversion_To_xGBR:
  813. {
  814. img.set_comp_flags(static_cast<pixel_format_helpers::component_flags>(pixel_format_helpers::cCompFlagGValid | pixel_format_helpers::cCompFlagBValid | pixel_format_helpers::cCompFlagAValid | pixel_format_helpers::cCompFlagNormalMap));
  815. break;
  816. }
  817. case image_utils::cConversion_To_AGBR:
  818. {
  819. img.set_comp_flags(static_cast<pixel_format_helpers::component_flags>(pixel_format_helpers::cCompFlagRValid | pixel_format_helpers::cCompFlagGValid | pixel_format_helpers::cCompFlagBValid | pixel_format_helpers::cCompFlagAValid | pixel_format_helpers::cCompFlagNormalMap));
  820. break;
  821. }
  822. case image_utils::cConversion_From_xGBR:
  823. {
  824. img.set_comp_flags(static_cast<pixel_format_helpers::component_flags>(pixel_format_helpers::cCompFlagRValid | pixel_format_helpers::cCompFlagGValid | pixel_format_helpers::cCompFlagBValid | pixel_format_helpers::cCompFlagNormalMap));
  825. break;
  826. }
  827. case image_utils::cConversion_From_AGBR:
  828. {
  829. img.set_comp_flags(static_cast<pixel_format_helpers::component_flags>(pixel_format_helpers::cCompFlagRValid | pixel_format_helpers::cCompFlagGValid | pixel_format_helpers::cCompFlagBValid | pixel_format_helpers::cCompFlagAValid | pixel_format_helpers::cCompFlagNormalMap));
  830. break;
  831. }
  832. case image_utils::cConversion_XY_to_XYZ:
  833. {
  834. img.set_comp_flags(static_cast<pixel_format_helpers::component_flags>(pixel_format_helpers::cCompFlagRValid | pixel_format_helpers::cCompFlagGValid | pixel_format_helpers::cCompFlagBValid | pixel_format_helpers::cCompFlagNormalMap));
  835. break;
  836. }
  837. case cConversion_Y_To_A:
  838. {
  839. img.set_comp_flags(static_cast<pixel_format_helpers::component_flags>(img.get_comp_flags() | pixel_format_helpers::cCompFlagAValid));
  840. break;
  841. }
  842. case cConversion_A_To_RGBA:
  843. {
  844. img.set_comp_flags(static_cast<pixel_format_helpers::component_flags>(pixel_format_helpers::cCompFlagRValid | pixel_format_helpers::cCompFlagGValid | pixel_format_helpers::cCompFlagBValid | pixel_format_helpers::cCompFlagAValid));
  845. break;
  846. }
  847. case cConversion_Y_To_RGB:
  848. {
  849. img.set_comp_flags(static_cast<pixel_format_helpers::component_flags>(pixel_format_helpers::cCompFlagRValid | pixel_format_helpers::cCompFlagGValid | pixel_format_helpers::cCompFlagBValid | pixel_format_helpers::cCompFlagGrayscale | (img.has_alpha() ? pixel_format_helpers::cCompFlagAValid : 0)));
  850. break;
  851. }
  852. case cConversion_To_Y:
  853. {
  854. img.set_comp_flags(static_cast<pixel_format_helpers::component_flags>(img.get_comp_flags() | pixel_format_helpers::cCompFlagGrayscale));
  855. break;
  856. }
  857. default:
  858. {
  859. CRNLIB_ASSERT(false);
  860. return;
  861. }
  862. }
  863. for (uint y = 0; y < img.get_height(); y++)
  864. {
  865. for (uint x = 0; x < img.get_width(); x++)
  866. {
  867. color_quad_u8 src(img(x, y));
  868. color_quad_u8 dst;
  869. switch (conv_type)
  870. {
  871. case image_utils::cConversion_To_CCxY:
  872. {
  873. color::RGB_to_YCC(dst, src);
  874. break;
  875. }
  876. case image_utils::cConversion_From_CCxY:
  877. {
  878. color::YCC_to_RGB(dst, src);
  879. break;
  880. }
  881. case image_utils::cConversion_To_xGxR:
  882. {
  883. dst.r = 0;
  884. dst.g = src.g;
  885. dst.b = 0;
  886. dst.a = src.r;
  887. break;
  888. }
  889. case image_utils::cConversion_From_xGxR:
  890. {
  891. dst.r = src.a;
  892. dst.g = src.g;
  893. // This is kinda iffy, we're assuming the image is a normal map here.
  894. dst.b = regen_z(src.a, src.g);
  895. dst.a = 255;
  896. break;
  897. }
  898. case image_utils::cConversion_To_xGBR:
  899. {
  900. dst.r = 0;
  901. dst.g = src.g;
  902. dst.b = src.b;
  903. dst.a = src.r;
  904. break;
  905. }
  906. case image_utils::cConversion_To_AGBR:
  907. {
  908. dst.r = src.a;
  909. dst.g = src.g;
  910. dst.b = src.b;
  911. dst.a = src.r;
  912. break;
  913. }
  914. case image_utils::cConversion_From_xGBR:
  915. {
  916. dst.r = src.a;
  917. dst.g = src.g;
  918. dst.b = src.b;
  919. dst.a = 255;
  920. break;
  921. }
  922. case image_utils::cConversion_From_AGBR:
  923. {
  924. dst.r = src.a;
  925. dst.g = src.g;
  926. dst.b = src.b;
  927. dst.a = src.r;
  928. break;
  929. }
  930. case image_utils::cConversion_XY_to_XYZ:
  931. {
  932. dst.r = src.r;
  933. dst.g = src.g;
  934. // This is kinda iffy, we're assuming the image is a normal map here.
  935. dst.b = regen_z(src.r, src.g);
  936. dst.a = 255;
  937. break;
  938. }
  939. case image_utils::cConversion_Y_To_A:
  940. {
  941. dst.r = src.r;
  942. dst.g = src.g;
  943. dst.b = src.b;
  944. dst.a = static_cast<uint8>(src.get_luma());
  945. break;
  946. }
  947. case image_utils::cConversion_Y_To_RGB:
  948. {
  949. uint8 y = static_cast<uint8>(src.get_luma());
  950. dst.r = y;
  951. dst.g = y;
  952. dst.b = y;
  953. dst.a = src.a;
  954. break;
  955. }
  956. case image_utils::cConversion_A_To_RGBA:
  957. {
  958. dst.r = src.a;
  959. dst.g = src.a;
  960. dst.b = src.a;
  961. dst.a = src.a;
  962. break;
  963. }
  964. case image_utils::cConversion_To_Y:
  965. {
  966. uint8 y = static_cast<uint8>(src.get_luma());
  967. dst.r = y;
  968. dst.g = y;
  969. dst.b = y;
  970. dst.a = src.a;
  971. break;
  972. }
  973. default:
  974. {
  975. CRNLIB_ASSERT(false);
  976. dst = src;
  977. break;
  978. }
  979. }
  980. img(x, y) = dst;
  981. }
  982. }
  983. }
  984. image_utils::conversion_type get_conversion_type(bool cooking, pixel_format fmt)
  985. {
  986. image_utils::conversion_type conv_type = image_utils::cConversion_Invalid;
  987. if (cooking)
  988. {
  989. switch (fmt)
  990. {
  991. case PIXEL_FMT_DXT5_CCxY:
  992. {
  993. conv_type = image_utils::cConversion_To_CCxY;
  994. break;
  995. }
  996. case PIXEL_FMT_DXT5_xGxR:
  997. {
  998. conv_type = image_utils::cConversion_To_xGxR;
  999. break;
  1000. }
  1001. case PIXEL_FMT_DXT5_xGBR:
  1002. {
  1003. conv_type = image_utils::cConversion_To_xGBR;
  1004. break;
  1005. }
  1006. case PIXEL_FMT_DXT5_AGBR:
  1007. {
  1008. conv_type = image_utils::cConversion_To_AGBR;
  1009. break;
  1010. }
  1011. default: break;
  1012. }
  1013. }
  1014. else
  1015. {
  1016. switch (fmt)
  1017. {
  1018. case PIXEL_FMT_3DC:
  1019. case PIXEL_FMT_DXN:
  1020. {
  1021. conv_type = image_utils::cConversion_XY_to_XYZ;
  1022. break;
  1023. }
  1024. case PIXEL_FMT_DXT5_CCxY:
  1025. {
  1026. conv_type = image_utils::cConversion_From_CCxY;
  1027. break;
  1028. }
  1029. case PIXEL_FMT_DXT5_xGxR:
  1030. {
  1031. conv_type = image_utils::cConversion_From_xGxR;
  1032. break;
  1033. }
  1034. case PIXEL_FMT_DXT5_xGBR:
  1035. {
  1036. conv_type = image_utils::cConversion_From_xGBR;
  1037. break;
  1038. }
  1039. case PIXEL_FMT_DXT5_AGBR:
  1040. {
  1041. conv_type = image_utils::cConversion_From_AGBR;
  1042. break;
  1043. }
  1044. default: break;
  1045. }
  1046. }
  1047. return conv_type;
  1048. }
  1049. image_utils::conversion_type get_image_conversion_type_from_crn_format(crn_format fmt)
  1050. {
  1051. switch (fmt)
  1052. {
  1053. case cCRNFmtDXT5_CCxY: return image_utils::cConversion_To_CCxY;
  1054. case cCRNFmtDXT5_xGxR: return image_utils::cConversion_To_xGxR;
  1055. case cCRNFmtDXT5_xGBR: return image_utils::cConversion_To_xGBR;
  1056. case cCRNFmtDXT5_AGBR: return image_utils::cConversion_To_AGBR;
  1057. default: break;
  1058. }
  1059. return image_utils::cConversion_Invalid;
  1060. }
  1061. double compute_std_dev(uint n, const color_quad_u8* pPixels, uint first_channel, uint num_channels)
  1062. {
  1063. if (!n)
  1064. return 0.0f;
  1065. double sum = 0.0f;
  1066. double sum2 = 0.0f;
  1067. for (uint i = 0; i < n; i++)
  1068. {
  1069. const color_quad_u8& cp = pPixels[i];
  1070. if (!num_channels)
  1071. {
  1072. uint l = cp.get_luma();
  1073. sum += l;
  1074. sum2 += l*l;
  1075. }
  1076. else
  1077. {
  1078. for (uint c = 0; c < num_channels; c++)
  1079. {
  1080. uint l = cp[first_channel + c];
  1081. sum += l;
  1082. sum2 += l*l;
  1083. }
  1084. }
  1085. }
  1086. double w = math::maximum(1U, num_channels) * n;
  1087. sum /= w;
  1088. sum2 /= w;
  1089. double var = sum2 - sum * sum;
  1090. var = math::maximum<double>(var, 0.0f);
  1091. return sqrt(var);
  1092. }
  1093. uint8* read_image_from_memory(const uint8* pImage, int nSize, int* pWidth, int* pHeight, int* pActualComps, int req_comps, const char* pFilename)
  1094. {
  1095. *pWidth = 0;
  1096. *pHeight = 0;
  1097. *pActualComps = 0;
  1098. if ((req_comps < 1) || (req_comps > 4))
  1099. return false;
  1100. mipmapped_texture tex;
  1101. buffer_stream buf_stream(pImage, nSize);
  1102. buf_stream.set_name(pFilename);
  1103. data_stream_serializer serializer(buf_stream);
  1104. if (!tex.read_from_stream(serializer))
  1105. return NULL;
  1106. if (tex.is_packed())
  1107. {
  1108. if (!tex.unpack_from_dxt(true))
  1109. return NULL;
  1110. }
  1111. image_u8 img;
  1112. image_u8* pImg = tex.get_level_image(0, 0, img);
  1113. if (!pImg)
  1114. return NULL;
  1115. *pWidth = tex.get_width();
  1116. *pHeight = tex.get_height();
  1117. if (pImg->has_alpha())
  1118. *pActualComps = 4;
  1119. else if (pImg->is_grayscale())
  1120. *pActualComps = 1;
  1121. else
  1122. *pActualComps = 3;
  1123. uint8 *pDst = NULL;
  1124. if (req_comps == 4)
  1125. {
  1126. pDst = (uint8*)malloc(tex.get_total_pixels() * sizeof(uint32));
  1127. uint8 *pSrc = (uint8*)pImg->get_ptr();
  1128. memcpy(pDst, pSrc, tex.get_total_pixels() * sizeof(uint32));
  1129. }
  1130. else
  1131. {
  1132. image_u8 luma_img;
  1133. if (req_comps == 1)
  1134. {
  1135. luma_img = *pImg;
  1136. luma_img.convert_to_grayscale();
  1137. pImg = &luma_img;
  1138. }
  1139. pixel_packer packer(req_comps, 8);
  1140. uint32 n;
  1141. pDst = image_utils::pack_image(*pImg, packer, n);
  1142. }
  1143. return pDst;
  1144. }
  1145. } // namespace image_utils
  1146. } // namespace crnlib