texturec.cpp 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201
  1. /*
  2. * Copyright 2011-2018 Branimir Karadzic. All rights reserved.
  3. * License: https://github.com/bkaradzic/bimg#license-bsd-2-clause
  4. */
  5. #include <stdio.h>
  6. #include <bx/allocator.h>
  7. #include <bx/readerwriter.h>
  8. #include <bx/endian.h>
  9. #include <bx/math.h>
  10. #include <bimg/decode.h>
  11. #include <bimg/encode.h>
  12. #if 0
  13. # define DBG(_format, ...) fprintf(stderr, "" _format "\n", ##__VA_ARGS__)
  14. #else
  15. # define DBG(...) BX_NOOP()
  16. #endif // DEBUG
  17. #include <bx/bx.h>
  18. #include <bx/commandline.h>
  19. #include <bx/file.h>
  20. #include <bx/uint32_t.h>
  21. #include <string>
  22. #define BIMG_TEXTUREC_VERSION_MAJOR 1
  23. #define BIMG_TEXTUREC_VERSION_MINOR 17
  24. BX_ERROR_RESULT(TEXTRUREC_ERROR, BX_MAKEFOURCC('t', 'c', 0, 0) );
  25. struct Options
  26. {
  27. Options()
  28. : maxSize(UINT32_MAX)
  29. , edge(0.0f)
  30. , format(bimg::TextureFormat::Count)
  31. , quality(bimg::Quality::Default)
  32. , mips(false)
  33. , normalMap(false)
  34. , equirect(false)
  35. , iqa(false)
  36. , pma(false)
  37. , sdf(false)
  38. , alphaTest(false)
  39. {
  40. }
  41. void dump()
  42. {
  43. DBG("Options:\n"
  44. "\t maxSize: %d\n"
  45. "\t edge: %f\n"
  46. "\t format: %s\n"
  47. "\t mips: %s\n"
  48. "\tnormalMap: %s\n"
  49. "\t iqa: %s\n"
  50. "\t pma: %s\n"
  51. "\t sdf: %s\n"
  52. "\t radiance: %s\n"
  53. , maxSize
  54. , edge
  55. , bimg::getName(format)
  56. , mips ? "true" : "false"
  57. , normalMap ? "true" : "false"
  58. , iqa ? "true" : "false"
  59. , pma ? "true" : "false"
  60. , sdf ? "true" : "false"
  61. , radiance ? "true" : "false"
  62. );
  63. }
  64. uint32_t maxSize;
  65. float edge;
  66. bimg::TextureFormat::Enum format;
  67. bimg::Quality::Enum quality;
  68. bool mips;
  69. bool normalMap;
  70. bool equirect;
  71. bool iqa;
  72. bool pma;
  73. bool sdf;
  74. bool alphaTest;
  75. bool radiance;
  76. };
  77. void imageRgba32fNormalize(void* _dst, uint32_t _width, uint32_t _height, uint32_t _srcPitch, const void* _src)
  78. {
  79. const uint8_t* src = (const uint8_t*)_src;
  80. uint8_t* dst = (uint8_t*)_dst;
  81. for (uint32_t yy = 0, ystep = _srcPitch; yy < _height; ++yy, src += ystep)
  82. {
  83. const float* rgba = (const float*)&src[0];
  84. for (uint32_t xx = 0; xx < _width; ++xx, rgba += 4, dst += 16)
  85. {
  86. float xyz[3];
  87. xyz[0] = rgba[0];
  88. xyz[1] = rgba[1];
  89. xyz[2] = rgba[2];
  90. bx::vec3Norm( (float*)dst, xyz);
  91. }
  92. }
  93. }
  94. void imagePremultiplyAlpha(void* _inOut, uint32_t _width, uint32_t _height, uint32_t _depth, bimg::TextureFormat::Enum _format)
  95. {
  96. uint8_t* inOut = (uint8_t*)_inOut;
  97. uint32_t bpp = bimg::getBitsPerPixel(_format);
  98. uint32_t pitch = _width*bpp/8;
  99. bimg::PackFn pack = bimg::getPack(_format);
  100. bimg::UnpackFn unpack = bimg::getUnpack(_format);
  101. for (uint32_t zz = 0; zz < _depth; ++zz)
  102. {
  103. for (uint32_t yy = 0; yy < _height; ++yy)
  104. {
  105. for (uint32_t xx = 0; xx < _width; ++xx)
  106. {
  107. const uint32_t offset = yy*pitch + xx*bpp/8;
  108. float rgba[4];
  109. unpack(rgba, &inOut[offset]);
  110. const float alpha = rgba[3];
  111. rgba[0] = bx::toGamma(bx::toLinear(rgba[0]) * alpha);
  112. rgba[1] = bx::toGamma(bx::toLinear(rgba[1]) * alpha);
  113. rgba[2] = bx::toGamma(bx::toLinear(rgba[2]) * alpha);
  114. pack(&inOut[offset], rgba);
  115. }
  116. }
  117. }
  118. }
  119. bimg::ImageContainer* convert(bx::AllocatorI* _allocator, const void* _inputData, uint32_t _inputSize, const Options& _options, bx::Error* _err)
  120. {
  121. BX_ERROR_SCOPE(_err);
  122. const uint8_t* inputData = (uint8_t*)_inputData;
  123. bimg::ImageContainer* output = NULL;
  124. bimg::ImageContainer* input = bimg::imageParse(_allocator, inputData, _inputSize, bimg::TextureFormat::Count, _err);
  125. if (!_err->isOk() )
  126. {
  127. return NULL;
  128. }
  129. if (NULL != input)
  130. {
  131. bimg::TextureFormat::Enum inputFormat = input->m_format;
  132. bimg::TextureFormat::Enum outputFormat = input->m_format;
  133. if (bimg::TextureFormat::Count != _options.format)
  134. {
  135. outputFormat = _options.format;
  136. }
  137. if (_options.sdf)
  138. {
  139. outputFormat = bimg::TextureFormat::R8;
  140. }
  141. const bimg::ImageBlockInfo& inputBlockInfo = bimg::getBlockInfo(inputFormat);
  142. const bimg::ImageBlockInfo& outputBlockInfo = bimg::getBlockInfo(outputFormat);
  143. const uint32_t blockWidth = outputBlockInfo.blockWidth;
  144. const uint32_t blockHeight = outputBlockInfo.blockHeight;
  145. const uint32_t minBlockX = outputBlockInfo.minBlockX;
  146. const uint32_t minBlockY = outputBlockInfo.minBlockY;
  147. uint32_t outputWidth = bx::uint32_max(blockWidth * minBlockX, ( (input->m_width + blockWidth - 1) / blockWidth )*blockWidth);
  148. uint32_t outputHeight = bx::uint32_max(blockHeight * minBlockY, ( (input->m_height + blockHeight - 1) / blockHeight)*blockHeight);
  149. uint32_t outputDepth = input->m_depth;
  150. if (_options.equirect)
  151. {
  152. if (outputDepth == 1
  153. && outputWidth/2 == outputHeight)
  154. {
  155. if (outputWidth/2 > _options.maxSize)
  156. {
  157. outputWidth = _options.maxSize*4;
  158. outputHeight = _options.maxSize*2;
  159. }
  160. }
  161. else
  162. {
  163. bimg::imageFree(input);
  164. BX_ERROR_SET(_err, TEXTRUREC_ERROR, "Input image format is not equirectangular projection.");
  165. return NULL;
  166. }
  167. }
  168. else if (outputWidth > _options.maxSize
  169. || outputHeight > _options.maxSize
  170. || outputDepth > _options.maxSize)
  171. {
  172. if (outputDepth > outputWidth
  173. && outputDepth > outputHeight)
  174. {
  175. outputWidth = outputWidth * _options.maxSize / outputDepth;
  176. outputHeight = outputHeight * _options.maxSize / outputDepth;
  177. outputDepth = _options.maxSize;
  178. }
  179. else if (outputWidth > outputHeight)
  180. {
  181. outputDepth = outputDepth * _options.maxSize / outputWidth;
  182. outputHeight = outputHeight * _options.maxSize / outputWidth;
  183. outputWidth = _options.maxSize;
  184. }
  185. else
  186. {
  187. outputDepth = outputDepth * _options.maxSize / outputHeight;
  188. outputWidth = outputWidth * _options.maxSize / outputHeight;
  189. outputHeight = _options.maxSize;
  190. }
  191. }
  192. const bool needResize = false
  193. || input->m_width != outputWidth
  194. || input->m_height != outputHeight
  195. ;
  196. const bool passThru = true
  197. && !needResize
  198. && (1 < input->m_numMips) == _options.mips
  199. && !_options.sdf
  200. && !_options.alphaTest
  201. && !_options.normalMap
  202. && !_options.equirect
  203. && !_options.iqa
  204. && !_options.pma
  205. && !_options.radiance
  206. ;
  207. if (needResize)
  208. {
  209. bimg::ImageContainer* src = bimg::imageConvert(_allocator, bimg::TextureFormat::RGBA32F, *input, false);
  210. bimg::ImageContainer* dst = bimg::imageAlloc(
  211. _allocator
  212. , bimg::TextureFormat::RGBA32F
  213. , uint16_t(outputWidth)
  214. , uint16_t(outputHeight)
  215. , uint16_t(outputDepth)
  216. , input->m_numLayers
  217. , input->m_cubeMap
  218. , false
  219. );
  220. bimg::imageResizeRgba32fLinear(dst, src);
  221. bimg::imageFree(src);
  222. bimg::imageFree(input);
  223. if (bimg::isCompressed(inputFormat) )
  224. {
  225. if (inputFormat == bimg::TextureFormat::BC6H)
  226. {
  227. inputFormat = bimg::TextureFormat::RGBA32F;
  228. }
  229. else
  230. {
  231. inputFormat = bimg::TextureFormat::RGBA8;
  232. }
  233. }
  234. input = bimg::imageConvert(_allocator, inputFormat, *dst);
  235. bimg::imageFree(dst);
  236. }
  237. if (passThru)
  238. {
  239. if (inputFormat != outputFormat
  240. && bimg::isCompressed(outputFormat) )
  241. {
  242. output = bimg::imageEncode(_allocator, outputFormat, _options.quality, *input);
  243. }
  244. else
  245. {
  246. output = bimg::imageConvert(_allocator, outputFormat, *input);
  247. }
  248. bimg::imageFree(input);
  249. return output;
  250. }
  251. if (_options.equirect)
  252. {
  253. bimg::ImageContainer* src = bimg::imageConvert(_allocator, bimg::TextureFormat::RGBA32F, *input);
  254. bimg::imageFree(input);
  255. bimg::ImageContainer* dst = bimg::imageCubemapFromLatLongRgba32F(_allocator, *src, true, _err);
  256. bimg::imageFree(src);
  257. if (!_err->isOk() )
  258. {
  259. return NULL;
  260. }
  261. input = bimg::imageConvert(_allocator, inputFormat, *dst);
  262. bimg::imageFree(dst);
  263. }
  264. if (_options.radiance)
  265. {
  266. output = bimg::imageCubemapRadianceFilter(_allocator, *input);
  267. if (bimg::TextureFormat::RGBA32F != outputFormat)
  268. {
  269. bimg::ImageContainer* temp = bimg::imageEncode(_allocator, outputFormat, _options.quality, *output);
  270. bimg::imageFree(output);
  271. output = temp;
  272. }
  273. bimg::imageFree(input);
  274. return output;
  275. }
  276. output = bimg::imageAlloc(
  277. _allocator
  278. , outputFormat
  279. , uint16_t(input->m_width)
  280. , uint16_t(input->m_height)
  281. , uint16_t(input->m_depth)
  282. , input->m_numLayers
  283. , input->m_cubeMap
  284. , _options.mips
  285. );
  286. const uint8_t numMips = output->m_numMips;
  287. const uint16_t numSides = output->m_numLayers * (output->m_cubeMap ? 6 : 1);
  288. for (uint16_t side = 0; side < numSides && _err->isOk(); ++side)
  289. {
  290. bimg::ImageMip mip;
  291. if (bimg::imageGetRawData(*input, side, 0, input->m_data, input->m_size, mip) )
  292. {
  293. bimg::ImageMip dstMip;
  294. bimg::imageGetRawData(*output, side, 0, output->m_data, output->m_size, dstMip);
  295. uint8_t* dstData = const_cast<uint8_t*>(dstMip.m_data);
  296. void* temp = NULL;
  297. // Normal map.
  298. if (_options.normalMap)
  299. {
  300. uint32_t size = bimg::imageGetSize(
  301. NULL
  302. , uint16_t(dstMip.m_width)
  303. , uint16_t(dstMip.m_height)
  304. , 0
  305. , false
  306. , false
  307. , 1
  308. , bimg::TextureFormat::RGBA32F
  309. );
  310. temp = BX_ALLOC(_allocator, size);
  311. float* rgba = (float*)temp;
  312. float* rgbaDst = (float*)BX_ALLOC(_allocator, size);
  313. bimg::imageDecodeToRgba32f(_allocator
  314. , rgba
  315. , mip.m_data
  316. , dstMip.m_width
  317. , dstMip.m_height
  318. , dstMip.m_depth
  319. , dstMip.m_width*16
  320. , mip.m_format
  321. );
  322. if (bimg::TextureFormat::BC5 != mip.m_format)
  323. {
  324. for (uint32_t yy = 0; yy < mip.m_height; ++yy)
  325. {
  326. for (uint32_t xx = 0; xx < mip.m_width; ++xx)
  327. {
  328. const uint32_t offset = (yy*mip.m_width + xx) * 4;
  329. float* inout = &rgba[offset];
  330. inout[0] = inout[0] * 2.0f - 1.0f;
  331. inout[1] = inout[1] * 2.0f - 1.0f;
  332. inout[2] = inout[2] * 2.0f - 1.0f;
  333. inout[3] = inout[3] * 2.0f - 1.0f;
  334. }
  335. }
  336. }
  337. imageRgba32fNormalize(rgba
  338. , dstMip.m_width
  339. , dstMip.m_height
  340. , dstMip.m_width*16
  341. , rgba
  342. );
  343. bimg::imageRgba32f11to01(rgbaDst
  344. , dstMip.m_width
  345. , dstMip.m_height
  346. , dstMip.m_depth
  347. , dstMip.m_width*16
  348. , rgba
  349. );
  350. bimg::imageEncodeFromRgba32f(_allocator
  351. , dstData
  352. , rgbaDst
  353. , dstMip.m_width
  354. , dstMip.m_height
  355. , dstMip.m_depth
  356. , outputFormat
  357. , _options.quality
  358. , _err
  359. );
  360. for (uint8_t lod = 1; lod < numMips && _err->isOk(); ++lod)
  361. {
  362. bimg::imageRgba32fDownsample2x2NormalMap(rgba
  363. , dstMip.m_width
  364. , dstMip.m_height
  365. , dstMip.m_width*16
  366. , bx::strideAlign(dstMip.m_width/2, blockWidth)*16
  367. , rgba
  368. );
  369. bimg::imageRgba32f11to01(rgbaDst
  370. , dstMip.m_width
  371. , dstMip.m_height
  372. , dstMip.m_depth
  373. , dstMip.m_width*16
  374. , rgba
  375. );
  376. bimg::imageGetRawData(*output, side, lod, output->m_data, output->m_size, dstMip);
  377. dstData = const_cast<uint8_t*>(dstMip.m_data);
  378. bimg::imageEncodeFromRgba32f(_allocator
  379. , dstData
  380. , rgbaDst
  381. , dstMip.m_width
  382. , dstMip.m_height
  383. , dstMip.m_depth
  384. , outputFormat
  385. , _options.quality
  386. , _err
  387. );
  388. }
  389. BX_FREE(_allocator, rgbaDst);
  390. }
  391. // HDR
  392. else if ( (!bimg::isCompressed(inputFormat) && 8 != inputBlockInfo.rBits)
  393. || outputFormat == bimg::TextureFormat::BC6H
  394. || outputFormat == bimg::TextureFormat::BC7
  395. )
  396. {
  397. uint32_t size = bimg::imageGetSize(
  398. NULL
  399. , uint16_t(dstMip.m_width)
  400. , uint16_t(dstMip.m_height)
  401. , uint16_t(dstMip.m_depth)
  402. , false
  403. , false
  404. , 1
  405. , bimg::TextureFormat::RGBA32F
  406. );
  407. temp = BX_ALLOC(_allocator, size);
  408. float* rgba32f = (float*)temp;
  409. float* rgbaDst = (float*)BX_ALLOC(_allocator, size);
  410. bimg::imageDecodeToRgba32f(_allocator
  411. , rgba32f
  412. , mip.m_data
  413. , mip.m_width
  414. , mip.m_height
  415. , mip.m_depth
  416. , mip.m_width*16
  417. , mip.m_format
  418. );
  419. if (_options.pma)
  420. {
  421. imagePremultiplyAlpha(
  422. rgba32f
  423. , dstMip.m_width
  424. , dstMip.m_height
  425. , dstMip.m_depth
  426. , bimg::TextureFormat::RGBA32F
  427. );
  428. }
  429. bimg::imageEncodeFromRgba32f(_allocator
  430. , dstData
  431. , rgba32f
  432. , dstMip.m_width
  433. , dstMip.m_height
  434. , dstMip.m_depth
  435. , outputFormat
  436. , _options.quality
  437. , _err
  438. );
  439. if (1 < numMips
  440. && _err->isOk() )
  441. {
  442. bimg::imageRgba32fToLinear(rgba32f
  443. , mip.m_width
  444. , mip.m_height
  445. , mip.m_depth
  446. , mip.m_width*16
  447. , rgba32f
  448. );
  449. for (uint8_t lod = 1; lod < numMips && _err->isOk(); ++lod)
  450. {
  451. bimg::imageRgba32fLinearDownsample2x2(rgba32f
  452. , dstMip.m_width
  453. , dstMip.m_height
  454. , dstMip.m_depth
  455. , dstMip.m_width*16
  456. , rgba32f
  457. );
  458. if (_options.pma)
  459. {
  460. imagePremultiplyAlpha(
  461. rgba32f
  462. , dstMip.m_width
  463. , dstMip.m_height
  464. , dstMip.m_depth
  465. , bimg::TextureFormat::RGBA32F
  466. );
  467. }
  468. bimg::imageGetRawData(*output, side, lod, output->m_data, output->m_size, dstMip);
  469. dstData = const_cast<uint8_t*>(dstMip.m_data);
  470. bimg::imageRgba32fToGamma(rgbaDst
  471. , mip.m_width
  472. , mip.m_height
  473. , mip.m_depth
  474. , mip.m_width*16
  475. , rgba32f
  476. );
  477. bimg::imageEncodeFromRgba32f(_allocator
  478. , dstData
  479. , rgbaDst
  480. , dstMip.m_width
  481. , dstMip.m_height
  482. , dstMip.m_depth
  483. , outputFormat
  484. , _options.quality
  485. , _err
  486. );
  487. }
  488. }
  489. BX_FREE(_allocator, rgbaDst);
  490. }
  491. // SDF
  492. else if (_options.sdf)
  493. {
  494. uint32_t size = bimg::imageGetSize(
  495. NULL
  496. , uint16_t(dstMip.m_width)
  497. , uint16_t(dstMip.m_height)
  498. , uint16_t(dstMip.m_depth)
  499. , false
  500. , false
  501. , 1
  502. , bimg::TextureFormat::R8
  503. );
  504. temp = BX_ALLOC(_allocator, size);
  505. uint8_t* rgba = (uint8_t*)temp;
  506. bimg::imageDecodeToR8(_allocator
  507. , rgba
  508. , mip.m_data
  509. , mip.m_width
  510. , mip.m_height
  511. , mip.m_depth
  512. , mip.m_width
  513. , mip.m_format
  514. );
  515. bimg::imageGetRawData(*output, side, 0, output->m_data, output->m_size, dstMip);
  516. dstData = const_cast<uint8_t*>(dstMip.m_data);
  517. bimg::imageMakeDist(_allocator
  518. , dstData
  519. , mip.m_width
  520. , mip.m_height
  521. , mip.m_width
  522. , _options.edge
  523. , rgba
  524. );
  525. }
  526. // RGBA8
  527. else
  528. {
  529. uint32_t size = bimg::imageGetSize(
  530. NULL
  531. , uint16_t(dstMip.m_width)
  532. , uint16_t(dstMip.m_height)
  533. , uint16_t(dstMip.m_depth)
  534. , false
  535. , false
  536. , 1
  537. , bimg::TextureFormat::RGBA8
  538. );
  539. temp = BX_ALLOC(_allocator, size);
  540. uint8_t* rgba = (uint8_t*)temp;
  541. bimg::imageDecodeToRgba8(
  542. _allocator
  543. , rgba
  544. , mip.m_data
  545. , mip.m_width
  546. , mip.m_height
  547. , mip.m_width*4
  548. , mip.m_format
  549. );
  550. float coverage = 0.0f;
  551. if (_options.alphaTest)
  552. {
  553. coverage = bimg::imageAlphaTestCoverage(bimg::TextureFormat::RGBA8
  554. , mip.m_width
  555. , mip.m_height
  556. , mip.m_width*4
  557. , rgba
  558. , _options.edge
  559. );
  560. }
  561. void* ref = NULL;
  562. if (_options.iqa)
  563. {
  564. ref = BX_ALLOC(_allocator, size);
  565. bx::memCopy(ref, rgba, size);
  566. }
  567. if (_options.pma)
  568. {
  569. imagePremultiplyAlpha(
  570. rgba
  571. , dstMip.m_width
  572. , dstMip.m_height
  573. , dstMip.m_depth
  574. , bimg::TextureFormat::RGBA8
  575. );
  576. }
  577. bimg::imageGetRawData(*output, side, 0, output->m_data, output->m_size, dstMip);
  578. dstData = const_cast<uint8_t*>(dstMip.m_data);
  579. bimg::imageEncodeFromRgba8(
  580. _allocator
  581. , dstData
  582. , rgba
  583. , dstMip.m_width
  584. , dstMip.m_height
  585. , dstMip.m_depth
  586. , outputFormat
  587. , _options.quality
  588. , _err
  589. );
  590. for (uint8_t lod = 1; lod < numMips && _err->isOk(); ++lod)
  591. {
  592. bimg::imageRgba8Downsample2x2(rgba
  593. , dstMip.m_width
  594. , dstMip.m_height
  595. , dstMip.m_depth
  596. , dstMip.m_width*4
  597. , bx::strideAlign(dstMip.m_width/2, blockWidth)*4
  598. , rgba
  599. );
  600. if (_options.alphaTest)
  601. {
  602. bimg::imageScaleAlphaToCoverage(bimg::TextureFormat::RGBA8
  603. , dstMip.m_width
  604. , dstMip.m_height
  605. , dstMip.m_width*4
  606. , rgba
  607. , coverage
  608. , _options.edge
  609. );
  610. }
  611. if (_options.pma)
  612. {
  613. imagePremultiplyAlpha(
  614. rgba
  615. , dstMip.m_width
  616. , dstMip.m_height
  617. , dstMip.m_depth
  618. , bimg::TextureFormat::RGBA8
  619. );
  620. }
  621. bimg::imageGetRawData(*output, side, lod, output->m_data, output->m_size, dstMip);
  622. dstData = const_cast<uint8_t*>(dstMip.m_data);
  623. bimg::imageEncodeFromRgba8(
  624. _allocator
  625. , dstData
  626. , rgba
  627. , dstMip.m_width
  628. , dstMip.m_height
  629. , dstMip.m_depth
  630. , outputFormat
  631. , _options.quality
  632. , _err
  633. );
  634. }
  635. if (NULL != ref)
  636. {
  637. bimg::imageDecodeToRgba8(
  638. _allocator
  639. , rgba
  640. , output->m_data
  641. , mip.m_width
  642. , mip.m_height
  643. , mip.m_width*mip.m_bpp/8
  644. , outputFormat
  645. );
  646. float result = bimg::imageQualityRgba8(
  647. ref
  648. , rgba
  649. , uint16_t(mip.m_width)
  650. , uint16_t(mip.m_height)
  651. );
  652. printf("%f\n", result);
  653. BX_FREE(_allocator, ref);
  654. }
  655. }
  656. BX_FREE(_allocator, temp);
  657. }
  658. }
  659. bimg::imageFree(input);
  660. }
  661. if (!_err->isOk()
  662. && NULL != output)
  663. {
  664. bimg::imageFree(output);
  665. output = NULL;
  666. }
  667. return output;
  668. }
  669. void help(const char* _error = NULL, bool _showHelp = true)
  670. {
  671. if (NULL != _error)
  672. {
  673. fprintf(stderr, "Error:\n%s\n\n", _error);
  674. if (!_showHelp)
  675. {
  676. return;
  677. }
  678. }
  679. fprintf(stderr
  680. , "texturec, bgfx texture compiler tool, version %d.%d.%d.\n"
  681. "Copyright 2011-2018 Branimir Karadzic. All rights reserved.\n"
  682. "License: https://github.com/bkaradzic/bimg#license-bsd-2-clause\n\n"
  683. , BIMG_TEXTUREC_VERSION_MAJOR
  684. , BIMG_TEXTUREC_VERSION_MINOR
  685. , BIMG_API_VERSION
  686. );
  687. fprintf(stderr
  688. , "Usage: texturec -f <in> -o <out> [-t <texture format>]\n"
  689. "\n"
  690. "Supported file formats:\n"
  691. " *.bmp (input) Windows Bitmap.\n"
  692. " *.dds (input, output) Direct Draw Surface.\n"
  693. " *.exr (input, output) OpenEXR.\n"
  694. " *.gif (input) Graphics Interchange Format.\n"
  695. " *.jpg (input) JPEG Interchange Format.\n"
  696. " *.hdr (input, output) Radiance RGBE.\n"
  697. " *.ktx (input, output) Khronos Texture.\n"
  698. " *.png (input, output) Portable Network Graphics.\n"
  699. " *.psd (input) Photoshop Document.\n"
  700. " *.pvr (input) PowerVR.\n"
  701. " *.tga (input) Targa.\n"
  702. "\n"
  703. "Options:\n"
  704. " -h, --help Help.\n"
  705. " -v, --version Version information only.\n"
  706. " -f <file path> Input file path.\n"
  707. " -o <file path> Output file path.\n"
  708. " -t <format> Output format type (BC1/2/3/4/5, ETC1, PVR14, etc.).\n"
  709. " -q <quality> Encoding quality (default, fastest, highest).\n"
  710. " -m, --mips Generate mip-maps.\n"
  711. " -n, --normalmap Input texture is normal map.\n"
  712. " --equirect Input texture equirectangular projection of cubemap.\n"
  713. " --sdf <edge> Compute SDF texture.\n"
  714. " --ref <alpha> Alpha reference value.\n"
  715. " --iqa Image Quality Assessment\n"
  716. " --pma Premultiply alpha into RGB channel.\n"
  717. " --max <max size> Maximum width/height (image will be scaled down and\n"
  718. " aspect ratio will be preserved.\n"
  719. " --as <extension> Save as.\n"
  720. " --validate *DEBUG* Validate that output image produced matches after loading.\n"
  721. "\n"
  722. "For additional information, see https://github.com/bkaradzic/bgfx\n"
  723. );
  724. }
  725. void help(const char* _str, const bx::Error& _err)
  726. {
  727. std::string str;
  728. if (_str != NULL)
  729. {
  730. str.append(_str);
  731. str.append(" ");
  732. }
  733. const bx::StringView& sv = _err.getMessage();
  734. str.append(sv.getPtr(), sv.getTerm() - sv.getPtr() );
  735. help(str.c_str(), false);
  736. }
  737. class AlignedAllocator : public bx::AllocatorI
  738. {
  739. public:
  740. AlignedAllocator(bx::AllocatorI* _allocator, size_t _minAlignment)
  741. : m_allocator(_allocator)
  742. , m_minAlignment(_minAlignment)
  743. {
  744. }
  745. virtual void* realloc(
  746. void* _ptr
  747. , size_t _size
  748. , size_t _align
  749. , const char* _file
  750. , uint32_t _line
  751. )
  752. {
  753. return m_allocator->realloc(_ptr, _size, bx::max(_align, m_minAlignment), _file, _line);
  754. }
  755. bx::AllocatorI* m_allocator;
  756. size_t m_minAlignment;
  757. };
  758. int main(int _argc, const char* _argv[])
  759. {
  760. bx::CommandLine cmdLine(_argc, _argv);
  761. if (cmdLine.hasArg('v', "version") )
  762. {
  763. fprintf(stderr
  764. , "texturec, bgfx texture compiler tool, version %d.%d.%d.\n"
  765. , BIMG_TEXTUREC_VERSION_MAJOR
  766. , BIMG_TEXTUREC_VERSION_MINOR
  767. , BIMG_API_VERSION
  768. );
  769. return bx::kExitSuccess;
  770. }
  771. if (cmdLine.hasArg('h', "help") )
  772. {
  773. help();
  774. return bx::kExitFailure;
  775. }
  776. const char* inputFileName = cmdLine.findOption('f');
  777. if (NULL == inputFileName)
  778. {
  779. help("Input file must be specified.");
  780. return bx::kExitFailure;
  781. }
  782. const char* outputFileName = cmdLine.findOption('o');
  783. if (NULL == outputFileName)
  784. {
  785. help("Output file must be specified.");
  786. return bx::kExitFailure;
  787. }
  788. const char* saveAs = cmdLine.findOption("as");
  789. saveAs = NULL == saveAs ? bx::strFindI(outputFileName, ".ktx") : saveAs;
  790. saveAs = NULL == saveAs ? bx::strFindI(outputFileName, ".dds") : saveAs;
  791. saveAs = NULL == saveAs ? bx::strFindI(outputFileName, ".png") : saveAs;
  792. saveAs = NULL == saveAs ? bx::strFindI(outputFileName, ".exr") : saveAs;
  793. saveAs = NULL == saveAs ? bx::strFindI(outputFileName, ".hdr") : saveAs;
  794. if (NULL == saveAs)
  795. {
  796. help("Output file format must be specified.");
  797. return bx::kExitFailure;
  798. }
  799. Options options;
  800. const char* edgeOpt = cmdLine.findOption("sdf");
  801. if (NULL != edgeOpt)
  802. {
  803. options.sdf = true;
  804. if (!bx::fromString(&options.edge, edgeOpt) )
  805. {
  806. options.edge = 255.0f;
  807. }
  808. }
  809. else
  810. {
  811. const char* alphaRef = cmdLine.findOption("ref");
  812. if (NULL != alphaRef)
  813. {
  814. options.alphaTest = true;
  815. if (!bx::fromString(&options.edge, alphaRef))
  816. {
  817. options.edge = 0.5f;
  818. }
  819. }
  820. }
  821. options.mips = cmdLine.hasArg('m', "mips");
  822. options.normalMap = cmdLine.hasArg('n', "normalmap");
  823. options.equirect = cmdLine.hasArg("equirect");
  824. options.iqa = cmdLine.hasArg("iqa");
  825. options.pma = cmdLine.hasArg("pma");
  826. options.radiance = cmdLine.hasArg("radiance");
  827. const char* maxSize = cmdLine.findOption("max");
  828. if (NULL != maxSize)
  829. {
  830. if (!bx::fromString(&options.maxSize, maxSize) )
  831. {
  832. help("Parsing max size failed.");
  833. return bx::kExitFailure;
  834. }
  835. }
  836. options.format = bimg::TextureFormat::Count;
  837. const char* type = cmdLine.findOption('t');
  838. if (NULL != type)
  839. {
  840. options.format = bimg::getFormat(type);
  841. if (!bimg::isValid(options.format) )
  842. {
  843. help("Invalid format specified.");
  844. return bx::kExitFailure;
  845. }
  846. }
  847. if (NULL != bx::strFindI(saveAs, "png") )
  848. {
  849. if (options.format == bimg::TextureFormat::Count)
  850. {
  851. options.format = bimg::TextureFormat::RGBA8;
  852. }
  853. else if (options.format != bimg::TextureFormat::RGBA8)
  854. {
  855. help("Output PNG format must be RGBA8.");
  856. return bx::kExitFailure;
  857. }
  858. }
  859. else if (NULL != bx::strFindI(saveAs, "exr") )
  860. {
  861. if (options.format == bimg::TextureFormat::Count)
  862. {
  863. options.format = bimg::TextureFormat::RGBA16F;
  864. }
  865. else if (options.format != bimg::TextureFormat::RGBA16F)
  866. {
  867. help("Output EXR format must be RGBA16F.");
  868. return bx::kExitFailure;
  869. }
  870. }
  871. const char* quality = cmdLine.findOption('q');
  872. if (NULL != quality)
  873. {
  874. switch (bx::toLower(quality[0]) )
  875. {
  876. case 'h': options.quality = bimg::Quality::Highest; break;
  877. case 'f': options.quality = bimg::Quality::Fastest; break;
  878. case 'd': options.quality = bimg::Quality::Default; break;
  879. default:
  880. help("Invalid quality specified.");
  881. return bx::kExitFailure;
  882. }
  883. }
  884. const bool validate = cmdLine.hasArg("validate");
  885. bx::Error err;
  886. bx::FileReader reader;
  887. if (!bx::open(&reader, inputFileName, &err) )
  888. {
  889. help("Failed to open input file.", err);
  890. return bx::kExitFailure;
  891. }
  892. uint32_t inputSize = (uint32_t)bx::getSize(&reader);
  893. if (0 == inputSize)
  894. {
  895. help("Failed to read input file.", err);
  896. return bx::kExitFailure;
  897. }
  898. bx::DefaultAllocator defaultAllocator;
  899. AlignedAllocator allocator(&defaultAllocator, 16);
  900. uint8_t* inputData = (uint8_t*)BX_ALLOC(&allocator, inputSize);
  901. bx::read(&reader, inputData, inputSize, &err);
  902. bx::close(&reader);
  903. if (!err.isOk() )
  904. {
  905. help("Failed to read input file.", err);
  906. return bx::kExitFailure;
  907. }
  908. bimg::ImageContainer* output = convert(&allocator, inputData, inputSize, options, &err);
  909. BX_FREE(&allocator, inputData);
  910. if (NULL != output)
  911. {
  912. bx::FileWriter writer;
  913. if (bx::open(&writer, outputFileName, false, &err) )
  914. {
  915. if (NULL != bx::strFindI(saveAs, "ktx") )
  916. {
  917. bimg::imageWriteKtx(&writer, *output, output->m_data, output->m_size, &err);
  918. }
  919. else if (NULL != bx::strFindI(saveAs, "dds") )
  920. {
  921. bimg::imageWriteDds(&writer, *output, output->m_data, output->m_size, &err);
  922. }
  923. else if (NULL != bx::strFindI(saveAs, "png") )
  924. {
  925. if (output->m_format != bimg::TextureFormat::RGBA8)
  926. {
  927. help("Incompatible output texture format. Output PNG format must be RGBA8.", err);
  928. return bx::kExitFailure;
  929. }
  930. bimg::ImageMip mip;
  931. bimg::imageGetRawData(*output, 0, 0, output->m_data, output->m_size, mip);
  932. bimg::imageWritePng(&writer
  933. , mip.m_width
  934. , mip.m_height
  935. , mip.m_width*4
  936. , mip.m_data
  937. , output->m_format
  938. , false
  939. , &err
  940. );
  941. }
  942. else if (NULL != bx::strFindI(saveAs, "exr") )
  943. {
  944. bimg::ImageMip mip;
  945. bimg::imageGetRawData(*output, 0, 0, output->m_data, output->m_size, mip);
  946. bimg::imageWriteExr(&writer
  947. , mip.m_width
  948. , mip.m_height
  949. , mip.m_width*8
  950. , mip.m_data
  951. , output->m_format
  952. , false
  953. , &err
  954. );
  955. }
  956. else if (NULL != bx::strFindI(saveAs, "hdr") )
  957. {
  958. bimg::ImageMip mip;
  959. bimg::imageGetRawData(*output, 0, 0, output->m_data, output->m_size, mip);
  960. bimg::imageWriteHdr(&writer
  961. , mip.m_width
  962. , mip.m_height
  963. , mip.m_width*getBitsPerPixel(mip.m_format)/8
  964. , mip.m_data
  965. , output->m_format
  966. , false
  967. , &err
  968. );
  969. }
  970. bx::close(&writer);
  971. if (!err.isOk() )
  972. {
  973. help(NULL, err);
  974. return bx::kExitFailure;
  975. }
  976. }
  977. else
  978. {
  979. help("Failed to open output file.", err);
  980. return bx::kExitFailure;
  981. }
  982. if (validate)
  983. {
  984. if (!bx::open(&reader, outputFileName, &err) )
  985. {
  986. help("Failed to validate file.", err);
  987. return bx::kExitFailure;
  988. }
  989. inputSize = (uint32_t)bx::getSize(&reader);
  990. if (0 == inputSize)
  991. {
  992. help("Failed to validate file.", err);
  993. return bx::kExitFailure;
  994. }
  995. inputData = (uint8_t*)BX_ALLOC(&allocator, inputSize);
  996. bx::read(&reader, inputData, inputSize, &err);
  997. bx::close(&reader);
  998. bimg::ImageContainer* input = bimg::imageParse(&allocator, inputData, inputSize, bimg::TextureFormat::Count, &err);
  999. if (!err.isOk() )
  1000. {
  1001. help("Failed to validate file.", err);
  1002. return bx::kExitFailure;
  1003. }
  1004. if (false
  1005. || input->m_format != output->m_format
  1006. || input->m_size != output->m_size
  1007. || input->m_width != output->m_width
  1008. || input->m_height != output->m_height
  1009. || input->m_depth != output->m_depth
  1010. || input->m_numLayers != output->m_numLayers
  1011. || input->m_numMips != output->m_numMips
  1012. || input->m_hasAlpha != output->m_hasAlpha
  1013. || input->m_cubeMap != output->m_cubeMap
  1014. )
  1015. {
  1016. help("Validation failed, image headers are different.");
  1017. return bx::kExitFailure;
  1018. }
  1019. {
  1020. const uint8_t numMips = output->m_numMips;
  1021. const uint16_t numSides = output->m_numLayers * (output->m_cubeMap ? 6 : 1);
  1022. for (uint8_t lod = 0; lod < numMips; ++lod)
  1023. {
  1024. for (uint16_t side = 0; side < numSides; ++side)
  1025. {
  1026. bimg::ImageMip srcMip;
  1027. bool hasSrc = bimg::imageGetRawData(*input, side, lod, input->m_data, input->m_size, srcMip);
  1028. bimg::ImageMip dstMip;
  1029. bool hasDst = bimg::imageGetRawData(*output, side, lod, output->m_data, output->m_size, dstMip);
  1030. if (false
  1031. || hasSrc != hasDst
  1032. || srcMip.m_size != dstMip.m_size
  1033. )
  1034. {
  1035. help("Validation failed, image mip/layer/side are different.");
  1036. return bx::kExitFailure;
  1037. }
  1038. if (0 != bx::memCmp(srcMip.m_data, dstMip.m_data, srcMip.m_size) )
  1039. {
  1040. help("Validation failed, image content are different.");
  1041. return bx::kExitFailure;
  1042. }
  1043. }
  1044. }
  1045. }
  1046. BX_FREE(&allocator, inputData);
  1047. }
  1048. bimg::imageFree(output);
  1049. }
  1050. else
  1051. {
  1052. help(NULL, err);
  1053. return bx::kExitFailure;
  1054. }
  1055. return bx::kExitSuccess;
  1056. }