CTexture.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. #include "CTexture.h"
  2. #include <string>
  3. #include <cstring>
  4. #include <lz4.h>
  5. using namespace WallpaperEngine::Assets;
  6. CTexture::CTexture (const void* fileData)
  7. {
  8. // ensure the header is parsed
  9. this->m_header = this->parseHeader (static_cast <const char*> (fileData));
  10. GLint internalFormat;
  11. if (this->isAnimated () == true)
  12. {
  13. this->m_resolution = {
  14. this->m_header->textureWidth, this->m_header->textureHeight,
  15. this->m_header->gifWidth, this->m_header->gifHeight
  16. };
  17. }
  18. else
  19. {
  20. if (this->m_header->freeImageFormat != FREE_IMAGE_FORMAT::FIF_UNKNOWN)
  21. {
  22. // wpengine-texture format always has one mipmap
  23. // get first image size
  24. std::vector<TextureMipmap*>::const_iterator element = this->m_header->images.find (0)->second.begin ();
  25. // set the texture resolution
  26. this->m_resolution = {
  27. (*element)->width, (*element)->height,
  28. this->m_header->width, this->m_header->height
  29. };
  30. }
  31. else
  32. {
  33. // set the texture resolution
  34. this->m_resolution = {
  35. this->m_header->textureWidth, this->m_header->textureHeight,
  36. this->m_header->width, this->m_header->height
  37. };
  38. }
  39. }
  40. if (this->m_header->freeImageFormat != FREE_IMAGE_FORMAT::FIF_UNKNOWN)
  41. {
  42. internalFormat = GL_RGBA8;
  43. // set some extra information too as it's used for image sizing
  44. // this ensures that a_TexCoord uses the full image instead of just part of it
  45. // TODO: MAYBE IT'S BETTER TO CREATE A TEXTURE OF THE GIVEN SIZE AND COPY OVER WHAT WE READ FROM THE FILE?
  46. /*this->m_header->width = this->m_header->mipmaps [0]->width;
  47. this->m_header->height = this->m_header->mipmaps [0]->height;
  48. this->m_header->textureWidth = this->m_header->mipmaps [0]->width;
  49. this->m_header->textureHeight = this->m_header->mipmaps [0]->height;*/
  50. }
  51. else
  52. {
  53. // detect the image format and hand it to openGL to be used
  54. switch (this->m_header->format)
  55. {
  56. case TextureFormat::DXT5:
  57. internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
  58. break;
  59. case TextureFormat::DXT3:
  60. internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
  61. break;
  62. case TextureFormat::DXT1:
  63. internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
  64. break;
  65. case TextureFormat::ARGB8888:
  66. internalFormat = GL_RGBA8;
  67. break;
  68. case TextureFormat::R8:
  69. internalFormat = GL_R8;
  70. break;
  71. case TextureFormat::RG88:
  72. internalFormat = GL_RG8;
  73. break;
  74. default:
  75. delete this->m_header;
  76. throw std::runtime_error ("Cannot determine the texture format");
  77. }
  78. }
  79. // allocate texture ids list
  80. this->m_textureID = new GLuint [this->m_header->imageCount];
  81. // ask opengl for the correct amount of textures
  82. glGenTextures (this->m_header->imageCount, this->m_textureID);
  83. auto imgCur = this->m_header->images.begin ();
  84. auto imgEnd = this->m_header->images.end ();
  85. for (int index = 0; imgCur != imgEnd; imgCur ++, index ++)
  86. {
  87. // bind the texture to assign information to it
  88. glBindTexture (GL_TEXTURE_2D, this->m_textureID [index]);
  89. // set mipmap levels
  90. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
  91. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, this->m_header->mipmapCount - 1);
  92. // setup texture wrapping and filtering
  93. if (this->m_header->flags & TextureFlags::ClampUVs)
  94. {
  95. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  96. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  97. }
  98. else
  99. {
  100. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  101. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  102. }
  103. if (this->m_header->flags & TextureFlags::NoInterpolation)
  104. {
  105. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  106. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
  107. }
  108. else
  109. {
  110. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  111. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
  112. }
  113. glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY, 8.0f);
  114. auto cur = (*imgCur).second.begin ();
  115. auto end = (*imgCur).second.end ();
  116. for (int32_t level = 0; cur != end; cur ++, level ++)
  117. {
  118. FIBITMAP* bitmap = nullptr;
  119. FIBITMAP* converted = nullptr;
  120. FIMEMORY* memory = nullptr;
  121. void* dataptr = (*cur)->uncompressedData;
  122. uint32_t width = (*cur)->width;
  123. uint32_t height = (*cur)->height;
  124. uint32_t bufferSize = (*cur)->uncompressedSize;
  125. GLenum textureFormat = GL_RGBA;
  126. if (this->m_header->freeImageFormat != FREE_IMAGE_FORMAT::FIF_UNKNOWN)
  127. {
  128. memory = FreeImage_OpenMemory (reinterpret_cast <BYTE *> ((*cur)->uncompressedData), (*cur)->uncompressedSize);
  129. // load the image and setup pointers so they can be used
  130. bitmap = FreeImage_LoadFromMemory (this->m_header->freeImageFormat, memory);
  131. // flip the image vertically
  132. FreeImage_FlipVertical (bitmap);
  133. // convert to a 32bits bytearray
  134. converted = FreeImage_ConvertTo32Bits (bitmap);
  135. dataptr = FreeImage_GetBits (converted);
  136. width = FreeImage_GetWidth (converted);
  137. height = FreeImage_GetHeight (converted);
  138. bufferSize = FreeImage_GetMemorySize (converted);
  139. textureFormat = GL_BGRA;
  140. }
  141. else
  142. {
  143. if (this->m_header->format == TextureFormat::RG88)
  144. textureFormat = GL_RG;
  145. else if (this->m_header->format == TextureFormat::R8)
  146. {
  147. // red textures are 1-byte-per-pixel, so it's alignment has to be set manually
  148. glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
  149. textureFormat = GL_RED;
  150. }
  151. }
  152. switch (internalFormat)
  153. {
  154. case GL_RGBA8:
  155. case GL_RG8:
  156. case GL_R8:
  157. glTexImage2D (
  158. GL_TEXTURE_2D, level, internalFormat,
  159. width, height, 0,
  160. textureFormat, GL_UNSIGNED_BYTE,
  161. dataptr
  162. );
  163. break;
  164. case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
  165. case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
  166. case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
  167. glCompressedTexImage2D (
  168. GL_TEXTURE_2D, level, internalFormat,
  169. width, height, 0,
  170. bufferSize, dataptr
  171. );
  172. break;
  173. default:
  174. throw std::runtime_error ("Cannot load texture, unknown format");
  175. }
  176. // freeimage buffer won't be used anymore, so free memory
  177. if (this->m_header->freeImageFormat != FREE_IMAGE_FORMAT::FIF_UNKNOWN)
  178. {
  179. FreeImage_Unload (bitmap);
  180. FreeImage_Unload (converted);
  181. FreeImage_CloseMemory (memory);
  182. }
  183. }
  184. }
  185. }
  186. CTexture::~CTexture ()
  187. {
  188. if (this->getHeader () == nullptr)
  189. return;
  190. // free the header if it was allocated
  191. delete this->getHeader ();
  192. }
  193. const GLuint CTexture::getTextureID (uint32_t imageIndex) const
  194. {
  195. // ensure we do not go out of bounds
  196. if (imageIndex > this->m_header->imageCount)
  197. return this->m_textureID [0];
  198. return this->m_textureID [imageIndex];
  199. }
  200. const uint32_t CTexture::getTextureWidth (uint32_t imageIndex) const
  201. {
  202. if (imageIndex > this->m_header->imageCount)
  203. return this->getHeader ()->textureWidth;
  204. return (*this->m_header->images [imageIndex].begin ())->width;
  205. }
  206. const uint32_t CTexture::getTextureHeight (uint32_t imageIndex) const
  207. {
  208. if (imageIndex > this->m_header->imageCount)
  209. return this->getHeader ()->textureHeight;
  210. return (*this->m_header->images [imageIndex].begin ())->height;
  211. }
  212. const uint32_t CTexture::getRealWidth () const
  213. {
  214. return this->isAnimated () == true ? this->getHeader ()->gifWidth : this->getHeader ()->width;
  215. }
  216. const uint32_t CTexture::getRealHeight () const
  217. {
  218. return this->isAnimated () == true ? this->getHeader ()->gifHeight : this->getHeader ()->height;
  219. }
  220. const ITexture::TextureFormat CTexture::getFormat () const
  221. {
  222. return this->getHeader ()->format;
  223. }
  224. const ITexture::TextureFlags CTexture::getFlags () const
  225. {
  226. return this->getHeader ()->flags;
  227. }
  228. const CTexture::TextureHeader* CTexture::getHeader () const
  229. {
  230. return this->m_header;
  231. }
  232. const glm::vec4* CTexture::getResolution () const
  233. {
  234. return &this->m_resolution;
  235. }
  236. const std::vector<ITexture::TextureFrame*>& CTexture::getFrames () const
  237. {
  238. return this->getHeader ()->frames;
  239. }
  240. const bool CTexture::isAnimated () const
  241. {
  242. return this->getHeader ()->isAnimated ();
  243. }
  244. CTexture::TextureMipmap::TextureMipmap ()
  245. {
  246. }
  247. CTexture::TextureMipmap::~TextureMipmap ()
  248. {
  249. if (this->compression == 1)
  250. delete this->compressedData;
  251. delete this->uncompressedData;
  252. }
  253. void CTexture::TextureMipmap::decompressData ()
  254. {
  255. if (this->compression == 1)
  256. {
  257. this->uncompressedData = new char [this->uncompressedSize];
  258. int result = LZ4_decompress_safe (
  259. this->compressedData, this->uncompressedData,
  260. this->compressedSize, this->uncompressedSize
  261. );
  262. if (!result)
  263. throw std::runtime_error ("Cannot decompress texture data");
  264. }
  265. }
  266. CTexture::TextureFrame::TextureFrame ()
  267. {
  268. }
  269. CTexture::TextureFrame::~TextureFrame ()
  270. {
  271. }
  272. CTexture::TextureHeader::TextureHeader ()
  273. {
  274. }
  275. CTexture::TextureHeader::~TextureHeader ()
  276. {
  277. auto imgCur = this->images.begin ();
  278. auto imgEnd = this->images.end ();
  279. for (; imgCur != imgEnd; imgCur ++)
  280. {
  281. auto cur = (*imgCur).second.begin ();
  282. auto end = (*imgCur).second.end ();
  283. for (; cur != end; cur ++)
  284. delete *cur;
  285. }
  286. }
  287. CTexture::TextureHeader* CTexture::parseHeader (const char* fileData)
  288. {
  289. // check the magic value on the header first
  290. if (memcmp (fileData, "TEXV0005", 9) != 0)
  291. throw std::runtime_error ("unexpected texture container type");
  292. // jump to the next value
  293. fileData += 9;
  294. // check the sub-magic value on the header
  295. if (memcmp (fileData, "TEXI0001", 9) != 0)
  296. throw std::runtime_error ("unexpected texture sub-container type");
  297. // jump through the string again
  298. fileData += 9;
  299. TextureHeader* header = new TextureHeader;
  300. const uint32_t* pointer = reinterpret_cast <const uint32_t*> (fileData);
  301. header->format = static_cast <TextureFormat>(*pointer ++);
  302. header->flags = static_cast <TextureFlags> (*pointer ++);
  303. header->textureWidth = *pointer ++;
  304. header->textureHeight = *pointer ++;
  305. header->width = *pointer ++;
  306. header->height = *pointer ++;
  307. pointer ++; // ignore some more bytes
  308. // now we're going to parse some more data that is string
  309. // so get the current position back as string
  310. fileData = reinterpret_cast <const char*> (pointer);
  311. // get the position of what comes after the texture data
  312. pointer = reinterpret_cast <const uint32_t*> (fileData + 9);
  313. if (memcmp (fileData, "TEXB0003", 9) == 0)
  314. {
  315. header->containerVersion = ContainerVersion::TEXB0003;
  316. header->imageCount = *pointer ++;
  317. header->freeImageFormat = static_cast <FREE_IMAGE_FORMAT> (*pointer++);
  318. }
  319. else if(memcmp (fileData, "TEXB0002", 9) == 0)
  320. {
  321. header->containerVersion = ContainerVersion::TEXB0002;
  322. header->imageCount = *pointer ++;
  323. }
  324. else if (memcmp (fileData, "TEXB0001", 9) == 0)
  325. {
  326. header->containerVersion = ContainerVersion::TEXB0001;
  327. header->imageCount = *pointer ++;
  328. }
  329. else
  330. {
  331. delete header;
  332. throw std::runtime_error ("unknown texture format type");
  333. }
  334. for (uint32_t image = 0; image < header->imageCount; image ++)
  335. {
  336. // read the number of mipmaps available for this image
  337. header->mipmapCount = *pointer ++;
  338. std::vector <TextureMipmap*> mipmaps;
  339. fileData = reinterpret_cast <const char*> (pointer);
  340. for (uint32_t i = 0; i < header->mipmapCount; i ++)
  341. mipmaps.emplace_back (parseMipmap (header, &fileData));
  342. // add the pixmaps back
  343. header->images.insert (std::pair <uint32_t, std::vector <TextureMipmap*>> (image, mipmaps));
  344. pointer = reinterpret_cast <const uint32_t*> (fileData);
  345. }
  346. // gifs have extra information after the mipmaps
  347. if (header->isAnimated () == true)
  348. {
  349. if (memcmp (fileData, "TEXS0002", 9) == 0)
  350. {
  351. header->animatedVersion = AnimatedVersion::TEXS0002;
  352. }
  353. else if (memcmp (fileData, "TEXS0003", 9) == 0)
  354. {
  355. header->animatedVersion = AnimatedVersion::TEXS0003;
  356. }
  357. else
  358. {
  359. throw std::runtime_error ("found animation information of unknown type");
  360. }
  361. // get an integer pointer back to read the frame count
  362. pointer = reinterpret_cast <const uint32_t*> (fileData + 9);
  363. uint32_t framecount = *pointer++;
  364. if (header->animatedVersion == AnimatedVersion::TEXS0003)
  365. {
  366. // ignore two extra integers as those are width and height of the git
  367. header->gifWidth = *pointer ++;
  368. header->gifHeight = *pointer ++;
  369. }
  370. // get back the pointer into filedata
  371. fileData = reinterpret_cast <const char*> (pointer);
  372. while (framecount > 0)
  373. {
  374. // add the frame to the list
  375. header->frames.push_back (parseAnimation (header, &fileData));
  376. framecount --;
  377. }
  378. // ensure gif width and height is right for TEXS0002
  379. if (header->animatedVersion == AnimatedVersion::TEXS0002)
  380. {
  381. TextureFrame* first = *header->frames.begin ();
  382. header->gifWidth = first->width1;
  383. header->gifHeight = first->height1;
  384. }
  385. }
  386. return header;
  387. }
  388. CTexture::TextureFrame* CTexture::parseAnimation (TextureHeader* header, const char** originalFileData)
  389. {
  390. const char* fileData = *originalFileData;
  391. // get back the pointer into integer
  392. const uint32_t* pointer = reinterpret_cast <const uint32_t*> (fileData);
  393. // start reading frame information
  394. TextureFrame* frame = new TextureFrame();
  395. frame->frameNumber = *pointer++;
  396. // reinterpret the pointer into float
  397. const float* fPointer = reinterpret_cast <const float*> (pointer);
  398. frame->frametime = *fPointer ++;
  399. frame->x = *fPointer ++;
  400. frame->y = *fPointer ++;
  401. frame->width1 = *fPointer ++;
  402. frame->width2 = *fPointer ++;
  403. frame->height2 = *fPointer ++;
  404. frame->height1 = *fPointer ++;
  405. // get back the pointer into fileData so it can be reused later
  406. *originalFileData = reinterpret_cast <const char*> (fPointer);
  407. return frame;
  408. }
  409. CTexture::TextureMipmap* CTexture::parseMipmap (TextureHeader* header, const char** originalFileData)
  410. {
  411. TextureMipmap* mipmap = new TextureMipmap ();
  412. // get the current position
  413. const char* fileData = *originalFileData;
  414. // get an integer pointer
  415. const uint32_t* pointer = reinterpret_cast <const uint32_t*> (fileData);
  416. mipmap->width = *pointer++;
  417. mipmap->height = *pointer++;
  418. if (header->containerVersion == ContainerVersion::TEXB0002 ||
  419. header->containerVersion == ContainerVersion::TEXB0003)
  420. {
  421. mipmap->compression = *pointer++;
  422. mipmap->uncompressedSize = *pointer++;
  423. }
  424. mipmap->compressedSize = *pointer++;
  425. // get back a normal char pointer
  426. fileData = reinterpret_cast <const char*> (pointer);
  427. if (mipmap->compression == 0)
  428. {
  429. // this might be better named as mipmap_bytes_size instead of compressedSize
  430. // as in uncompressed files this variable actually holds the file length
  431. mipmap->uncompressedSize = mipmap->compressedSize;
  432. }
  433. mipmap->uncompressedData = new char [mipmap->uncompressedSize];
  434. if (mipmap->compression == 1)
  435. {
  436. mipmap->compressedData = new char [mipmap->compressedSize];
  437. memcpy (mipmap->compressedData, fileData, mipmap->compressedSize);
  438. mipmap->decompressData ();
  439. // advance to the end of the mipmap
  440. fileData += mipmap->compressedSize;
  441. }
  442. else
  443. {
  444. memcpy (mipmap->uncompressedData, fileData, mipmap->uncompressedSize);
  445. // advance to the end of the mipmap
  446. fileData += mipmap->uncompressedSize;
  447. }
  448. // ensure the pointer is updated with the latest position when reading the data
  449. *originalFileData = fileData;
  450. return mipmap;
  451. }
  452. const bool CTexture::TextureHeader::isAnimated () const
  453. {
  454. return this->flags & TextureFlags::IsGif;
  455. }