texturec.cpp 26 KB

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