texturec.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  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 BX_TRACE(_format, ...) fprintf(stderr, "" _format "\n", ##__VA_ARGS__)
  13. #endif // DEBUG
  14. #include <bx/bx.h>
  15. #include <bx/commandline.h>
  16. #include <bx/crtimpl.h>
  17. #include <bx/uint32_t.h>
  18. extern "C" {
  19. #include <iqa.h>
  20. }
  21. void help(const char* _error = NULL)
  22. {
  23. if (NULL != _error)
  24. {
  25. fprintf(stderr, "Error:\n%s\n\n", _error);
  26. }
  27. fprintf(stderr
  28. , "texturec, bgfx texture compiler tool\n"
  29. "Copyright 2011-2017 Branimir Karadzic. All rights reserved.\n"
  30. "License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause\n\n"
  31. );
  32. fprintf(stderr
  33. , "Usage: texturec -f <in> -o <out> [-t <format>]\n"
  34. "\n"
  35. "Supported input file types:\n"
  36. " *.png Portable Network Graphics\n"
  37. " *.tga Targa\n"
  38. " *.dds Direct Draw Surface\n"
  39. " *.ktx Khronos Texture\n"
  40. " *.pvr PowerVR\n"
  41. "\n"
  42. "Options:\n"
  43. " -f <file path> Input file path.\n"
  44. " -o <file path> Output file path (file will be written in KTX format).\n"
  45. " -t <format> Output format type (BC1/2/3/4/5, ETC1, PVR14, etc.).\n"
  46. " -m, --mips Generate mip-maps.\n"
  47. " -n, --normalmap Input texture is normal map.\n"
  48. " --sdf <edge> Compute SDF texture.\n"
  49. " --iqa Image Quality Assesment\n"
  50. "\n"
  51. "For additional information, see https://github.com/bkaradzic/bgfx\n"
  52. );
  53. }
  54. int main(int _argc, const char* _argv[])
  55. {
  56. bx::CommandLine cmdLine(_argc, _argv);
  57. if (cmdLine.hasArg('h', "help") )
  58. {
  59. help();
  60. return EXIT_FAILURE;
  61. }
  62. const char* inputFileName = cmdLine.findOption('f');
  63. if (NULL == inputFileName)
  64. {
  65. help("Input file must be specified.");
  66. return EXIT_FAILURE;
  67. }
  68. const char* outputFileName = cmdLine.findOption('o');
  69. if (NULL == outputFileName)
  70. {
  71. help("Output file must be specified.");
  72. return EXIT_FAILURE;
  73. }
  74. bool sdf = false;
  75. double edge = 16.0;
  76. const char* edgeOpt = cmdLine.findOption("sdf");
  77. if (NULL != edgeOpt)
  78. {
  79. sdf = true;
  80. edge = atof(edgeOpt);
  81. }
  82. BX_UNUSED(sdf, edge);
  83. const bool mips = cmdLine.hasArg('m', "mips");
  84. const bool normalMap = cmdLine.hasArg('n', "normalmap");
  85. const bool iqa = cmdLine.hasArg('\0', "iqa");
  86. bx::CrtFileReader reader;
  87. if (!bx::open(&reader, inputFileName) )
  88. {
  89. help("Failed to open input file.");
  90. return EXIT_FAILURE;
  91. }
  92. bx::CrtAllocator allocator;
  93. uint32_t inputSize = (uint32_t)bx::getSize(&reader);
  94. uint8_t* inputData = (uint8_t*)BX_ALLOC(&allocator, inputSize);
  95. bx::read(&reader, inputData, inputSize);
  96. bx::close(&reader);
  97. {
  98. using namespace bimg;
  99. ImageContainer* input = imageParse(&allocator, inputData, inputSize);
  100. if (NULL != input)
  101. {
  102. BX_FREE(&allocator, inputData);
  103. const char* type = cmdLine.findOption('t');
  104. bimg::TextureFormat::Enum format = input->m_format;
  105. if (NULL != type)
  106. {
  107. format = bimg::getFormat(type);
  108. if (!isValid(format) )
  109. {
  110. help("Invalid format specified.");
  111. return EXIT_FAILURE;
  112. }
  113. }
  114. ImageContainer* output = NULL;
  115. ImageMip mip;
  116. if (imageGetRawData(*input, 0, 0, input->m_data, input->m_size, mip) )
  117. {
  118. uint8_t numMips = mips
  119. ? imageGetNumMips(format, mip.m_width, mip.m_height)
  120. : 1
  121. ;
  122. void* temp = NULL;
  123. if (normalMap)
  124. {
  125. output = imageAlloc(&allocator, format, mip.m_width, mip.m_height, 0, 1, false, mips);
  126. ImageMip dstMip;
  127. imageGetRawData(*output, 0, 0, NULL, 0, dstMip);
  128. if (mip.m_width != dstMip.m_width
  129. && mip.m_height != dstMip.m_height)
  130. {
  131. printf("Invalid input image size %dx%d, it must be at least %dx%d to be converted to %s format.\n"
  132. , mip.m_width
  133. , mip.m_height
  134. , dstMip.m_width
  135. , dstMip.m_height
  136. , getName(format)
  137. );
  138. return EXIT_FAILURE;
  139. }
  140. uint32_t size = imageGetSize(
  141. NULL
  142. , dstMip.m_width
  143. , dstMip.m_height
  144. , 0
  145. , false
  146. , false
  147. , 1
  148. , TextureFormat::RGBA32F
  149. );
  150. temp = BX_ALLOC(&allocator, size);
  151. float* rgba = (float*)temp;
  152. float* rgbaDst = (float*)BX_ALLOC(&allocator, size);
  153. imageDecodeToRgba32f(&allocator
  154. , rgba
  155. , mip.m_data
  156. , mip.m_width
  157. , mip.m_height
  158. , mip.m_width*mip.m_bpp/8
  159. , mip.m_format
  160. );
  161. if (TextureFormat::BC5 != mip.m_format)
  162. {
  163. for (uint32_t yy = 0; yy < mip.m_height; ++yy)
  164. {
  165. for (uint32_t xx = 0; xx < mip.m_width; ++xx)
  166. {
  167. const uint32_t offset = (yy*mip.m_width + xx) * 4;
  168. float* inout = &rgba[offset];
  169. inout[0] = inout[0] * 2.0f - 1.0f;
  170. inout[1] = inout[1] * 2.0f - 1.0f;
  171. inout[2] = inout[2] * 2.0f - 1.0f;
  172. inout[3] = inout[3] * 2.0f - 1.0f;
  173. }
  174. }
  175. }
  176. imageRgba32f11to01(rgbaDst, dstMip.m_width, dstMip.m_height, dstMip.m_width*16, rgba);
  177. imageEncodeFromRgba32f(&allocator, output->m_data, rgbaDst, dstMip.m_width, dstMip.m_height, format);
  178. for (uint8_t lod = 1; lod < numMips; ++lod)
  179. {
  180. imageRgba32fDownsample2x2NormalMap(rgba, dstMip.m_width, dstMip.m_height, dstMip.m_width*16, rgba);
  181. imageRgba32f11to01(rgbaDst, dstMip.m_width, dstMip.m_height, dstMip.m_width*16, rgba);
  182. imageGetRawData(*output, 0, lod, output->m_data, output->m_size, dstMip);
  183. uint8_t* data = const_cast<uint8_t*>(dstMip.m_data);
  184. imageEncodeFromRgba32f(&allocator, data, rgbaDst, dstMip.m_width, dstMip.m_height, format);
  185. }
  186. BX_FREE(&allocator, rgbaDst);
  187. }
  188. else if (8 != getBlockInfo(input->m_format).rBits)
  189. {
  190. output = imageAlloc(&allocator, format, mip.m_width, mip.m_height, 0, 1, false, mips);
  191. ImageMip dstMip;
  192. imageGetRawData(*output, 0, 0, NULL, 0, dstMip);
  193. if (mip.m_width != dstMip.m_width
  194. && mip.m_height != dstMip.m_height)
  195. {
  196. printf("Invalid input image size %dx%d, it must be at least %dx%d to be converted to %s format.\n"
  197. , mip.m_width
  198. , mip.m_height
  199. , dstMip.m_width
  200. , dstMip.m_height
  201. , getName(format)
  202. );
  203. return EXIT_FAILURE;
  204. }
  205. uint32_t size = imageGetSize(
  206. NULL
  207. , dstMip.m_width
  208. , dstMip.m_height
  209. , 0
  210. , false
  211. , false
  212. , 1
  213. , TextureFormat::RGBA32F
  214. );
  215. temp = BX_ALLOC(&allocator, size);
  216. float* rgba = (float*)temp;
  217. float* rgbaDst = (float*)BX_ALLOC(&allocator, size);
  218. imageDecodeToRgba32f(&allocator
  219. , rgba
  220. , mip.m_data
  221. , mip.m_width
  222. , mip.m_height
  223. , mip.m_width*mip.m_bpp/8
  224. , mip.m_format
  225. );
  226. imageEncodeFromRgba32f(&allocator, output->m_data, rgba, dstMip.m_width, dstMip.m_height, format);
  227. imageRgba32fToLinear(rgba
  228. , mip.m_width
  229. , mip.m_height
  230. , mip.m_width*mip.m_bpp/8
  231. , rgba
  232. );
  233. for (uint8_t lod = 1; lod < numMips; ++lod)
  234. {
  235. imageRgba32fLinearDownsample2x2(rgba, dstMip.m_width, dstMip.m_height, dstMip.m_width*16, rgba);
  236. imageGetRawData(*output, 0, lod, output->m_data, output->m_size, dstMip);
  237. uint8_t* data = const_cast<uint8_t*>(dstMip.m_data);
  238. imageRgba32fToGamma(rgbaDst
  239. , mip.m_width
  240. , mip.m_height
  241. , mip.m_width*mip.m_bpp/8
  242. , rgba
  243. );
  244. imageEncodeFromRgba32f(&allocator, data, rgbaDst, dstMip.m_width, dstMip.m_height, format);
  245. }
  246. BX_FREE(&allocator, rgbaDst);
  247. }
  248. else
  249. {
  250. output = imageAlloc(&allocator, format, mip.m_width, mip.m_height, 0, 1, false, mips);
  251. ImageMip dstMip;
  252. imageGetRawData(*output, 0, 0, NULL, 0, dstMip);
  253. if (mip.m_width != dstMip.m_width
  254. && mip.m_height != dstMip.m_height)
  255. {
  256. printf("Invalid input image size %dx%d, it must be at least %dx%d to be converted to %s format.\n"
  257. , mip.m_width
  258. , mip.m_height
  259. , dstMip.m_width
  260. , dstMip.m_height
  261. , getName(format)
  262. );
  263. return EXIT_FAILURE;
  264. }
  265. uint32_t size = imageGetSize(
  266. NULL
  267. , dstMip.m_width
  268. , dstMip.m_height
  269. , 0
  270. , false
  271. , false
  272. , 1
  273. , TextureFormat::RGBA8
  274. );
  275. temp = BX_ALLOC(&allocator, size);
  276. bx::memSet(temp, 0, size);
  277. uint8_t* rgba = (uint8_t*)temp;
  278. imageDecodeToRgba8(rgba
  279. , mip.m_data
  280. , mip.m_width
  281. , mip.m_height
  282. , mip.m_width*mip.m_bpp/8
  283. , mip.m_format
  284. );
  285. void* ref = NULL;
  286. if (iqa)
  287. {
  288. ref = BX_ALLOC(&allocator, size);
  289. bx::memCopy(ref, rgba, size);
  290. }
  291. imageEncodeFromRgba8(output->m_data, rgba, dstMip.m_width, dstMip.m_height, format);
  292. for (uint8_t lod = 1; lod < numMips; ++lod)
  293. {
  294. imageRgba8Downsample2x2(rgba, dstMip.m_width, dstMip.m_height, dstMip.m_width*4, rgba);
  295. imageGetRawData(*output, 0, lod, output->m_data, output->m_size, dstMip);
  296. uint8_t* data = const_cast<uint8_t*>(dstMip.m_data);
  297. imageEncodeFromRgba8(data, rgba, dstMip.m_width, dstMip.m_height, format);
  298. }
  299. if (NULL != ref)
  300. {
  301. imageDecodeToRgba8(rgba
  302. , output->m_data
  303. , mip.m_width
  304. , mip.m_height
  305. , mip.m_width*mip.m_bpp/8
  306. , format
  307. );
  308. static const iqa_ssim_args args =
  309. {
  310. 0.39f, // alpha
  311. 0.731f, // beta
  312. 1.12f, // gamma
  313. 187, // L
  314. 0.025987f, // K1
  315. 0.0173f, // K2
  316. 1 // factor
  317. };
  318. float result = iqa_ssim( (uint8_t*)ref
  319. , rgba
  320. , mip.m_width
  321. , mip.m_height
  322. , mip.m_width*mip.m_bpp/8
  323. , 0
  324. , &args
  325. );
  326. printf("%f\n", result);
  327. BX_FREE(&allocator, ref);
  328. }
  329. }
  330. BX_FREE(&allocator, temp);
  331. }
  332. if (NULL != output)
  333. {
  334. bx::CrtFileWriter writer;
  335. if (bx::open(&writer, outputFileName) )
  336. {
  337. if (NULL != bx::stristr(outputFileName, ".ktx") )
  338. {
  339. imageWriteKtx(&writer, *output, output->m_data, output->m_size);
  340. }
  341. bx::close(&writer);
  342. }
  343. else
  344. {
  345. help("Failed to open output file.");
  346. return EXIT_FAILURE;
  347. }
  348. imageFree(output);
  349. }
  350. else
  351. {
  352. help("No output generated.");
  353. return EXIT_FAILURE;
  354. }
  355. }
  356. else
  357. {
  358. help("Failed to load input file.");
  359. return EXIT_FAILURE;
  360. }
  361. }
  362. return EXIT_SUCCESS;
  363. }