texturec.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706
  1. /*
  2. * Copyright 2011-2017 Branimir Karadzic. All rights reserved.
  3. * License: https://github.com/bkaradzic/bgfx#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 <bimg/decode.h>
  10. #include <bimg/encode.h>
  11. #if 0
  12. # define DBG(_format, ...) fprintf(stderr, "" _format "\n", ##__VA_ARGS__)
  13. #else
  14. # define DBG(...) BX_NOOP()
  15. #endif // DEBUG
  16. #include <bx/bx.h>
  17. #include <bx/commandline.h>
  18. #include <bx/crtimpl.h>
  19. #include <bx/uint32_t.h>
  20. #include <string>
  21. #define BIMG_TEXTUREC_VERSION_MAJOR 1
  22. #define BIMG_TEXTUREC_VERSION_MINOR 3
  23. struct Options
  24. {
  25. Options()
  26. : maxSize(UINT32_MAX)
  27. , edge(0.0f)
  28. , format(bimg::TextureFormat::Count)
  29. , quality(bimg::Quality::Default)
  30. , mips(false)
  31. , normalMap(false)
  32. , iqa(false)
  33. , sdf(false)
  34. , alphaTest(false)
  35. {
  36. }
  37. void dump()
  38. {
  39. DBG("Options:\n"
  40. "\t maxSize: %d\n"
  41. "\t edge: %f\n"
  42. "\t format: %s\n"
  43. "\t mips: %s\n"
  44. "\tnormalMap: %s\n"
  45. "\t iqa: %s\n"
  46. "\t sdf: %s\n"
  47. , maxSize
  48. , edge
  49. , bimg::getName(format)
  50. , mips ? "true" : "false"
  51. , normalMap ? "true" : "false"
  52. , iqa ? "true" : "false"
  53. , sdf ? "true" : "false"
  54. );
  55. }
  56. uint32_t maxSize;
  57. float edge;
  58. bimg::TextureFormat::Enum format;
  59. bimg::Quality::Enum quality;
  60. bool mips;
  61. bool normalMap;
  62. bool iqa;
  63. bool sdf;
  64. bool alphaTest;
  65. };
  66. bimg::ImageContainer* convert(bx::AllocatorI* _allocator, const void* _inputData, uint32_t _inputSize, const Options& _options, bx::Error* _err)
  67. {
  68. BX_ERROR_SCOPE(_err);
  69. const uint8_t* inputData = (uint8_t*)_inputData;
  70. bimg::ImageContainer* output = NULL;
  71. bimg::ImageContainer* input = bimg::imageParse(_allocator, inputData, _inputSize);
  72. if (NULL != input)
  73. {
  74. const bimg::TextureFormat::Enum inputFormat = input->m_format;
  75. bimg::TextureFormat::Enum outputFormat = input->m_format;
  76. if (bimg::TextureFormat::Count != _options.format)
  77. {
  78. outputFormat = _options.format;
  79. }
  80. const bimg::ImageBlockInfo& inputBlockInfo = bimg::getBlockInfo(inputFormat);
  81. const bimg::ImageBlockInfo& outputBlockInfo = bimg::getBlockInfo(outputFormat);
  82. const uint32_t blockWidth = outputBlockInfo.blockWidth;
  83. const uint32_t blockHeight = outputBlockInfo.blockHeight;
  84. const uint32_t minBlockX = outputBlockInfo.minBlockX;
  85. const uint32_t minBlockY = outputBlockInfo.minBlockY;
  86. uint32_t outputWidth = bx::uint32_max(blockWidth * minBlockX, ( (input->m_width + blockWidth - 1) / blockWidth )*blockWidth);
  87. uint32_t outputHeight = bx::uint32_max(blockHeight * minBlockY, ( (input->m_height + blockHeight - 1) / blockHeight)*blockHeight);
  88. if (outputWidth > _options.maxSize
  89. || outputHeight > _options.maxSize)
  90. {
  91. if (outputWidth > outputHeight)
  92. {
  93. outputHeight = outputHeight * _options.maxSize / outputWidth;
  94. outputWidth = _options.maxSize;
  95. }
  96. else
  97. {
  98. outputWidth = outputWidth * _options.maxSize / outputHeight;
  99. outputHeight = _options.maxSize;
  100. }
  101. }
  102. const bool needResize = false
  103. || input->m_width != outputWidth
  104. || input->m_height != outputHeight
  105. ;
  106. const bool passThru = true
  107. && inputFormat == outputFormat
  108. && !needResize
  109. && (1 < input->m_numMips) == _options.mips
  110. && !_options.sdf
  111. && !_options.alphaTest
  112. && !_options.normalMap
  113. && !_options.iqa
  114. ;
  115. if (needResize)
  116. {
  117. bimg::ImageContainer* src = bimg::imageConvert(_allocator, bimg::TextureFormat::RGBA32F, *input);
  118. bimg::ImageContainer* dst = bimg::imageAlloc(
  119. _allocator
  120. , bimg::TextureFormat::RGBA32F
  121. , uint16_t(outputWidth)
  122. , uint16_t(outputHeight)
  123. , 1
  124. , input->m_numLayers
  125. , input->m_cubeMap
  126. , false
  127. );
  128. bimg::imageResizeRgba32fLinear(dst, src);
  129. bimg::imageFree(src);
  130. bimg::imageFree(input);
  131. input = bimg::imageConvert(_allocator, inputFormat, *dst);
  132. bimg::imageFree(dst);
  133. }
  134. if (passThru)
  135. {
  136. output = bimg::imageConvert(_allocator, outputFormat, *input);
  137. bimg::imageFree(input);
  138. return output;
  139. }
  140. output = bimg::imageAlloc(
  141. _allocator
  142. , outputFormat
  143. , uint16_t(input->m_width)
  144. , uint16_t(input->m_height)
  145. , uint16_t(input->m_depth)
  146. , input->m_numLayers
  147. , input->m_cubeMap
  148. , _options.mips
  149. );
  150. const uint8_t numMips = output->m_numMips;
  151. const uint16_t numSides = output->m_numLayers * (output->m_cubeMap ? 6 : 1);
  152. for (uint16_t side = 0; side < numSides && _err->isOk(); ++side)
  153. {
  154. bimg::ImageMip mip;
  155. if (bimg::imageGetRawData(*input, side, 0, input->m_data, input->m_size, mip) )
  156. {
  157. bimg::ImageMip dstMip;
  158. bimg::imageGetRawData(*output, side, 0, output->m_data, output->m_size, dstMip);
  159. uint8_t* dstData = const_cast<uint8_t*>(dstMip.m_data);
  160. void* temp = NULL;
  161. if (_options.normalMap)
  162. {
  163. uint32_t size = bimg::imageGetSize(
  164. NULL
  165. , uint16_t(dstMip.m_width)
  166. , uint16_t(dstMip.m_height)
  167. , 0
  168. , false
  169. , false
  170. , 1
  171. , bimg::TextureFormat::RGBA32F
  172. );
  173. temp = BX_ALLOC(_allocator, size);
  174. float* rgba = (float*)temp;
  175. float* rgbaDst = (float*)BX_ALLOC(_allocator, size);
  176. bimg::imageDecodeToRgba32f(_allocator
  177. , rgba
  178. , mip.m_data
  179. , dstMip.m_width
  180. , dstMip.m_height
  181. , dstMip.m_width*16
  182. , mip.m_format
  183. );
  184. if (bimg::TextureFormat::BC5 != mip.m_format)
  185. {
  186. for (uint32_t yy = 0; yy < mip.m_height; ++yy)
  187. {
  188. for (uint32_t xx = 0; xx < mip.m_width; ++xx)
  189. {
  190. const uint32_t offset = (yy*mip.m_width + xx) * 4;
  191. float* inout = &rgba[offset];
  192. inout[0] = inout[0] * 2.0f - 1.0f;
  193. inout[1] = inout[1] * 2.0f - 1.0f;
  194. inout[2] = inout[2] * 2.0f - 1.0f;
  195. inout[3] = inout[3] * 2.0f - 1.0f;
  196. }
  197. }
  198. }
  199. bimg::imageRgba32f11to01(rgbaDst
  200. , dstMip.m_width
  201. , dstMip.m_height
  202. , dstMip.m_width*16
  203. , rgba
  204. );
  205. bimg::imageEncodeFromRgba32f(_allocator
  206. , dstData
  207. , rgbaDst
  208. , dstMip.m_width
  209. , dstMip.m_height
  210. , outputFormat
  211. , _options.quality
  212. , _err
  213. );
  214. for (uint8_t lod = 1; lod < numMips && _err->isOk(); ++lod)
  215. {
  216. bimg::imageRgba32fDownsample2x2NormalMap(rgba
  217. , dstMip.m_width
  218. , dstMip.m_height
  219. , dstMip.m_width*16
  220. , rgba
  221. );
  222. bimg::imageRgba32f11to01(rgbaDst
  223. , dstMip.m_width
  224. , dstMip.m_height
  225. , dstMip.m_width*16
  226. , rgba
  227. );
  228. bimg::imageGetRawData(*output, side, lod, output->m_data, output->m_size, dstMip);
  229. dstData = const_cast<uint8_t*>(dstMip.m_data);
  230. bimg::imageEncodeFromRgba32f(_allocator
  231. , dstData
  232. , rgbaDst
  233. , dstMip.m_width
  234. , dstMip.m_height
  235. , outputFormat
  236. , _options.quality
  237. , _err
  238. );
  239. }
  240. BX_FREE(_allocator, rgbaDst);
  241. }
  242. else if (!bimg::isCompressed(input->m_format)
  243. && 8 != inputBlockInfo.rBits)
  244. {
  245. uint32_t size = bimg::imageGetSize(
  246. NULL
  247. , uint16_t(dstMip.m_width)
  248. , uint16_t(dstMip.m_height)
  249. , 0
  250. , false
  251. , false
  252. , 1
  253. , bimg::TextureFormat::RGBA32F
  254. );
  255. temp = BX_ALLOC(_allocator, size);
  256. float* rgba32f = (float*)temp;
  257. float* rgbaDst = (float*)BX_ALLOC(_allocator, size);
  258. bimg::imageDecodeToRgba32f(_allocator
  259. , rgba32f
  260. , mip.m_data
  261. , mip.m_width
  262. , mip.m_height
  263. , mip.m_width*16
  264. , mip.m_format
  265. );
  266. bimg::imageEncodeFromRgba32f(_allocator
  267. , dstData
  268. , rgba32f
  269. , dstMip.m_width
  270. , dstMip.m_height
  271. , outputFormat
  272. , _options.quality
  273. , _err
  274. );
  275. if (1 < numMips
  276. && _err->isOk() )
  277. {
  278. bimg::imageRgba32fToLinear(rgba32f
  279. , mip.m_width
  280. , mip.m_height
  281. , mip.m_width*16
  282. , rgba32f
  283. );
  284. for (uint8_t lod = 1; lod < numMips && _err->isOk(); ++lod)
  285. {
  286. bimg::imageRgba32fLinearDownsample2x2(rgba32f
  287. , dstMip.m_width
  288. , dstMip.m_height
  289. , dstMip.m_width*16
  290. , rgba32f
  291. );
  292. bimg::imageGetRawData(*output, side, lod, output->m_data, output->m_size, dstMip);
  293. dstData = const_cast<uint8_t*>(dstMip.m_data);
  294. bimg::imageRgba32fToGamma(rgbaDst
  295. , mip.m_width
  296. , mip.m_height
  297. , mip.m_width*16
  298. , rgba32f
  299. );
  300. bimg::imageEncodeFromRgba32f(_allocator
  301. , dstData
  302. , rgbaDst
  303. , dstMip.m_width
  304. , dstMip.m_height
  305. , outputFormat
  306. , _options.quality
  307. , _err
  308. );
  309. }
  310. }
  311. BX_FREE(_allocator, rgbaDst);
  312. }
  313. else
  314. {
  315. uint32_t size = bimg::imageGetSize(
  316. NULL
  317. , uint16_t(dstMip.m_width)
  318. , uint16_t(dstMip.m_height)
  319. , 0
  320. , false
  321. , false
  322. , 1
  323. , bimg::TextureFormat::RGBA8
  324. );
  325. temp = BX_ALLOC(_allocator, size);
  326. uint8_t* rgba = (uint8_t*)temp;
  327. bimg::imageDecodeToRgba8(rgba
  328. , mip.m_data
  329. , mip.m_width
  330. , mip.m_height
  331. , mip.m_width*4
  332. , mip.m_format
  333. );
  334. float coverage = 0.0f;
  335. if (_options.alphaTest)
  336. {
  337. coverage = bimg::imageAlphaTestCoverage(bimg::TextureFormat::RGBA8
  338. , mip.m_width
  339. , mip.m_height
  340. , mip.m_width*4
  341. , rgba
  342. , _options.edge
  343. );
  344. }
  345. void* ref = NULL;
  346. if (_options.iqa)
  347. {
  348. ref = BX_ALLOC(_allocator, size);
  349. bx::memCopy(ref, rgba, size);
  350. }
  351. bimg::imageGetRawData(*output, side, 0, output->m_data, output->m_size, dstMip);
  352. dstData = const_cast<uint8_t*>(dstMip.m_data);
  353. bimg::imageEncodeFromRgba8(dstData
  354. , rgba
  355. , dstMip.m_width
  356. , dstMip.m_height
  357. , outputFormat
  358. , _options.quality
  359. , _err
  360. );
  361. for (uint8_t lod = 1; lod < numMips && _err->isOk(); ++lod)
  362. {
  363. bimg::imageRgba8Downsample2x2(rgba
  364. , dstMip.m_width
  365. , dstMip.m_height
  366. , dstMip.m_width*4
  367. , rgba
  368. );
  369. if (_options.alphaTest)
  370. {
  371. bimg::imageScaleAlphaToCoverage(bimg::TextureFormat::RGBA8
  372. , dstMip.m_width
  373. , dstMip.m_height
  374. , dstMip.m_width*4
  375. , rgba
  376. , coverage
  377. , _options.edge
  378. );
  379. }
  380. bimg::imageGetRawData(*output, side, lod, output->m_data, output->m_size, dstMip);
  381. dstData = const_cast<uint8_t*>(dstMip.m_data);
  382. bimg::imageEncodeFromRgba8(dstData
  383. , rgba
  384. , dstMip.m_width
  385. , dstMip.m_height
  386. , outputFormat
  387. , _options.quality
  388. , _err
  389. );
  390. }
  391. if (NULL != ref)
  392. {
  393. bimg::imageDecodeToRgba8(rgba
  394. , output->m_data
  395. , mip.m_width
  396. , mip.m_height
  397. , mip.m_width*mip.m_bpp/8
  398. , outputFormat
  399. );
  400. float result = bimg::imageQualityRgba8(
  401. ref
  402. , rgba
  403. , uint16_t(mip.m_width)
  404. , uint16_t(mip.m_height)
  405. );
  406. printf("%f\n", result);
  407. BX_FREE(_allocator, ref);
  408. }
  409. }
  410. BX_FREE(_allocator, temp);
  411. }
  412. }
  413. bimg::imageFree(input);
  414. }
  415. if (!_err->isOk()
  416. && NULL != output)
  417. {
  418. bimg::imageFree(output);
  419. output = NULL;
  420. }
  421. return output;
  422. }
  423. void help(const char* _error = NULL, bool _showHelp = true)
  424. {
  425. if (NULL != _error)
  426. {
  427. fprintf(stderr, "Error:\n%s\n\n", _error);
  428. if (!_showHelp)
  429. {
  430. return;
  431. }
  432. }
  433. fprintf(stderr
  434. , "texturec, bgfx texture compiler tool, version %d.%d.%d.\n"
  435. "Copyright 2011-2017 Branimir Karadzic. All rights reserved.\n"
  436. "License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause\n\n"
  437. , BIMG_TEXTUREC_VERSION_MAJOR
  438. , BIMG_TEXTUREC_VERSION_MINOR
  439. , BIMG_API_VERSION
  440. );
  441. fprintf(stderr
  442. , "Usage: texturec -f <in> -o <out> [-t <format>]\n"
  443. "\n"
  444. "Supported input file types:\n"
  445. " *.png Portable Network Graphics\n"
  446. " *.tga Targa\n"
  447. " *.dds Direct Draw Surface\n"
  448. " *.ktx Khronos Texture\n"
  449. " *.pvr PowerVR\n"
  450. "\n"
  451. "Options:\n"
  452. " -h, --help Help.\n"
  453. " -v, --version Version information only.\n"
  454. " -f <file path> Input file path.\n"
  455. " -o <file path> Output file path (file will be written in KTX format).\n"
  456. " -t <format> Output format type (BC1/2/3/4/5, ETC1, PVR14, etc.).\n"
  457. " -q <quality> Encoding quality (default, fastest, highest).\n"
  458. " -m, --mips Generate mip-maps.\n"
  459. " -n, --normalmap Input texture is normal map.\n"
  460. " --sdf <edge> Compute SDF texture.\n"
  461. " --ref <alpha> Alpha reference value.\n"
  462. " --iqa Image Quality Assessment\n"
  463. " --max <max size> Maximum width/height (image will be scaled down and\n"
  464. " aspect ratio will be preserved.\n"
  465. " --as <extension> Save as.\n"
  466. "\n"
  467. "For additional information, see https://github.com/bkaradzic/bgfx\n"
  468. );
  469. }
  470. void help(const char* _str, const bx::Error& _err)
  471. {
  472. std::string str;
  473. if (_str != NULL)
  474. {
  475. str.append(_str);
  476. str.append(" ");
  477. }
  478. const bx::StringView& sv = _err.getMessage();
  479. str.append(sv.getPtr(), sv.getTerm() - sv.getPtr() );
  480. help(str.c_str(), false);
  481. }
  482. int main(int _argc, const char* _argv[])
  483. {
  484. bx::CommandLine cmdLine(_argc, _argv);
  485. if (cmdLine.hasArg('v', "version") )
  486. {
  487. fprintf(stderr
  488. , "texturec, bgfx texture compiler tool, version %d.%d.%d.\n"
  489. , BIMG_TEXTUREC_VERSION_MAJOR
  490. , BIMG_TEXTUREC_VERSION_MINOR
  491. , BIMG_API_VERSION
  492. );
  493. return EXIT_SUCCESS;
  494. }
  495. if (cmdLine.hasArg('h', "help") )
  496. {
  497. help();
  498. return EXIT_FAILURE;
  499. }
  500. const char* inputFileName = cmdLine.findOption('f');
  501. if (NULL == inputFileName)
  502. {
  503. help("Input file must be specified.");
  504. return EXIT_FAILURE;
  505. }
  506. const char* outputFileName = cmdLine.findOption('o');
  507. if (NULL == outputFileName)
  508. {
  509. help("Output file must be specified.");
  510. return EXIT_FAILURE;
  511. }
  512. const char* saveAs = cmdLine.findOption("as");
  513. saveAs = NULL == saveAs ? bx::strFindI(outputFileName, ".ktx") : saveAs;
  514. if (NULL == saveAs)
  515. {
  516. help("Output file format must be specified.");
  517. return EXIT_FAILURE;
  518. }
  519. Options options;
  520. const char* edgeOpt = cmdLine.findOption("sdf");
  521. if (NULL != edgeOpt)
  522. {
  523. options.sdf = true;
  524. options.edge = (float)atof(edgeOpt);
  525. }
  526. else
  527. {
  528. const char* alphaRef = cmdLine.findOption("ref");
  529. if (NULL != alphaRef)
  530. {
  531. options.alphaTest = true;
  532. options.edge = (float)atof(alphaRef);
  533. }
  534. }
  535. options.mips = cmdLine.hasArg('m', "mips");
  536. options.normalMap = cmdLine.hasArg('n', "normalmap");
  537. options.iqa = cmdLine.hasArg('\0', "iqa");
  538. const char* maxSize = cmdLine.findOption("max");
  539. if (NULL != maxSize)
  540. {
  541. options.maxSize = atoi(maxSize);
  542. }
  543. options.format = bimg::TextureFormat::Count;
  544. const char* type = cmdLine.findOption('t');
  545. if (NULL != type)
  546. {
  547. options.format = bimg::getFormat(type);
  548. if (!bimg::isValid(options.format) )
  549. {
  550. help("Invalid format specified.");
  551. return EXIT_FAILURE;
  552. }
  553. }
  554. const char* quality = cmdLine.findOption('q');
  555. if (NULL != quality)
  556. {
  557. switch (bx::toLower(quality[0]) )
  558. {
  559. case 'h': options.quality = bimg::Quality::Highest; break;
  560. case 'f': options.quality = bimg::Quality::Fastest; break;
  561. case 'd': options.quality = bimg::Quality::Default; break;
  562. default:
  563. help("Invalid quality specified.");
  564. return EXIT_FAILURE;
  565. }
  566. }
  567. bx::Error err;
  568. bx::CrtFileReader reader;
  569. if (!bx::open(&reader, inputFileName, &err) )
  570. {
  571. help("Failed to open input file.", err);
  572. return EXIT_FAILURE;
  573. }
  574. bx::CrtAllocator allocator;
  575. uint32_t inputSize = (uint32_t)bx::getSize(&reader);
  576. uint8_t* inputData = (uint8_t*)BX_ALLOC(&allocator, inputSize);
  577. bx::read(&reader, inputData, inputSize, &err);
  578. bx::close(&reader);
  579. if (!err.isOk() )
  580. {
  581. help("Failed to read input file.", err);
  582. return EXIT_FAILURE;
  583. }
  584. bimg::ImageContainer* output = convert(&allocator, inputData, inputSize, options, &err);
  585. BX_FREE(&allocator, inputData);
  586. if (NULL != output)
  587. {
  588. bx::CrtFileWriter writer;
  589. if (bx::open(&writer, outputFileName, false, &err) )
  590. {
  591. if (NULL != bx::strFindI(saveAs, "ktx") )
  592. {
  593. bimg::imageWriteKtx(&writer, *output, output->m_data, output->m_size);
  594. }
  595. bx::close(&writer);
  596. }
  597. else
  598. {
  599. help("Failed to open output file.", err);
  600. return EXIT_FAILURE;
  601. }
  602. bimg::imageFree(output);
  603. }
  604. else
  605. {
  606. help(NULL, err);
  607. return EXIT_FAILURE;
  608. }
  609. return EXIT_SUCCESS;
  610. }