texturec.cpp 25 KB

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