CImageLoaderTEX.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. #include "CImageLoaderTEX.h"
  2. #include <irrlicht/irrlicht.h>
  3. #include <lz4.h>
  4. #include <wallpaperengine/irrlicht.h>
  5. #include <string>
  6. namespace irr {
  7. namespace video {
  8. //! returns true if the file maybe is able to be loaded by this class
  9. //! based on the file extension (e.g. ".tga")
  10. bool CImageLoaderTex::isALoadableFileExtension (const io::path &filename) const
  11. {
  12. return core::hasFileExtension (filename, "tex");
  13. }
  14. //! returns true if the file maybe is able to be loaded by this class
  15. bool CImageLoaderTex::isALoadableFileFormat (io::IReadFile *file) const
  16. {
  17. return false;
  18. }
  19. // load in the image data
  20. IImage *CImageLoaderTex::loadImage (io::IReadFile *input) const
  21. {
  22. if (!input)
  23. return nullptr;
  24. video::IImage *image = nullptr;
  25. char buffer [1024];
  26. if (input->read (buffer, 9) != 9)
  27. {
  28. wp::irrlicht::device->getLogger ()->log ("LOAD TEX: cannot read header\n", input->getFileName ().c_str (), irr::ELL_ERROR);
  29. return nullptr;
  30. }
  31. if (memcmp (buffer, "TEXV0005", 9) != 0)
  32. {
  33. wp::irrlicht::device->getLogger ()->log ("LOAD TEX: not really a tex\n", input->getFileName ().c_str (), irr::ELL_ERROR);
  34. return nullptr;
  35. }
  36. if (input->read (buffer, 9) != 9)
  37. {
  38. wp::irrlicht::device->getLogger ()->log ("LOAD TEX: cannot read second header\n", input->getFileName ().c_str (), irr::ELL_ERROR);
  39. return nullptr;
  40. }
  41. if (memcmp (buffer, "TEXI0001", 9) != 0)
  42. {
  43. wp::irrlicht::device->getLogger ()->log ("LOAD TEX: not really a tex\n", input->getFileName ().c_str (), irr::ELL_ERROR);
  44. return nullptr;
  45. }
  46. u32 width;
  47. u32 height;
  48. u32 texture_width;
  49. u32 texture_height;
  50. u32 format;
  51. u32 imageFormat = FREE_IMAGE_FORMAT::FIF_UNKNOWN;
  52. u8 containerVersion = 0;
  53. input->read (&format, 4);
  54. input->seek (4, true); // ignore bytes
  55. input->read (&texture_width, 4);
  56. input->read (&texture_height, 4);
  57. input->read (&width, 4);
  58. input->read (&height, 4);
  59. input->seek (4, true); // ignore bytes
  60. input->read (buffer, 9);
  61. if (memcmp (buffer, "TEXB0003", 9) == 0)
  62. {
  63. containerVersion = 3;
  64. input->seek (4, true);
  65. input->read (&imageFormat, 4);
  66. }
  67. else if (memcmp (buffer, "TEXB0002", 9) == 0)
  68. {
  69. containerVersion = 2;
  70. input->seek (4, true);
  71. }
  72. else
  73. {
  74. wp::irrlicht::device->getLogger ()->log ("LOAD TEX: Unknown container type\n", input->getFileName ().c_str (), irr::ELL_ERROR);
  75. return nullptr;
  76. }
  77. if (format == TextureFormat::A8)
  78. {
  79. wp::irrlicht::device->getLogger ()-> log ("LOAD TEX: A8 not supported\n", input->getFileName ().c_str (), irr::ELL_ERROR);
  80. return nullptr;
  81. }
  82. if (format == TextureFormat::RA88)
  83. {
  84. wp::irrlicht::device->getLogger ()->log ("LOAD TEX: RA88 not supported\n", input->getFileName ().c_str (), irr::ELL_ERROR);
  85. return nullptr;
  86. }
  87. u32 mipmap_count = 0;
  88. input->read (&mipmap_count, 4);
  89. u32 mipmap_width = 0;
  90. u32 mipmap_height = 0;
  91. u32 mipmap_compression = 0;
  92. u32 mipmap_uncompressed_size = 0;
  93. u32 mipmap_compressed_size = 0;
  94. input->read (&mipmap_width, 4);
  95. input->read (&mipmap_height, 4);
  96. input->read (&mipmap_compression, 4);
  97. input->read (&mipmap_uncompressed_size, 4);
  98. input->read (&mipmap_compressed_size, 4);
  99. char *decompressedBuffer = new char [mipmap_uncompressed_size];
  100. if (mipmap_compression == 1)
  101. {
  102. char *compressedBuffer = new char [mipmap_compressed_size];
  103. input->read (compressedBuffer, mipmap_compressed_size);
  104. int result = LZ4_decompress_safe (compressedBuffer, decompressedBuffer, mipmap_compressed_size, mipmap_uncompressed_size);
  105. if (!result)
  106. {
  107. delete [] decompressedBuffer;
  108. delete [] compressedBuffer;
  109. wp::irrlicht::device->getLogger ()->log ("LOAD TEX: cannot decompress texture data\n", input->getFileName ().c_str (), irr::ELL_ERROR);
  110. return nullptr;
  111. }
  112. delete [] compressedBuffer;
  113. }
  114. else
  115. {
  116. input->read (decompressedBuffer, mipmap_uncompressed_size);
  117. }
  118. if (containerVersion == 2)
  119. {
  120. image = wp::irrlicht::driver->createImage (ECF_A8R8G8B8, irr::core::dimension2d<u32> (width, height));
  121. if (!image)
  122. {
  123. delete [] decompressedBuffer;
  124. delete image;
  125. wp::irrlicht::device->getLogger ()->log ("LOAD TEX: cannot create destination image\n", input->getFileName ().c_str (), irr::ELL_ERROR);
  126. return nullptr;
  127. }
  128. switch (format)
  129. {
  130. case TextureFormat::ARGB8888:
  131. this->loadImageFromARGB8Data (image, decompressedBuffer, width, height, mipmap_width);
  132. break;
  133. case TextureFormat::DXT5:
  134. this->loadImageFromDXT5 (image, decompressedBuffer, width, height, mipmap_width, mipmap_height);
  135. break;
  136. case TextureFormat::DXT1:
  137. this->loadImageFromDXT1 (image, decompressedBuffer, width, height, mipmap_width, mipmap_height);
  138. break;
  139. case TextureFormat::DXT3:
  140. wp::irrlicht::device->getLogger ()->log ("LOAD TEX: DXT3 textures not supported yet\n", input->getFileName ().c_str (), irr::ELL_ERROR);
  141. delete [] decompressedBuffer;
  142. delete image;
  143. return nullptr;
  144. }
  145. }
  146. else
  147. {
  148. // copy the buffer to a new address
  149. char* filebuffer = new char [mipmap_uncompressed_size];
  150. char tmpname [TMP_MAX];
  151. // copy file data to the final file buffer to be used
  152. memcpy (filebuffer, decompressedBuffer, mipmap_uncompressed_size);
  153. // generate temporal name
  154. std::tmpnam (tmpname);
  155. // store it in a std::string
  156. std::string filename = tmpname;
  157. irr::io::IReadFile* file;
  158. // free image format
  159. switch (imageFormat)
  160. {
  161. case FREE_IMAGE_FORMAT::FIF_BMP:
  162. // add extension to the file
  163. filename += ".bmp";
  164. file = wp::irrlicht::device->getFileSystem ()->createMemoryReadFile (filebuffer, mipmap_uncompressed_size, filename.c_str (), true);
  165. break;
  166. case FREE_IMAGE_FORMAT::FIF_PNG:
  167. // add extension to the file
  168. filename += ".png";
  169. file = wp::irrlicht::device->getFileSystem ()->createMemoryReadFile (filebuffer, mipmap_uncompressed_size, filename.c_str (), true);
  170. break;
  171. case FREE_IMAGE_FORMAT::FIF_JPEG:
  172. // add extension to the file
  173. filename += ".jpg";
  174. file = wp::irrlicht::device->getFileSystem ()->createMemoryReadFile (filebuffer, mipmap_uncompressed_size, filename.c_str (), true);
  175. break;
  176. default:
  177. wp::irrlicht::device->getLogger ()->log ("LOAD TEX: detected unsupported free-image format\n", input->getFileName ().c_str (), irr::ELL_ERROR);
  178. delete [] decompressedBuffer;
  179. delete [] filebuffer;
  180. return nullptr;
  181. }
  182. image = wp::irrlicht::driver->createImageFromFile (file);
  183. if (!image)
  184. {
  185. file->drop ();
  186. delete [] decompressedBuffer;
  187. delete image;
  188. wp::irrlicht::device->getLogger ()->log ("LOAD TEX: cannot create destination image\n", input->getFileName ().c_str (), irr::ELL_ERROR);
  189. return nullptr;
  190. }
  191. }
  192. delete [] decompressedBuffer;
  193. return image;
  194. }
  195. void CImageLoaderTex::loadImageFromARGB8Data (IImage* output, const char* input, u32 width, u32 height, u32 mipmap_width) const
  196. {
  197. u32 bytesPerPixel = output->getBytesPerPixel ();
  198. char *imagedata = (char *) output->lock ();
  199. for (u32 y = 0; y < height; y ++)
  200. {
  201. u32 baseDestination = y * output->getPitch ();
  202. u32 baseOrigin = y * (mipmap_width * 4);
  203. for (u32 x = 0; x < width; x ++)
  204. {
  205. imagedata [baseDestination + (x * bytesPerPixel) + 2] = input [baseOrigin + ((width - x) * 4) + 0]; // r
  206. imagedata [baseDestination + (x * bytesPerPixel) + 1] = input [baseOrigin + ((width - x) * 4) + 1]; // g
  207. imagedata [baseDestination + (x * bytesPerPixel) + 0] = input [baseOrigin + ((width - x) * 4) + 2]; // b
  208. imagedata [baseDestination + (x * bytesPerPixel) + 3] = input [baseOrigin + ((width - x) * 4) + 3]; // alpha
  209. }
  210. }
  211. output->unlock ();
  212. }
  213. void CImageLoaderTex::loadImageFromDXT1 (IImage* output, const char* input, u32 destination_width, u32 destination_height, u32 origin_width, u32 origin_height) const
  214. {
  215. char* decompressedBuffer = new char [origin_width * origin_height * 4];
  216. this->BlockDecompressImageDXT1 (origin_width, origin_height, (const unsigned char*) input, (unsigned long*) decompressedBuffer);
  217. this->loadImageFromARGB8Data (output, decompressedBuffer, destination_width, destination_height, origin_width);
  218. delete [] decompressedBuffer;
  219. }
  220. void CImageLoaderTex::loadImageFromDXT5 (IImage* output, const char* input, u32 destination_width, u32 destination_height, u32 origin_width, u32 origin_height) const
  221. {
  222. char* decompressedBuffer = new char [origin_width * origin_height * 4];
  223. this->BlockDecompressImageDXT5 (origin_width, origin_height, (const unsigned char*) input, (unsigned long*) decompressedBuffer);
  224. this->loadImageFromARGB8Data (output, decompressedBuffer, destination_width, destination_height, origin_width);
  225. delete [] decompressedBuffer;
  226. }
  227. // ------------------------------------------------------------------------------------
  228. // The following code is a slightly modified version of this repository
  229. // https://github.com/Benjamin-Dobell/s3tc-dxt-decompression
  230. // ------------------------------------------------------------------------------------
  231. // unsigned long PackRGBA(): Helper method that packs RGBA channels into a single 4 byte pixel.
  232. //
  233. // unsigned char r: red channel.
  234. // unsigned char g: green channel.
  235. // unsigned char b: blue channel.
  236. // unsigned char a: alpha channel.
  237. unsigned long CImageLoaderTex::PackRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a) const
  238. {
  239. return ((r << 24) | (g << 16) | (b << 8) | a);
  240. }
  241. // void DecompressBlockDXT1(): Decompresses one block of a DXT1 texture and stores the resulting pixels at the appropriate offset in 'image'.
  242. //
  243. // unsigned long x: x-coordinate of the first pixel in the block.
  244. // unsigned long y: y-coordinate of the first pixel in the block.
  245. // unsigned long width: width of the texture being decompressed.
  246. // unsigned long height: height of the texture being decompressed.
  247. // const unsigned char *blockStorage: pointer to the block to decompress.
  248. // unsigned long *image: pointer to image where the decompressed pixel data should be stored.
  249. void CImageLoaderTex::DecompressBlockDXT1(unsigned long x, unsigned long y, unsigned long width, const unsigned char *blockStorage, unsigned long *image) const
  250. {
  251. unsigned short color0 = *reinterpret_cast<const unsigned short *>(blockStorage);
  252. unsigned short color1 = *reinterpret_cast<const unsigned short *>(blockStorage + 2);
  253. unsigned long temp;
  254. temp = (color0 >> 11) * 255 + 16;
  255. unsigned char r0 = (unsigned char)((temp/32 + temp)/32);
  256. temp = ((color0 & 0x07E0) >> 5) * 255 + 32;
  257. unsigned char g0 = (unsigned char)((temp/64 + temp)/64);
  258. temp = (color0 & 0x001F) * 255 + 16;
  259. unsigned char b0 = (unsigned char)((temp/32 + temp)/32);
  260. temp = (color1 >> 11) * 255 + 16;
  261. unsigned char r1 = (unsigned char)((temp/32 + temp)/32);
  262. temp = ((color1 & 0x07E0) >> 5) * 255 + 32;
  263. unsigned char g1 = (unsigned char)((temp/64 + temp)/64);
  264. temp = (color1 & 0x001F) * 255 + 16;
  265. unsigned char b1 = (unsigned char)((temp/32 + temp)/32);
  266. unsigned long code = *reinterpret_cast<const unsigned long *>(blockStorage + 4);
  267. for (int j=0; j < 4; j++)
  268. {
  269. for (int i=0; i < 4; i++)
  270. {
  271. unsigned long finalColor = 0;
  272. unsigned char positionCode = (code >> 2*(4*j+i)) & 0x03;
  273. if (color0 > color1)
  274. {
  275. switch (positionCode)
  276. {
  277. case 0:
  278. finalColor = PackRGBA(r0, g0, b0, 255);
  279. break;
  280. case 1:
  281. finalColor = PackRGBA(r1, g1, b1, 255);
  282. break;
  283. case 2:
  284. finalColor = PackRGBA((2*r0+r1)/3, (2*g0+g1)/3, (2*b0+b1)/3, 255);
  285. break;
  286. case 3:
  287. finalColor = PackRGBA((r0+2*r1)/3, (g0+2*g1)/3, (b0+2*b1)/3, 255);
  288. break;
  289. }
  290. }
  291. else
  292. {
  293. switch (positionCode)
  294. {
  295. case 0:
  296. finalColor = PackRGBA(r0, g0, b0, 255);
  297. break;
  298. case 1:
  299. finalColor = PackRGBA(r1, g1, b1, 255);
  300. break;
  301. case 2:
  302. finalColor = PackRGBA((r0+r1)/2, (g0+g1)/2, (b0+b1)/2, 255);
  303. break;
  304. case 3:
  305. finalColor = PackRGBA(0, 0, 0, 255);
  306. break;
  307. }
  308. }
  309. if (x + i < width)
  310. image[(y + j)*width + (x + i)] = finalColor;
  311. }
  312. }
  313. }
  314. // void BlockDecompressImageDXT1(): Decompresses all the blocks of a DXT1 compressed texture and stores the resulting pixels in 'image'.
  315. //
  316. // unsigned long width: Texture width.
  317. // unsigned long height: Texture height.
  318. // const unsigned char *blockStorage: pointer to compressed DXT1 blocks.
  319. // unsigned long *image: pointer to the image where the decompressed pixels will be stored.
  320. void CImageLoaderTex::BlockDecompressImageDXT1(unsigned long width, unsigned long height, const unsigned char *blockStorage, unsigned long *image) const
  321. {
  322. unsigned long blockCountX = (width + 3) / 4;
  323. unsigned long blockCountY = (height + 3) / 4;
  324. unsigned long blockWidth = (width < 4) ? width : 4;
  325. unsigned long blockHeight = (height < 4) ? height : 4;
  326. for (unsigned long j = 0; j < blockCountY; j++)
  327. {
  328. for (unsigned long i = 0; i < blockCountX; i++) DecompressBlockDXT1(i*4, j*4, width, blockStorage + i * 8, image);
  329. blockStorage += blockCountX * 8;
  330. }
  331. }
  332. // void DecompressBlockDXT5(): Decompresses one block of a DXT5 texture and stores the resulting pixels at the appropriate offset in 'image'.
  333. //
  334. // unsigned long x: x-coordinate of the first pixel in the block.
  335. // unsigned long y: y-coordinate of the first pixel in the block.
  336. // unsigned long width: width of the texture being decompressed.
  337. // unsigned long height: height of the texture being decompressed.
  338. // const unsigned char *blockStorage: pointer to the block to decompress.
  339. // unsigned long *image: pointer to image where the decompressed pixel data should be stored.
  340. void CImageLoaderTex::DecompressBlockDXT5(unsigned long x, unsigned long y, unsigned long width, const unsigned char *blockStorage, unsigned long *image) const
  341. {
  342. unsigned char alpha0 = *reinterpret_cast<const unsigned char *>(blockStorage);
  343. unsigned char alpha1 = *reinterpret_cast<const unsigned char *>(blockStorage + 1);
  344. const unsigned char *bits = blockStorage + 2;
  345. unsigned long alphaCode1 = bits[2] | (bits[3] << 8) | (bits[4] << 16) | (bits[5] << 24);
  346. unsigned short alphaCode2 = bits[0] | (bits[1] << 8);
  347. unsigned short color0 = *reinterpret_cast<const unsigned short *>(blockStorage + 8);
  348. unsigned short color1 = *reinterpret_cast<const unsigned short *>(blockStorage + 10);
  349. unsigned long temp;
  350. temp = (color0 >> 11) * 255 + 16;
  351. unsigned char r0 = (unsigned char)((temp/32 + temp)/32);
  352. temp = ((color0 & 0x07E0) >> 5) * 255 + 32;
  353. unsigned char g0 = (unsigned char)((temp/64 + temp)/64);
  354. temp = (color0 & 0x001F) * 255 + 16;
  355. unsigned char b0 = (unsigned char)((temp/32 + temp)/32);
  356. temp = (color1 >> 11) * 255 + 16;
  357. unsigned char r1 = (unsigned char)((temp/32 + temp)/32);
  358. temp = ((color1 & 0x07E0) >> 5) * 255 + 32;
  359. unsigned char g1 = (unsigned char)((temp/64 + temp)/64);
  360. temp = (color1 & 0x001F) * 255 + 16;
  361. unsigned char b1 = (unsigned char)((temp/32 + temp)/32);
  362. unsigned long code = *reinterpret_cast<const unsigned long *>(blockStorage + 12);
  363. for (int j=0; j < 4; j++)
  364. {
  365. for (int i=0; i < 4; i++)
  366. {
  367. int alphaCodeIndex = 3*(4*j+i);
  368. int alphaCode;
  369. if (alphaCodeIndex <= 12)
  370. {
  371. alphaCode = (alphaCode2 >> alphaCodeIndex) & 0x07;
  372. }
  373. else if (alphaCodeIndex == 15)
  374. {
  375. alphaCode = (alphaCode2 >> 15) | ((alphaCode1 << 1) & 0x06);
  376. }
  377. else // alphaCodeIndex >= 18 && alphaCodeIndex <= 45
  378. {
  379. alphaCode = (alphaCode1 >> (alphaCodeIndex - 16)) & 0x07;
  380. }
  381. unsigned char finalAlpha;
  382. if (alphaCode == 0)
  383. {
  384. finalAlpha = alpha0;
  385. }
  386. else if (alphaCode == 1)
  387. {
  388. finalAlpha = alpha1;
  389. }
  390. else
  391. {
  392. if (alpha0 > alpha1)
  393. {
  394. finalAlpha = ((8-alphaCode)*alpha0 + (alphaCode-1)*alpha1)/7;
  395. }
  396. else
  397. {
  398. if (alphaCode == 6)
  399. finalAlpha = 0;
  400. else if (alphaCode == 7)
  401. finalAlpha = 255;
  402. else
  403. finalAlpha = ((6-alphaCode)*alpha0 + (alphaCode-1)*alpha1)/5;
  404. }
  405. }
  406. unsigned char colorCode = (code >> 2*(4*j+i)) & 0x03;
  407. unsigned long finalColor;
  408. switch (colorCode)
  409. {
  410. case 0:
  411. finalColor = PackRGBA(r0, g0, b0, finalAlpha);
  412. break;
  413. case 1:
  414. finalColor = PackRGBA(r1, g1, b1, finalAlpha);
  415. break;
  416. case 2:
  417. finalColor = PackRGBA((2*r0+r1)/3, (2*g0+g1)/3, (2*b0+b1)/3, finalAlpha);
  418. break;
  419. case 3:
  420. finalColor = PackRGBA((r0+2*r1)/3, (g0+2*g1)/3, (b0+2*b1)/3, finalAlpha);
  421. break;
  422. }
  423. if (x + i < width)
  424. image[(y + j)*width + (x + i)] = finalColor;
  425. }
  426. }
  427. }
  428. // void BlockDecompressImageDXT5(): Decompresses all the blocks of a DXT5 compressed texture and stores the resulting pixels in 'image'.
  429. //
  430. // unsigned long width: Texture width.
  431. // unsigned long height: Texture height.
  432. // const unsigned char *blockStorage: pointer to compressed DXT5 blocks.
  433. // unsigned long *image: pointer to the image where the decompressed pixels will be stored.
  434. void CImageLoaderTex::BlockDecompressImageDXT5(unsigned long width, unsigned long height, const unsigned char *blockStorage, unsigned long *image) const
  435. {
  436. unsigned long blockCountX = (width + 3) / 4;
  437. unsigned long blockCountY = (height + 3) / 4;
  438. unsigned long blockWidth = (width < 4) ? width : 4;
  439. unsigned long blockHeight = (height < 4) ? height : 4;
  440. for (unsigned long j = 0; j < blockCountY; j++)
  441. {
  442. for (unsigned long i = 0; i < blockCountX; i++) DecompressBlockDXT5(i*4, j*4, width, blockStorage + i * 16, image);
  443. blockStorage += blockCountX * 16;
  444. }
  445. }
  446. }// end namespace irr
  447. }//end namespace video