texturec.cpp 15 KB

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