texturec.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293
  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 (needResize)
  229. {
  230. bimg::ImageContainer* src = bimg::imageConvert(_allocator, bimg::TextureFormat::RGBA32F, *input, false);
  231. bimg::ImageContainer* dst = bimg::imageAlloc(
  232. _allocator
  233. , bimg::TextureFormat::RGBA32F
  234. , uint16_t(outputWidth)
  235. , uint16_t(outputHeight)
  236. , uint16_t(outputDepth)
  237. , input->m_numLayers
  238. , input->m_cubeMap
  239. , false
  240. );
  241. bimg::imageResizeRgba32fLinear(dst, src);
  242. bimg::imageFree(src);
  243. bimg::imageFree(input);
  244. if (bimg::isCompressed(inputFormat) )
  245. {
  246. if (inputFormat == bimg::TextureFormat::BC6H)
  247. {
  248. inputFormat = bimg::TextureFormat::RGBA32F;
  249. }
  250. else
  251. {
  252. inputFormat = bimg::TextureFormat::RGBA8;
  253. }
  254. }
  255. input = bimg::imageConvert(_allocator, inputFormat, *dst);
  256. bimg::imageFree(dst);
  257. }
  258. if (passThru)
  259. {
  260. if (inputFormat != outputFormat
  261. && bimg::isCompressed(outputFormat) )
  262. {
  263. output = bimg::imageEncode(_allocator, outputFormat, _options.quality, *input);
  264. }
  265. else
  266. {
  267. output = bimg::imageConvert(_allocator, outputFormat, *input);
  268. }
  269. bimg::imageFree(input);
  270. return output;
  271. }
  272. if (_options.equirect
  273. || _options.strip)
  274. {
  275. bimg::ImageContainer* src = bimg::imageConvert(_allocator, bimg::TextureFormat::RGBA32F, *input);
  276. bimg::imageFree(input);
  277. bimg::ImageContainer* dst;
  278. if (outputWidth/2 == outputHeight)
  279. {
  280. dst = bimg::imageCubemapFromLatLongRgba32F(_allocator, *src, true, _err);
  281. bimg::imageFree(src);
  282. }
  283. else
  284. {
  285. dst = bimg::imageCubemapFromStripRgba32F(_allocator, *src, _err);
  286. bimg::imageFree(src);
  287. }
  288. if (!_err->isOk() )
  289. {
  290. return NULL;
  291. }
  292. input = bimg::imageConvert(_allocator, inputFormat, *dst);
  293. bimg::imageFree(dst);
  294. }
  295. if (bimg::LightingModel::Count != _options.radiance)
  296. {
  297. output = bimg::imageCubemapRadianceFilter(_allocator, *input, _options.radiance, _err);
  298. if (!_err->isOk() )
  299. {
  300. return NULL;
  301. }
  302. if (bimg::TextureFormat::RGBA32F != outputFormat)
  303. {
  304. bimg::ImageContainer* temp = bimg::imageEncode(_allocator, outputFormat, _options.quality, *output);
  305. bimg::imageFree(output);
  306. output = temp;
  307. }
  308. bimg::imageFree(input);
  309. return output;
  310. }
  311. output = bimg::imageAlloc(
  312. _allocator
  313. , outputFormat
  314. , uint16_t(input->m_width)
  315. , uint16_t(input->m_height)
  316. , uint16_t(input->m_depth)
  317. , input->m_numLayers
  318. , input->m_cubeMap
  319. , _options.mips
  320. );
  321. const uint8_t numMips = output->m_numMips;
  322. const uint16_t numSides = output->m_numLayers * (output->m_cubeMap ? 6 : 1);
  323. for (uint16_t side = 0; side < numSides && _err->isOk(); ++side)
  324. {
  325. bimg::ImageMip mip;
  326. if (bimg::imageGetRawData(*input, side, 0, input->m_data, input->m_size, mip) )
  327. {
  328. bimg::ImageMip dstMip;
  329. bimg::imageGetRawData(*output, side, 0, output->m_data, output->m_size, dstMip);
  330. uint8_t* dstData = const_cast<uint8_t*>(dstMip.m_data);
  331. void* temp = NULL;
  332. // Normal map.
  333. if (_options.normalMap)
  334. {
  335. uint32_t size = bimg::imageGetSize(
  336. NULL
  337. , uint16_t(dstMip.m_width)
  338. , uint16_t(dstMip.m_height)
  339. , 0
  340. , false
  341. , false
  342. , 1
  343. , bimg::TextureFormat::RGBA32F
  344. );
  345. temp = BX_ALLOC(_allocator, size);
  346. float* rgba = (float*)temp;
  347. float* rgbaDst = (float*)BX_ALLOC(_allocator, size);
  348. bimg::imageDecodeToRgba32f(_allocator
  349. , rgba
  350. , mip.m_data
  351. , dstMip.m_width
  352. , dstMip.m_height
  353. , dstMip.m_depth
  354. , dstMip.m_width*16
  355. , mip.m_format
  356. );
  357. if (bimg::TextureFormat::BC5 != mip.m_format)
  358. {
  359. for (uint32_t yy = 0; yy < mip.m_height; ++yy)
  360. {
  361. for (uint32_t xx = 0; xx < mip.m_width; ++xx)
  362. {
  363. const uint32_t offset = (yy*mip.m_width + xx) * 4;
  364. float* inout = &rgba[offset];
  365. inout[0] = inout[0] * 2.0f - 1.0f;
  366. inout[1] = inout[1] * 2.0f - 1.0f;
  367. inout[2] = inout[2] * 2.0f - 1.0f;
  368. inout[3] = inout[3] * 2.0f - 1.0f;
  369. }
  370. }
  371. }
  372. imageRgba32fNormalize(rgba
  373. , dstMip.m_width
  374. , dstMip.m_height
  375. , dstMip.m_width*16
  376. , rgba
  377. );
  378. bimg::imageRgba32f11to01(rgbaDst
  379. , dstMip.m_width
  380. , dstMip.m_height
  381. , dstMip.m_depth
  382. , dstMip.m_width*16
  383. , rgba
  384. );
  385. bimg::imageEncodeFromRgba32f(_allocator
  386. , dstData
  387. , rgbaDst
  388. , dstMip.m_width
  389. , dstMip.m_height
  390. , dstMip.m_depth
  391. , outputFormat
  392. , _options.quality
  393. , _err
  394. );
  395. for (uint8_t lod = 1; lod < numMips && _err->isOk(); ++lod)
  396. {
  397. bimg::imageRgba32fDownsample2x2NormalMap(rgba
  398. , dstMip.m_width
  399. , dstMip.m_height
  400. , dstMip.m_width*16
  401. , bx::strideAlign(dstMip.m_width/2, blockWidth)*16
  402. , rgba
  403. );
  404. bimg::imageRgba32f11to01(rgbaDst
  405. , dstMip.m_width
  406. , dstMip.m_height
  407. , dstMip.m_depth
  408. , dstMip.m_width*16
  409. , rgba
  410. );
  411. bimg::imageGetRawData(*output, side, lod, output->m_data, output->m_size, dstMip);
  412. dstData = const_cast<uint8_t*>(dstMip.m_data);
  413. bimg::imageEncodeFromRgba32f(_allocator
  414. , dstData
  415. , rgbaDst
  416. , dstMip.m_width
  417. , dstMip.m_height
  418. , dstMip.m_depth
  419. , outputFormat
  420. , _options.quality
  421. , _err
  422. );
  423. }
  424. BX_FREE(_allocator, rgbaDst);
  425. }
  426. // HDR
  427. else if ( (!bimg::isCompressed(inputFormat) && 8 != inputBlockInfo.rBits)
  428. || outputFormat == bimg::TextureFormat::BC6H
  429. || outputFormat == bimg::TextureFormat::BC7
  430. )
  431. {
  432. uint32_t size = bimg::imageGetSize(
  433. NULL
  434. , uint16_t(dstMip.m_width)
  435. , uint16_t(dstMip.m_height)
  436. , uint16_t(dstMip.m_depth)
  437. , false
  438. , false
  439. , 1
  440. , bimg::TextureFormat::RGBA32F
  441. );
  442. temp = BX_ALLOC(_allocator, size);
  443. float* rgba32f = (float*)temp;
  444. float* rgbaDst = (float*)BX_ALLOC(_allocator, size);
  445. bimg::imageDecodeToRgba32f(_allocator
  446. , rgba32f
  447. , mip.m_data
  448. , mip.m_width
  449. , mip.m_height
  450. , mip.m_depth
  451. , mip.m_width*16
  452. , mip.m_format
  453. );
  454. if (_options.pma)
  455. {
  456. imagePremultiplyAlpha(
  457. rgba32f
  458. , dstMip.m_width
  459. , dstMip.m_height
  460. , dstMip.m_depth
  461. , bimg::TextureFormat::RGBA32F
  462. );
  463. }
  464. bimg::imageEncodeFromRgba32f(_allocator
  465. , dstData
  466. , rgba32f
  467. , dstMip.m_width
  468. , dstMip.m_height
  469. , dstMip.m_depth
  470. , outputFormat
  471. , _options.quality
  472. , _err
  473. );
  474. if (1 < numMips
  475. && _err->isOk() )
  476. {
  477. bimg::imageRgba32fToLinear(rgba32f
  478. , mip.m_width
  479. , mip.m_height
  480. , mip.m_depth
  481. , mip.m_width*16
  482. , rgba32f
  483. );
  484. for (uint8_t lod = 1; lod < numMips && _err->isOk(); ++lod)
  485. {
  486. bimg::imageRgba32fLinearDownsample2x2(rgba32f
  487. , dstMip.m_width
  488. , dstMip.m_height
  489. , dstMip.m_depth
  490. , dstMip.m_width*16
  491. , rgba32f
  492. );
  493. if (_options.pma)
  494. {
  495. imagePremultiplyAlpha(
  496. rgba32f
  497. , dstMip.m_width
  498. , dstMip.m_height
  499. , dstMip.m_depth
  500. , bimg::TextureFormat::RGBA32F
  501. );
  502. }
  503. bimg::imageGetRawData(*output, side, lod, output->m_data, output->m_size, dstMip);
  504. dstData = const_cast<uint8_t*>(dstMip.m_data);
  505. bimg::imageRgba32fToGamma(rgbaDst
  506. , mip.m_width
  507. , mip.m_height
  508. , mip.m_depth
  509. , mip.m_width*16
  510. , rgba32f
  511. );
  512. bimg::imageEncodeFromRgba32f(_allocator
  513. , dstData
  514. , rgbaDst
  515. , dstMip.m_width
  516. , dstMip.m_height
  517. , dstMip.m_depth
  518. , outputFormat
  519. , _options.quality
  520. , _err
  521. );
  522. }
  523. }
  524. BX_FREE(_allocator, rgbaDst);
  525. }
  526. // SDF
  527. else if (_options.sdf)
  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::R8
  538. );
  539. temp = BX_ALLOC(_allocator, size);
  540. uint8_t* rgba = (uint8_t*)temp;
  541. bimg::imageDecodeToR8(_allocator
  542. , rgba
  543. , mip.m_data
  544. , mip.m_width
  545. , mip.m_height
  546. , mip.m_depth
  547. , mip.m_width
  548. , mip.m_format
  549. );
  550. bimg::imageGetRawData(*output, side, 0, output->m_data, output->m_size, dstMip);
  551. dstData = const_cast<uint8_t*>(dstMip.m_data);
  552. bimg::imageMakeDist(_allocator
  553. , dstData
  554. , mip.m_width
  555. , mip.m_height
  556. , mip.m_width
  557. , _options.edge
  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 <edge> 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* edgeOpt = cmdLine.findOption("sdf");
  852. if (NULL != edgeOpt)
  853. {
  854. options.sdf = true;
  855. if (!bx::fromString(&options.edge, edgeOpt) )
  856. {
  857. options.edge = 255.0f;
  858. }
  859. }
  860. else
  861. {
  862. const char* alphaRef = cmdLine.findOption("ref");
  863. if (NULL != alphaRef)
  864. {
  865. options.alphaTest = true;
  866. if (!bx::fromString(&options.edge, alphaRef))
  867. {
  868. options.edge = 0.5f;
  869. }
  870. }
  871. }
  872. options.mips = cmdLine.hasArg('m', "mips");
  873. options.normalMap = cmdLine.hasArg('n', "normalmap");
  874. options.equirect = cmdLine.hasArg("equirect");
  875. options.strip = cmdLine.hasArg("strip");
  876. options.iqa = cmdLine.hasArg("iqa");
  877. options.pma = cmdLine.hasArg("pma");
  878. if (options.equirect
  879. && options.strip)
  880. {
  881. help("Image can't be equirect and strip at the same time.");
  882. return bx::kExitFailure;
  883. }
  884. const char* maxSize = cmdLine.findOption("max");
  885. if (NULL != maxSize)
  886. {
  887. if (!bx::fromString(&options.maxSize, maxSize) )
  888. {
  889. help("Parsing `--max` failed.");
  890. return bx::kExitFailure;
  891. }
  892. }
  893. const char* mipSkip = cmdLine.findOption("mipskip");
  894. if (NULL != mipSkip)
  895. {
  896. if (!bx::fromString(&options.mipSkip, mipSkip) )
  897. {
  898. help("Parsing `--mipskip` failed.");
  899. return bx::kExitFailure;
  900. }
  901. }
  902. options.format = bimg::TextureFormat::Count;
  903. const char* type = cmdLine.findOption('t');
  904. if (NULL != type)
  905. {
  906. options.format = bimg::getFormat(type);
  907. if (!bimg::isValid(options.format) )
  908. {
  909. help("Invalid format specified.");
  910. return bx::kExitFailure;
  911. }
  912. }
  913. if (!bx::strFindI(saveAs, "png").isEmpty() )
  914. {
  915. if (options.format == bimg::TextureFormat::Count)
  916. {
  917. options.format = bimg::TextureFormat::RGBA8;
  918. }
  919. else if (options.format != bimg::TextureFormat::RGBA8)
  920. {
  921. help("Output PNG format must be RGBA8.");
  922. return bx::kExitFailure;
  923. }
  924. }
  925. else if (!bx::strFindI(saveAs, "exr").isEmpty() )
  926. {
  927. if (options.format == bimg::TextureFormat::Count)
  928. {
  929. options.format = bimg::TextureFormat::RGBA16F;
  930. }
  931. else if (options.format != bimg::TextureFormat::RGBA16F)
  932. {
  933. help("Output EXR format must be RGBA16F.");
  934. return bx::kExitFailure;
  935. }
  936. }
  937. const char* quality = cmdLine.findOption('q');
  938. if (NULL != quality)
  939. {
  940. switch (bx::toLower(quality[0]) )
  941. {
  942. case 'h': options.quality = bimg::Quality::Highest; break;
  943. case 'f': options.quality = bimg::Quality::Fastest; break;
  944. case 'd': options.quality = bimg::Quality::Default; break;
  945. default:
  946. help("Invalid quality specified.");
  947. return bx::kExitFailure;
  948. }
  949. }
  950. const char* radiance = cmdLine.findOption("radiance");
  951. if (NULL != radiance)
  952. {
  953. if (0 == bx::strCmpI(radiance, "phong" ) ) { options.radiance = bimg::LightingModel::Phong; }
  954. else if (0 == bx::strCmpI(radiance, "phongbrdf") ) { options.radiance = bimg::LightingModel::PhongBrdf; }
  955. else if (0 == bx::strCmpI(radiance, "blinn" ) ) { options.radiance = bimg::LightingModel::Blinn; }
  956. else if (0 == bx::strCmpI(radiance, "blinnbrdf") ) { options.radiance = bimg::LightingModel::BlinnBrdf; }
  957. else if (0 == bx::strCmpI(radiance, "ggx" ) ) { options.radiance = bimg::LightingModel::Ggx; }
  958. else
  959. {
  960. help("Invalid radiance lighting model specified.");
  961. return bx::kExitFailure;
  962. }
  963. }
  964. const bool validate = cmdLine.hasArg("validate");
  965. bx::Error err;
  966. bx::FileReader reader;
  967. if (!bx::open(&reader, inputFileName, &err) )
  968. {
  969. help("Failed to open input file.", err);
  970. return bx::kExitFailure;
  971. }
  972. uint32_t inputSize = (uint32_t)bx::getSize(&reader);
  973. if (0 == inputSize)
  974. {
  975. help("Failed to read input file.", err);
  976. return bx::kExitFailure;
  977. }
  978. bx::DefaultAllocator defaultAllocator;
  979. AlignedAllocator allocator(&defaultAllocator, 16);
  980. uint8_t* inputData = (uint8_t*)BX_ALLOC(&allocator, inputSize);
  981. bx::read(&reader, inputData, inputSize, &err);
  982. bx::close(&reader);
  983. if (!err.isOk() )
  984. {
  985. help("Failed to read input file.", err);
  986. return bx::kExitFailure;
  987. }
  988. bimg::ImageContainer* output = convert(&allocator, inputData, inputSize, options, &err);
  989. BX_FREE(&allocator, inputData);
  990. if (NULL != output)
  991. {
  992. bx::FileWriter writer;
  993. if (bx::open(&writer, outputFileName, false, &err) )
  994. {
  995. if (!bx::strFindI(saveAs, "ktx").isEmpty() )
  996. {
  997. bimg::imageWriteKtx(&writer, *output, output->m_data, output->m_size, &err);
  998. }
  999. else if (!bx::strFindI(saveAs, "dds").isEmpty() )
  1000. {
  1001. bimg::imageWriteDds(&writer, *output, output->m_data, output->m_size, &err);
  1002. }
  1003. else if (!bx::strFindI(saveAs, "png").isEmpty() )
  1004. {
  1005. if (output->m_format != bimg::TextureFormat::RGBA8)
  1006. {
  1007. help("Incompatible output texture format. Output PNG format must be RGBA8.", err);
  1008. return bx::kExitFailure;
  1009. }
  1010. bimg::ImageMip mip;
  1011. bimg::imageGetRawData(*output, 0, 0, output->m_data, output->m_size, mip);
  1012. bimg::imageWritePng(&writer
  1013. , mip.m_width
  1014. , mip.m_height
  1015. , mip.m_width*4
  1016. , mip.m_data
  1017. , output->m_format
  1018. , false
  1019. , &err
  1020. );
  1021. }
  1022. else if (!bx::strFindI(saveAs, "exr").isEmpty() )
  1023. {
  1024. bimg::ImageMip mip;
  1025. bimg::imageGetRawData(*output, 0, 0, output->m_data, output->m_size, mip);
  1026. bimg::imageWriteExr(&writer
  1027. , mip.m_width
  1028. , mip.m_height
  1029. , mip.m_width*8
  1030. , mip.m_data
  1031. , output->m_format
  1032. , false
  1033. , &err
  1034. );
  1035. }
  1036. else if (!bx::strFindI(saveAs, "hdr").isEmpty() )
  1037. {
  1038. bimg::ImageMip mip;
  1039. bimg::imageGetRawData(*output, 0, 0, output->m_data, output->m_size, mip);
  1040. bimg::imageWriteHdr(&writer
  1041. , mip.m_width
  1042. , mip.m_height
  1043. , mip.m_width*getBitsPerPixel(mip.m_format)/8
  1044. , mip.m_data
  1045. , output->m_format
  1046. , false
  1047. , &err
  1048. );
  1049. }
  1050. bx::close(&writer);
  1051. if (!err.isOk() )
  1052. {
  1053. help(NULL, err);
  1054. return bx::kExitFailure;
  1055. }
  1056. }
  1057. else
  1058. {
  1059. help("Failed to open output file.", err);
  1060. return bx::kExitFailure;
  1061. }
  1062. if (validate)
  1063. {
  1064. if (!bx::open(&reader, outputFileName, &err) )
  1065. {
  1066. help("Failed to validate file.", err);
  1067. return bx::kExitFailure;
  1068. }
  1069. inputSize = (uint32_t)bx::getSize(&reader);
  1070. if (0 == inputSize)
  1071. {
  1072. help("Failed to validate file.", err);
  1073. return bx::kExitFailure;
  1074. }
  1075. inputData = (uint8_t*)BX_ALLOC(&allocator, inputSize);
  1076. bx::read(&reader, inputData, inputSize, &err);
  1077. bx::close(&reader);
  1078. bimg::ImageContainer* input = bimg::imageParse(&allocator, inputData, inputSize, bimg::TextureFormat::Count, &err);
  1079. if (!err.isOk() )
  1080. {
  1081. help("Failed to validate file.", err);
  1082. return bx::kExitFailure;
  1083. }
  1084. if (false
  1085. || input->m_format != output->m_format
  1086. || input->m_size != output->m_size
  1087. || input->m_width != output->m_width
  1088. || input->m_height != output->m_height
  1089. || input->m_depth != output->m_depth
  1090. || input->m_numLayers != output->m_numLayers
  1091. || input->m_numMips != output->m_numMips
  1092. || input->m_hasAlpha != output->m_hasAlpha
  1093. || input->m_cubeMap != output->m_cubeMap
  1094. )
  1095. {
  1096. help("Validation failed, image headers are different.");
  1097. return bx::kExitFailure;
  1098. }
  1099. {
  1100. const uint8_t numMips = output->m_numMips;
  1101. const uint16_t numSides = output->m_numLayers * (output->m_cubeMap ? 6 : 1);
  1102. for (uint8_t lod = 0; lod < numMips; ++lod)
  1103. {
  1104. for (uint16_t side = 0; side < numSides; ++side)
  1105. {
  1106. bimg::ImageMip srcMip;
  1107. bool hasSrc = bimg::imageGetRawData(*input, side, lod, input->m_data, input->m_size, srcMip);
  1108. bimg::ImageMip dstMip;
  1109. bool hasDst = bimg::imageGetRawData(*output, side, lod, output->m_data, output->m_size, dstMip);
  1110. if (false
  1111. || hasSrc != hasDst
  1112. || srcMip.m_size != dstMip.m_size
  1113. )
  1114. {
  1115. help("Validation failed, image mip/layer/side are different.");
  1116. return bx::kExitFailure;
  1117. }
  1118. if (0 != bx::memCmp(srcMip.m_data, dstMip.m_data, srcMip.m_size) )
  1119. {
  1120. help("Validation failed, image content are different.");
  1121. return bx::kExitFailure;
  1122. }
  1123. }
  1124. }
  1125. }
  1126. BX_FREE(&allocator, inputData);
  1127. }
  1128. bimg::imageFree(output);
  1129. }
  1130. else
  1131. {
  1132. help(NULL, err);
  1133. return bx::kExitFailure;
  1134. }
  1135. return bx::kExitSuccess;
  1136. }