texturec.cpp 32 KB

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