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