texturec.cpp 13 KB

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