texturec.cpp 28 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193
  1. /*
  2. * Copyright 2011-2018 Branimir Karadzic. All rights reserved.
  3. * License: https://github.com/bkaradzic/bimg#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 <bx/math.h>
  10. #include <bimg/decode.h>
  11. #include <bimg/encode.h>
  12. #if 0
  13. # define DBG(_format, ...) fprintf(stderr, "" _format "\n", ##__VA_ARGS__)
  14. #else
  15. # define DBG(...) BX_NOOP()
  16. #endif // DEBUG
  17. #include <bx/bx.h>
  18. #include <bx/commandline.h>
  19. #include <bx/file.h>
  20. #include <bx/uint32_t.h>
  21. #include <string>
  22. #define BIMG_TEXTUREC_VERSION_MAJOR 1
  23. #define BIMG_TEXTUREC_VERSION_MINOR 17
  24. BX_ERROR_RESULT(TEXTRUREC_ERROR, BX_MAKEFOURCC('t', 'c', 0, 0) );
  25. struct Options
  26. {
  27. Options()
  28. : maxSize(UINT32_MAX)
  29. , edge(0.0f)
  30. , format(bimg::TextureFormat::Count)
  31. , quality(bimg::Quality::Default)
  32. , mips(false)
  33. , normalMap(false)
  34. , equirect(false)
  35. , iqa(false)
  36. , pma(false)
  37. , sdf(false)
  38. , alphaTest(false)
  39. {
  40. }
  41. void dump()
  42. {
  43. DBG("Options:\n"
  44. "\t maxSize: %d\n"
  45. "\t edge: %f\n"
  46. "\t format: %s\n"
  47. "\t mips: %s\n"
  48. "\tnormalMap: %s\n"
  49. "\t iqa: %s\n"
  50. "\t pma: %s\n"
  51. "\t sdf: %s\n"
  52. "\t radiance: %s\n"
  53. , maxSize
  54. , edge
  55. , bimg::getName(format)
  56. , mips ? "true" : "false"
  57. , normalMap ? "true" : "false"
  58. , iqa ? "true" : "false"
  59. , pma ? "true" : "false"
  60. , sdf ? "true" : "false"
  61. , radiance ? "true" : "false"
  62. );
  63. }
  64. uint32_t maxSize;
  65. float edge;
  66. bimg::TextureFormat::Enum format;
  67. bimg::Quality::Enum quality;
  68. bool mips;
  69. bool normalMap;
  70. bool equirect;
  71. bool iqa;
  72. bool pma;
  73. bool sdf;
  74. bool alphaTest;
  75. bool radiance;
  76. };
  77. void imageRgba32fNormalize(void* _dst, uint32_t _width, uint32_t _height, uint32_t _srcPitch, const void* _src)
  78. {
  79. const uint8_t* src = (const uint8_t*)_src;
  80. uint8_t* dst = (uint8_t*)_dst;
  81. for (uint32_t yy = 0, ystep = _srcPitch; yy < _height; ++yy, src += ystep)
  82. {
  83. const float* rgba = (const float*)&src[0];
  84. for (uint32_t xx = 0; xx < _width; ++xx, rgba += 4, dst += 16)
  85. {
  86. float xyz[3];
  87. xyz[0] = rgba[0];
  88. xyz[1] = rgba[1];
  89. xyz[2] = rgba[2];
  90. bx::vec3Norm( (float*)dst, xyz);
  91. }
  92. }
  93. }
  94. void imagePremultiplyAlpha(void* _inOut, uint32_t _width, uint32_t _height, uint32_t _depth, bimg::TextureFormat::Enum _format)
  95. {
  96. uint8_t* inOut = (uint8_t*)_inOut;
  97. uint32_t bpp = bimg::getBitsPerPixel(_format);
  98. uint32_t pitch = _width*bpp/8;
  99. bimg::PackFn pack = bimg::getPack(_format);
  100. bimg::UnpackFn unpack = bimg::getUnpack(_format);
  101. for (uint32_t zz = 0; zz < _depth; ++zz)
  102. {
  103. for (uint32_t yy = 0; yy < _height; ++yy)
  104. {
  105. for (uint32_t xx = 0; xx < _width; ++xx)
  106. {
  107. const uint32_t offset = yy*pitch + xx*bpp/8;
  108. float rgba[4];
  109. unpack(rgba, &inOut[offset]);
  110. const float alpha = rgba[3];
  111. rgba[0] = bx::toGamma(bx::toLinear(rgba[0]) * alpha);
  112. rgba[1] = bx::toGamma(bx::toLinear(rgba[1]) * alpha);
  113. rgba[2] = bx::toGamma(bx::toLinear(rgba[2]) * alpha);
  114. pack(&inOut[offset], rgba);
  115. }
  116. }
  117. }
  118. }
  119. bimg::ImageContainer* convert(bx::AllocatorI* _allocator, const void* _inputData, uint32_t _inputSize, const Options& _options, bx::Error* _err)
  120. {
  121. BX_ERROR_SCOPE(_err);
  122. const uint8_t* inputData = (uint8_t*)_inputData;
  123. bimg::ImageContainer* output = NULL;
  124. bimg::ImageContainer* input = bimg::imageParse(_allocator, inputData, _inputSize, bimg::TextureFormat::Count, _err);
  125. if (!_err->isOk() )
  126. {
  127. return NULL;
  128. }
  129. if (NULL != input)
  130. {
  131. bimg::TextureFormat::Enum inputFormat = input->m_format;
  132. bimg::TextureFormat::Enum outputFormat = input->m_format;
  133. if (bimg::TextureFormat::Count != _options.format)
  134. {
  135. outputFormat = _options.format;
  136. }
  137. if (_options.sdf)
  138. {
  139. outputFormat = bimg::TextureFormat::R8;
  140. }
  141. const bimg::ImageBlockInfo& inputBlockInfo = bimg::getBlockInfo(inputFormat);
  142. const bimg::ImageBlockInfo& outputBlockInfo = bimg::getBlockInfo(outputFormat);
  143. const uint32_t blockWidth = outputBlockInfo.blockWidth;
  144. const uint32_t blockHeight = outputBlockInfo.blockHeight;
  145. const uint32_t minBlockX = outputBlockInfo.minBlockX;
  146. const uint32_t minBlockY = outputBlockInfo.minBlockY;
  147. uint32_t outputWidth = bx::uint32_max(blockWidth * minBlockX, ( (input->m_width + blockWidth - 1) / blockWidth )*blockWidth);
  148. uint32_t outputHeight = bx::uint32_max(blockHeight * minBlockY, ( (input->m_height + blockHeight - 1) / blockHeight)*blockHeight);
  149. uint32_t outputDepth = input->m_depth;
  150. if (_options.equirect)
  151. {
  152. if (outputDepth == 1
  153. && outputWidth/2 == outputHeight)
  154. {
  155. if (outputWidth/2 > _options.maxSize)
  156. {
  157. outputWidth = _options.maxSize*4;
  158. outputHeight = _options.maxSize*2;
  159. }
  160. }
  161. else
  162. {
  163. bimg::imageFree(input);
  164. BX_ERROR_SET(_err, TEXTRUREC_ERROR, "Input image format is not equirectangular projection.");
  165. return NULL;
  166. }
  167. }
  168. else if (outputWidth > _options.maxSize
  169. || outputHeight > _options.maxSize
  170. || outputDepth > _options.maxSize)
  171. {
  172. if (outputDepth > outputWidth
  173. && outputDepth > outputHeight)
  174. {
  175. outputWidth = outputWidth * _options.maxSize / outputDepth;
  176. outputHeight = outputHeight * _options.maxSize / outputDepth;
  177. outputDepth = _options.maxSize;
  178. }
  179. else if (outputWidth > outputHeight)
  180. {
  181. outputDepth = outputDepth * _options.maxSize / outputWidth;
  182. outputHeight = outputHeight * _options.maxSize / outputWidth;
  183. outputWidth = _options.maxSize;
  184. }
  185. else
  186. {
  187. outputDepth = outputDepth * _options.maxSize / outputHeight;
  188. outputWidth = outputWidth * _options.maxSize / outputHeight;
  189. outputHeight = _options.maxSize;
  190. }
  191. }
  192. const bool needResize = false
  193. || input->m_width != outputWidth
  194. || input->m_height != outputHeight
  195. ;
  196. const bool passThru = true
  197. && !needResize
  198. && (1 < input->m_numMips) == _options.mips
  199. && !_options.sdf
  200. && !_options.alphaTest
  201. && !_options.normalMap
  202. && !_options.equirect
  203. && !_options.iqa
  204. && !_options.pma
  205. && !_options.radiance
  206. ;
  207. if (needResize)
  208. {
  209. bimg::ImageContainer* src = bimg::imageConvert(_allocator, bimg::TextureFormat::RGBA32F, *input, false);
  210. bimg::ImageContainer* dst = bimg::imageAlloc(
  211. _allocator
  212. , bimg::TextureFormat::RGBA32F
  213. , uint16_t(outputWidth)
  214. , uint16_t(outputHeight)
  215. , uint16_t(outputDepth)
  216. , input->m_numLayers
  217. , input->m_cubeMap
  218. , false
  219. );
  220. bimg::imageResizeRgba32fLinear(dst, src);
  221. bimg::imageFree(src);
  222. bimg::imageFree(input);
  223. if (bimg::isCompressed(inputFormat) )
  224. {
  225. if (inputFormat == bimg::TextureFormat::BC6H)
  226. {
  227. inputFormat = bimg::TextureFormat::RGBA32F;
  228. }
  229. else
  230. {
  231. inputFormat = bimg::TextureFormat::RGBA8;
  232. }
  233. }
  234. input = bimg::imageConvert(_allocator, inputFormat, *dst);
  235. bimg::imageFree(dst);
  236. }
  237. if (passThru)
  238. {
  239. if (inputFormat != outputFormat
  240. && bimg::isCompressed(outputFormat) )
  241. {
  242. output = bimg::imageEncode(_allocator, outputFormat, _options.quality, *input);
  243. }
  244. else
  245. {
  246. output = bimg::imageConvert(_allocator, outputFormat, *input);
  247. }
  248. bimg::imageFree(input);
  249. return output;
  250. }
  251. if (_options.equirect)
  252. {
  253. bimg::ImageContainer* src = bimg::imageConvert(_allocator, bimg::TextureFormat::RGBA32F, *input);
  254. bimg::imageFree(input);
  255. bimg::ImageContainer* dst = bimg::imageCubemapFromLatLongRgba32F(_allocator, *src, true, _err);
  256. bimg::imageFree(src);
  257. if (!_err->isOk() )
  258. {
  259. return NULL;
  260. }
  261. input = bimg::imageConvert(_allocator, inputFormat, *dst);
  262. bimg::imageFree(dst);
  263. }
  264. if (_options.radiance)
  265. {
  266. output = bimg::imageCubemapRadianceFilter(_allocator, *input);
  267. bimg::imageFree(input);
  268. return output;
  269. }
  270. output = bimg::imageAlloc(
  271. _allocator
  272. , outputFormat
  273. , uint16_t(input->m_width)
  274. , uint16_t(input->m_height)
  275. , uint16_t(input->m_depth)
  276. , input->m_numLayers
  277. , input->m_cubeMap
  278. , _options.mips
  279. );
  280. const uint8_t numMips = output->m_numMips;
  281. const uint16_t numSides = output->m_numLayers * (output->m_cubeMap ? 6 : 1);
  282. for (uint16_t side = 0; side < numSides && _err->isOk(); ++side)
  283. {
  284. bimg::ImageMip mip;
  285. if (bimg::imageGetRawData(*input, side, 0, input->m_data, input->m_size, mip) )
  286. {
  287. bimg::ImageMip dstMip;
  288. bimg::imageGetRawData(*output, side, 0, output->m_data, output->m_size, dstMip);
  289. uint8_t* dstData = const_cast<uint8_t*>(dstMip.m_data);
  290. void* temp = NULL;
  291. // Normal map.
  292. if (_options.normalMap)
  293. {
  294. uint32_t size = bimg::imageGetSize(
  295. NULL
  296. , uint16_t(dstMip.m_width)
  297. , uint16_t(dstMip.m_height)
  298. , 0
  299. , false
  300. , false
  301. , 1
  302. , bimg::TextureFormat::RGBA32F
  303. );
  304. temp = BX_ALLOC(_allocator, size);
  305. float* rgba = (float*)temp;
  306. float* rgbaDst = (float*)BX_ALLOC(_allocator, size);
  307. bimg::imageDecodeToRgba32f(_allocator
  308. , rgba
  309. , mip.m_data
  310. , dstMip.m_width
  311. , dstMip.m_height
  312. , dstMip.m_depth
  313. , dstMip.m_width*16
  314. , mip.m_format
  315. );
  316. if (bimg::TextureFormat::BC5 != mip.m_format)
  317. {
  318. for (uint32_t yy = 0; yy < mip.m_height; ++yy)
  319. {
  320. for (uint32_t xx = 0; xx < mip.m_width; ++xx)
  321. {
  322. const uint32_t offset = (yy*mip.m_width + xx) * 4;
  323. float* inout = &rgba[offset];
  324. inout[0] = inout[0] * 2.0f - 1.0f;
  325. inout[1] = inout[1] * 2.0f - 1.0f;
  326. inout[2] = inout[2] * 2.0f - 1.0f;
  327. inout[3] = inout[3] * 2.0f - 1.0f;
  328. }
  329. }
  330. }
  331. imageRgba32fNormalize(rgba
  332. , dstMip.m_width
  333. , dstMip.m_height
  334. , dstMip.m_width*16
  335. , rgba
  336. );
  337. bimg::imageRgba32f11to01(rgbaDst
  338. , dstMip.m_width
  339. , dstMip.m_height
  340. , dstMip.m_depth
  341. , dstMip.m_width*16
  342. , rgba
  343. );
  344. bimg::imageEncodeFromRgba32f(_allocator
  345. , dstData
  346. , rgbaDst
  347. , dstMip.m_width
  348. , dstMip.m_height
  349. , dstMip.m_depth
  350. , outputFormat
  351. , _options.quality
  352. , _err
  353. );
  354. for (uint8_t lod = 1; lod < numMips && _err->isOk(); ++lod)
  355. {
  356. bimg::imageRgba32fDownsample2x2NormalMap(rgba
  357. , dstMip.m_width
  358. , dstMip.m_height
  359. , dstMip.m_width*16
  360. , bx::strideAlign(dstMip.m_width/2, blockWidth)*16
  361. , rgba
  362. );
  363. bimg::imageRgba32f11to01(rgbaDst
  364. , dstMip.m_width
  365. , dstMip.m_height
  366. , dstMip.m_depth
  367. , dstMip.m_width*16
  368. , rgba
  369. );
  370. bimg::imageGetRawData(*output, side, lod, output->m_data, output->m_size, dstMip);
  371. dstData = const_cast<uint8_t*>(dstMip.m_data);
  372. bimg::imageEncodeFromRgba32f(_allocator
  373. , dstData
  374. , rgbaDst
  375. , dstMip.m_width
  376. , dstMip.m_height
  377. , dstMip.m_depth
  378. , outputFormat
  379. , _options.quality
  380. , _err
  381. );
  382. }
  383. BX_FREE(_allocator, rgbaDst);
  384. }
  385. // HDR
  386. else if ( (!bimg::isCompressed(inputFormat) && 8 != inputBlockInfo.rBits)
  387. || outputFormat == bimg::TextureFormat::BC6H
  388. || outputFormat == bimg::TextureFormat::BC7
  389. )
  390. {
  391. uint32_t size = bimg::imageGetSize(
  392. NULL
  393. , uint16_t(dstMip.m_width)
  394. , uint16_t(dstMip.m_height)
  395. , uint16_t(dstMip.m_depth)
  396. , false
  397. , false
  398. , 1
  399. , bimg::TextureFormat::RGBA32F
  400. );
  401. temp = BX_ALLOC(_allocator, size);
  402. float* rgba32f = (float*)temp;
  403. float* rgbaDst = (float*)BX_ALLOC(_allocator, size);
  404. bimg::imageDecodeToRgba32f(_allocator
  405. , rgba32f
  406. , mip.m_data
  407. , mip.m_width
  408. , mip.m_height
  409. , mip.m_depth
  410. , mip.m_width*16
  411. , mip.m_format
  412. );
  413. if (_options.pma)
  414. {
  415. imagePremultiplyAlpha(
  416. rgba32f
  417. , dstMip.m_width
  418. , dstMip.m_height
  419. , dstMip.m_depth
  420. , bimg::TextureFormat::RGBA32F
  421. );
  422. }
  423. bimg::imageEncodeFromRgba32f(_allocator
  424. , dstData
  425. , rgba32f
  426. , dstMip.m_width
  427. , dstMip.m_height
  428. , dstMip.m_depth
  429. , outputFormat
  430. , _options.quality
  431. , _err
  432. );
  433. if (1 < numMips
  434. && _err->isOk() )
  435. {
  436. bimg::imageRgba32fToLinear(rgba32f
  437. , mip.m_width
  438. , mip.m_height
  439. , mip.m_depth
  440. , mip.m_width*16
  441. , rgba32f
  442. );
  443. for (uint8_t lod = 1; lod < numMips && _err->isOk(); ++lod)
  444. {
  445. bimg::imageRgba32fLinearDownsample2x2(rgba32f
  446. , dstMip.m_width
  447. , dstMip.m_height
  448. , dstMip.m_depth
  449. , dstMip.m_width*16
  450. , rgba32f
  451. );
  452. if (_options.pma)
  453. {
  454. imagePremultiplyAlpha(
  455. rgba32f
  456. , dstMip.m_width
  457. , dstMip.m_height
  458. , dstMip.m_depth
  459. , bimg::TextureFormat::RGBA32F
  460. );
  461. }
  462. bimg::imageGetRawData(*output, side, lod, output->m_data, output->m_size, dstMip);
  463. dstData = const_cast<uint8_t*>(dstMip.m_data);
  464. bimg::imageRgba32fToGamma(rgbaDst
  465. , mip.m_width
  466. , mip.m_height
  467. , mip.m_depth
  468. , mip.m_width*16
  469. , rgba32f
  470. );
  471. bimg::imageEncodeFromRgba32f(_allocator
  472. , dstData
  473. , rgbaDst
  474. , dstMip.m_width
  475. , dstMip.m_height
  476. , dstMip.m_depth
  477. , outputFormat
  478. , _options.quality
  479. , _err
  480. );
  481. }
  482. }
  483. BX_FREE(_allocator, rgbaDst);
  484. }
  485. // SDF
  486. else if (_options.sdf)
  487. {
  488. uint32_t size = bimg::imageGetSize(
  489. NULL
  490. , uint16_t(dstMip.m_width)
  491. , uint16_t(dstMip.m_height)
  492. , uint16_t(dstMip.m_depth)
  493. , false
  494. , false
  495. , 1
  496. , bimg::TextureFormat::R8
  497. );
  498. temp = BX_ALLOC(_allocator, size);
  499. uint8_t* rgba = (uint8_t*)temp;
  500. bimg::imageDecodeToR8(_allocator
  501. , rgba
  502. , mip.m_data
  503. , mip.m_width
  504. , mip.m_height
  505. , mip.m_depth
  506. , mip.m_width
  507. , mip.m_format
  508. );
  509. bimg::imageGetRawData(*output, side, 0, output->m_data, output->m_size, dstMip);
  510. dstData = const_cast<uint8_t*>(dstMip.m_data);
  511. bimg::imageMakeDist(_allocator
  512. , dstData
  513. , mip.m_width
  514. , mip.m_height
  515. , mip.m_width
  516. , _options.edge
  517. , rgba
  518. );
  519. }
  520. // RGBA8
  521. else
  522. {
  523. uint32_t size = bimg::imageGetSize(
  524. NULL
  525. , uint16_t(dstMip.m_width)
  526. , uint16_t(dstMip.m_height)
  527. , uint16_t(dstMip.m_depth)
  528. , false
  529. , false
  530. , 1
  531. , bimg::TextureFormat::RGBA8
  532. );
  533. temp = BX_ALLOC(_allocator, size);
  534. uint8_t* rgba = (uint8_t*)temp;
  535. bimg::imageDecodeToRgba8(
  536. _allocator
  537. , rgba
  538. , mip.m_data
  539. , mip.m_width
  540. , mip.m_height
  541. , mip.m_width*4
  542. , mip.m_format
  543. );
  544. float coverage = 0.0f;
  545. if (_options.alphaTest)
  546. {
  547. coverage = bimg::imageAlphaTestCoverage(bimg::TextureFormat::RGBA8
  548. , mip.m_width
  549. , mip.m_height
  550. , mip.m_width*4
  551. , rgba
  552. , _options.edge
  553. );
  554. }
  555. void* ref = NULL;
  556. if (_options.iqa)
  557. {
  558. ref = BX_ALLOC(_allocator, size);
  559. bx::memCopy(ref, rgba, size);
  560. }
  561. if (_options.pma)
  562. {
  563. imagePremultiplyAlpha(
  564. rgba
  565. , dstMip.m_width
  566. , dstMip.m_height
  567. , dstMip.m_depth
  568. , bimg::TextureFormat::RGBA8
  569. );
  570. }
  571. bimg::imageGetRawData(*output, side, 0, output->m_data, output->m_size, dstMip);
  572. dstData = const_cast<uint8_t*>(dstMip.m_data);
  573. bimg::imageEncodeFromRgba8(
  574. _allocator
  575. , dstData
  576. , rgba
  577. , dstMip.m_width
  578. , dstMip.m_height
  579. , dstMip.m_depth
  580. , outputFormat
  581. , _options.quality
  582. , _err
  583. );
  584. for (uint8_t lod = 1; lod < numMips && _err->isOk(); ++lod)
  585. {
  586. bimg::imageRgba8Downsample2x2(rgba
  587. , dstMip.m_width
  588. , dstMip.m_height
  589. , dstMip.m_depth
  590. , dstMip.m_width*4
  591. , bx::strideAlign(dstMip.m_width/2, blockWidth)*4
  592. , rgba
  593. );
  594. if (_options.alphaTest)
  595. {
  596. bimg::imageScaleAlphaToCoverage(bimg::TextureFormat::RGBA8
  597. , dstMip.m_width
  598. , dstMip.m_height
  599. , dstMip.m_width*4
  600. , rgba
  601. , coverage
  602. , _options.edge
  603. );
  604. }
  605. if (_options.pma)
  606. {
  607. imagePremultiplyAlpha(
  608. rgba
  609. , dstMip.m_width
  610. , dstMip.m_height
  611. , dstMip.m_depth
  612. , bimg::TextureFormat::RGBA8
  613. );
  614. }
  615. bimg::imageGetRawData(*output, side, lod, output->m_data, output->m_size, dstMip);
  616. dstData = const_cast<uint8_t*>(dstMip.m_data);
  617. bimg::imageEncodeFromRgba8(
  618. _allocator
  619. , dstData
  620. , rgba
  621. , dstMip.m_width
  622. , dstMip.m_height
  623. , dstMip.m_depth
  624. , outputFormat
  625. , _options.quality
  626. , _err
  627. );
  628. }
  629. if (NULL != ref)
  630. {
  631. bimg::imageDecodeToRgba8(
  632. _allocator
  633. , rgba
  634. , output->m_data
  635. , mip.m_width
  636. , mip.m_height
  637. , mip.m_width*mip.m_bpp/8
  638. , outputFormat
  639. );
  640. float result = bimg::imageQualityRgba8(
  641. ref
  642. , rgba
  643. , uint16_t(mip.m_width)
  644. , uint16_t(mip.m_height)
  645. );
  646. printf("%f\n", result);
  647. BX_FREE(_allocator, ref);
  648. }
  649. }
  650. BX_FREE(_allocator, temp);
  651. }
  652. }
  653. bimg::imageFree(input);
  654. }
  655. if (!_err->isOk()
  656. && NULL != output)
  657. {
  658. bimg::imageFree(output);
  659. output = NULL;
  660. }
  661. return output;
  662. }
  663. void help(const char* _error = NULL, bool _showHelp = true)
  664. {
  665. if (NULL != _error)
  666. {
  667. fprintf(stderr, "Error:\n%s\n\n", _error);
  668. if (!_showHelp)
  669. {
  670. return;
  671. }
  672. }
  673. fprintf(stderr
  674. , "texturec, bgfx texture compiler tool, version %d.%d.%d.\n"
  675. "Copyright 2011-2018 Branimir Karadzic. All rights reserved.\n"
  676. "License: https://github.com/bkaradzic/bimg#license-bsd-2-clause\n\n"
  677. , BIMG_TEXTUREC_VERSION_MAJOR
  678. , BIMG_TEXTUREC_VERSION_MINOR
  679. , BIMG_API_VERSION
  680. );
  681. fprintf(stderr
  682. , "Usage: texturec -f <in> -o <out> [-t <texture format>]\n"
  683. "\n"
  684. "Supported file formats:\n"
  685. " *.bmp (input) Windows Bitmap.\n"
  686. " *.dds (input, output) Direct Draw Surface.\n"
  687. " *.exr (input, output) OpenEXR.\n"
  688. " *.gif (input) Graphics Interchange Format.\n"
  689. " *.jpg (input) JPEG Interchange Format.\n"
  690. " *.hdr (input, output) Radiance RGBE.\n"
  691. " *.ktx (input, output) Khronos Texture.\n"
  692. " *.png (input, output) Portable Network Graphics.\n"
  693. " *.psd (input) Photoshop Document.\n"
  694. " *.pvr (input) PowerVR.\n"
  695. " *.tga (input) Targa.\n"
  696. "\n"
  697. "Options:\n"
  698. " -h, --help Help.\n"
  699. " -v, --version Version information only.\n"
  700. " -f <file path> Input file path.\n"
  701. " -o <file path> Output file path.\n"
  702. " -t <format> Output format type (BC1/2/3/4/5, ETC1, PVR14, etc.).\n"
  703. " -q <quality> Encoding quality (default, fastest, highest).\n"
  704. " -m, --mips Generate mip-maps.\n"
  705. " -n, --normalmap Input texture is normal map.\n"
  706. " --equirect Input texture equirectangular projection of cubemap.\n"
  707. " --sdf <edge> Compute SDF texture.\n"
  708. " --ref <alpha> Alpha reference value.\n"
  709. " --iqa Image Quality Assessment\n"
  710. " --pma Premultiply alpha into RGB channel.\n"
  711. " --max <max size> Maximum width/height (image will be scaled down and\n"
  712. " aspect ratio will be preserved.\n"
  713. " --as <extension> Save as.\n"
  714. " --validate *DEBUG* Validate that output image produced matches after loading.\n"
  715. "\n"
  716. "For additional information, see https://github.com/bkaradzic/bgfx\n"
  717. );
  718. }
  719. void help(const char* _str, const bx::Error& _err)
  720. {
  721. std::string str;
  722. if (_str != NULL)
  723. {
  724. str.append(_str);
  725. str.append(" ");
  726. }
  727. const bx::StringView& sv = _err.getMessage();
  728. str.append(sv.getPtr(), sv.getTerm() - sv.getPtr() );
  729. help(str.c_str(), false);
  730. }
  731. class AlignedAllocator : public bx::AllocatorI
  732. {
  733. public:
  734. AlignedAllocator(bx::AllocatorI* _allocator, size_t _minAlignment)
  735. : m_allocator(_allocator)
  736. , m_minAlignment(_minAlignment)
  737. {
  738. }
  739. virtual void* realloc(
  740. void* _ptr
  741. , size_t _size
  742. , size_t _align
  743. , const char* _file
  744. , uint32_t _line
  745. )
  746. {
  747. return m_allocator->realloc(_ptr, _size, bx::max(_align, m_minAlignment), _file, _line);
  748. }
  749. bx::AllocatorI* m_allocator;
  750. size_t m_minAlignment;
  751. };
  752. int main(int _argc, const char* _argv[])
  753. {
  754. bx::CommandLine cmdLine(_argc, _argv);
  755. if (cmdLine.hasArg('v', "version") )
  756. {
  757. fprintf(stderr
  758. , "texturec, bgfx texture compiler tool, version %d.%d.%d.\n"
  759. , BIMG_TEXTUREC_VERSION_MAJOR
  760. , BIMG_TEXTUREC_VERSION_MINOR
  761. , BIMG_API_VERSION
  762. );
  763. return bx::kExitSuccess;
  764. }
  765. if (cmdLine.hasArg('h', "help") )
  766. {
  767. help();
  768. return bx::kExitFailure;
  769. }
  770. const char* inputFileName = cmdLine.findOption('f');
  771. if (NULL == inputFileName)
  772. {
  773. help("Input file must be specified.");
  774. return bx::kExitFailure;
  775. }
  776. const char* outputFileName = cmdLine.findOption('o');
  777. if (NULL == outputFileName)
  778. {
  779. help("Output file must be specified.");
  780. return bx::kExitFailure;
  781. }
  782. const char* saveAs = cmdLine.findOption("as");
  783. saveAs = NULL == saveAs ? bx::strFindI(outputFileName, ".ktx") : saveAs;
  784. saveAs = NULL == saveAs ? bx::strFindI(outputFileName, ".dds") : saveAs;
  785. saveAs = NULL == saveAs ? bx::strFindI(outputFileName, ".png") : saveAs;
  786. saveAs = NULL == saveAs ? bx::strFindI(outputFileName, ".exr") : saveAs;
  787. saveAs = NULL == saveAs ? bx::strFindI(outputFileName, ".hdr") : saveAs;
  788. if (NULL == saveAs)
  789. {
  790. help("Output file format must be specified.");
  791. return bx::kExitFailure;
  792. }
  793. Options options;
  794. const char* edgeOpt = cmdLine.findOption("sdf");
  795. if (NULL != edgeOpt)
  796. {
  797. options.sdf = true;
  798. if (!bx::fromString(&options.edge, edgeOpt) )
  799. {
  800. options.edge = 255.0f;
  801. }
  802. }
  803. else
  804. {
  805. const char* alphaRef = cmdLine.findOption("ref");
  806. if (NULL != alphaRef)
  807. {
  808. options.alphaTest = true;
  809. if (!bx::fromString(&options.edge, alphaRef))
  810. {
  811. options.edge = 0.5f;
  812. }
  813. }
  814. }
  815. options.mips = cmdLine.hasArg('m', "mips");
  816. options.normalMap = cmdLine.hasArg('n', "normalmap");
  817. options.equirect = cmdLine.hasArg("equirect");
  818. options.iqa = cmdLine.hasArg("iqa");
  819. options.pma = cmdLine.hasArg("pma");
  820. options.radiance = cmdLine.hasArg("radiance");
  821. const char* maxSize = cmdLine.findOption("max");
  822. if (NULL != maxSize)
  823. {
  824. if (!bx::fromString(&options.maxSize, maxSize) )
  825. {
  826. help("Parsing max size failed.");
  827. return bx::kExitFailure;
  828. }
  829. }
  830. options.format = bimg::TextureFormat::Count;
  831. const char* type = cmdLine.findOption('t');
  832. if (NULL != type)
  833. {
  834. options.format = bimg::getFormat(type);
  835. if (!bimg::isValid(options.format) )
  836. {
  837. help("Invalid format specified.");
  838. return bx::kExitFailure;
  839. }
  840. }
  841. if (NULL != bx::strFindI(saveAs, "png") )
  842. {
  843. if (options.format == bimg::TextureFormat::Count)
  844. {
  845. options.format = bimg::TextureFormat::RGBA8;
  846. }
  847. else if (options.format != bimg::TextureFormat::RGBA8)
  848. {
  849. help("Output PNG format must be RGBA8.");
  850. return bx::kExitFailure;
  851. }
  852. }
  853. else if (NULL != bx::strFindI(saveAs, "exr") )
  854. {
  855. if (options.format == bimg::TextureFormat::Count)
  856. {
  857. options.format = bimg::TextureFormat::RGBA16F;
  858. }
  859. else if (options.format != bimg::TextureFormat::RGBA16F)
  860. {
  861. help("Output EXR format must be RGBA16F.");
  862. return bx::kExitFailure;
  863. }
  864. }
  865. const char* quality = cmdLine.findOption('q');
  866. if (NULL != quality)
  867. {
  868. switch (bx::toLower(quality[0]) )
  869. {
  870. case 'h': options.quality = bimg::Quality::Highest; break;
  871. case 'f': options.quality = bimg::Quality::Fastest; break;
  872. case 'd': options.quality = bimg::Quality::Default; break;
  873. default:
  874. help("Invalid quality specified.");
  875. return bx::kExitFailure;
  876. }
  877. }
  878. const bool validate = cmdLine.hasArg("validate");
  879. bx::Error err;
  880. bx::FileReader reader;
  881. if (!bx::open(&reader, inputFileName, &err) )
  882. {
  883. help("Failed to open input file.", err);
  884. return bx::kExitFailure;
  885. }
  886. uint32_t inputSize = (uint32_t)bx::getSize(&reader);
  887. if (0 == inputSize)
  888. {
  889. help("Failed to read input file.", err);
  890. return bx::kExitFailure;
  891. }
  892. bx::DefaultAllocator defaultAllocator;
  893. AlignedAllocator allocator(&defaultAllocator, 16);
  894. uint8_t* inputData = (uint8_t*)BX_ALLOC(&allocator, inputSize);
  895. bx::read(&reader, inputData, inputSize, &err);
  896. bx::close(&reader);
  897. if (!err.isOk() )
  898. {
  899. help("Failed to read input file.", err);
  900. return bx::kExitFailure;
  901. }
  902. bimg::ImageContainer* output = convert(&allocator, inputData, inputSize, options, &err);
  903. BX_FREE(&allocator, inputData);
  904. if (NULL != output)
  905. {
  906. bx::FileWriter writer;
  907. if (bx::open(&writer, outputFileName, false, &err) )
  908. {
  909. if (NULL != bx::strFindI(saveAs, "ktx") )
  910. {
  911. bimg::imageWriteKtx(&writer, *output, output->m_data, output->m_size, &err);
  912. }
  913. else if (NULL != bx::strFindI(saveAs, "dds") )
  914. {
  915. bimg::imageWriteDds(&writer, *output, output->m_data, output->m_size, &err);
  916. }
  917. else if (NULL != bx::strFindI(saveAs, "png") )
  918. {
  919. if (output->m_format != bimg::TextureFormat::RGBA8)
  920. {
  921. help("Incompatible output texture format. Output PNG format must be RGBA8.", err);
  922. return bx::kExitFailure;
  923. }
  924. bimg::ImageMip mip;
  925. bimg::imageGetRawData(*output, 0, 0, output->m_data, output->m_size, mip);
  926. bimg::imageWritePng(&writer
  927. , mip.m_width
  928. , mip.m_height
  929. , mip.m_width*4
  930. , mip.m_data
  931. , output->m_format
  932. , false
  933. , &err
  934. );
  935. }
  936. else if (NULL != bx::strFindI(saveAs, "exr") )
  937. {
  938. bimg::ImageMip mip;
  939. bimg::imageGetRawData(*output, 0, 0, output->m_data, output->m_size, mip);
  940. bimg::imageWriteExr(&writer
  941. , mip.m_width
  942. , mip.m_height
  943. , mip.m_width*8
  944. , mip.m_data
  945. , output->m_format
  946. , false
  947. , &err
  948. );
  949. }
  950. else if (NULL != bx::strFindI(saveAs, "hdr") )
  951. {
  952. bimg::ImageMip mip;
  953. bimg::imageGetRawData(*output, 0, 0, output->m_data, output->m_size, mip);
  954. bimg::imageWriteHdr(&writer
  955. , mip.m_width
  956. , mip.m_height
  957. , mip.m_width*getBitsPerPixel(mip.m_format)/8
  958. , mip.m_data
  959. , output->m_format
  960. , false
  961. , &err
  962. );
  963. }
  964. bx::close(&writer);
  965. if (!err.isOk() )
  966. {
  967. help(NULL, err);
  968. return bx::kExitFailure;
  969. }
  970. }
  971. else
  972. {
  973. help("Failed to open output file.", err);
  974. return bx::kExitFailure;
  975. }
  976. if (validate)
  977. {
  978. if (!bx::open(&reader, outputFileName, &err) )
  979. {
  980. help("Failed to validate file.", err);
  981. return bx::kExitFailure;
  982. }
  983. inputSize = (uint32_t)bx::getSize(&reader);
  984. if (0 == inputSize)
  985. {
  986. help("Failed to validate file.", err);
  987. return bx::kExitFailure;
  988. }
  989. inputData = (uint8_t*)BX_ALLOC(&allocator, inputSize);
  990. bx::read(&reader, inputData, inputSize, &err);
  991. bx::close(&reader);
  992. bimg::ImageContainer* input = bimg::imageParse(&allocator, inputData, inputSize, bimg::TextureFormat::Count, &err);
  993. if (!err.isOk() )
  994. {
  995. help("Failed to validate file.", err);
  996. return bx::kExitFailure;
  997. }
  998. if (false
  999. || input->m_format != output->m_format
  1000. || input->m_size != output->m_size
  1001. || input->m_width != output->m_width
  1002. || input->m_height != output->m_height
  1003. || input->m_depth != output->m_depth
  1004. || input->m_numLayers != output->m_numLayers
  1005. || input->m_numMips != output->m_numMips
  1006. || input->m_hasAlpha != output->m_hasAlpha
  1007. || input->m_cubeMap != output->m_cubeMap
  1008. )
  1009. {
  1010. help("Validation failed, image headers are different.");
  1011. return bx::kExitFailure;
  1012. }
  1013. {
  1014. const uint8_t numMips = output->m_numMips;
  1015. const uint16_t numSides = output->m_numLayers * (output->m_cubeMap ? 6 : 1);
  1016. for (uint8_t lod = 0; lod < numMips; ++lod)
  1017. {
  1018. for (uint16_t side = 0; side < numSides; ++side)
  1019. {
  1020. bimg::ImageMip srcMip;
  1021. bool hasSrc = bimg::imageGetRawData(*input, side, lod, input->m_data, input->m_size, srcMip);
  1022. bimg::ImageMip dstMip;
  1023. bool hasDst = bimg::imageGetRawData(*output, side, lod, output->m_data, output->m_size, dstMip);
  1024. if (false
  1025. || hasSrc != hasDst
  1026. || srcMip.m_size != dstMip.m_size
  1027. )
  1028. {
  1029. help("Validation failed, image mip/layer/side are different.");
  1030. return bx::kExitFailure;
  1031. }
  1032. if (0 != bx::memCmp(srcMip.m_data, dstMip.m_data, srcMip.m_size) )
  1033. {
  1034. help("Validation failed, image content are different.");
  1035. return bx::kExitFailure;
  1036. }
  1037. }
  1038. }
  1039. }
  1040. BX_FREE(&allocator, inputData);
  1041. }
  1042. bimg::imageFree(output);
  1043. }
  1044. else
  1045. {
  1046. help(NULL, err);
  1047. return bx::kExitFailure;
  1048. }
  1049. return bx::kExitSuccess;
  1050. }