texturec.cpp 13 KB

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