texturec.cpp 10 KB

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