CTexture.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. #include "CTexture.h"
  2. #include <string>
  3. #include <cstring>
  4. #include <lz4.h>
  5. using namespace WallpaperEngine::Assets;
  6. CTexture::CTexture (void* fileData)
  7. {
  8. // ensure the header is parsed
  9. this->parseHeader (static_cast <char*> (fileData));
  10. GLint internalFormat;
  11. if (this->m_header->freeImageFormat != FREE_IMAGE_FORMAT::FIF_UNKNOWN)
  12. {
  13. internalFormat = GL_RGBA8;
  14. // set some extra information too as it's used for image sizing
  15. // this ensures that a_TexCoord uses the full image instead of just part of it
  16. this->m_header->width = this->m_header->mipmaps [0]->width;
  17. this->m_header->height = this->m_header->mipmaps [0]->height;
  18. this->m_header->textureWidth = this->m_header->mipmaps [0]->width;
  19. this->m_header->textureHeight = this->m_header->mipmaps [0]->height;
  20. // TODO: MAYBE IT'S BETTER TO CREATE A TEXTURE OF THE GIVEN SIZE AND COPY OVER WHAT WE READ FROM THE FILE?
  21. }
  22. else
  23. {
  24. // detect the image format and hand it to openGL to be used
  25. switch (this->m_header->format)
  26. {
  27. case TextureFormat::DXT5:
  28. internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
  29. break;
  30. case TextureFormat::DXT3:
  31. internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
  32. break;
  33. case TextureFormat::DXT1:
  34. internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
  35. break;
  36. case TextureFormat::ARGB8888:
  37. internalFormat = GL_RGBA8;
  38. break;
  39. case TextureFormat::R8:
  40. internalFormat = GL_R8;
  41. break;
  42. case TextureFormat::RG88:
  43. internalFormat = GL_RG8;
  44. break;
  45. default:
  46. delete this->m_header;
  47. throw std::runtime_error ("Cannot determine the texture format");
  48. }
  49. }
  50. // set the texture resolution
  51. // TODO: SUPPORT SPRITES
  52. this->m_resolution = {
  53. this->m_header->textureWidth, this->m_header->textureHeight,
  54. this->m_header->width, this->m_header->height
  55. };
  56. // reserve a texture
  57. glGenTextures (1, &this->m_textureID);
  58. // bind the texture to assign information to it
  59. glBindTexture (GL_TEXTURE_2D, this->m_textureID);
  60. // set mipmap levels
  61. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
  62. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, this->m_header->mipmapCount - 1);
  63. // TODO: ADD SUPPORT FOR .tex-json FILES AS THEY ALSO HAVE FLAGS LIKE THESE ONES
  64. // setup texture wrapping and filtering
  65. if (this->m_header->flags & TextureFlags::ClampUVs)
  66. {
  67. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  68. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  69. }
  70. else
  71. {
  72. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  73. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  74. }
  75. if (this->m_header->flags & TextureFlags::NoInterpolation)
  76. {
  77. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  78. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  79. }
  80. else
  81. {
  82. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  83. glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  84. }
  85. // TODO: USE THIS ONE
  86. // uint32_t blockSize = (internalFormat == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) ? 8 : 16;
  87. auto cur = this->m_header->mipmaps.begin ();
  88. auto end = this->m_header->mipmaps.end ();
  89. for (int32_t level = 0; cur != end; cur ++, level ++)
  90. {
  91. FIBITMAP* bitmap = nullptr;
  92. FIBITMAP* converted = nullptr;
  93. FIMEMORY* memory = nullptr;
  94. void* dataptr = (*cur)->uncompressedData;
  95. uint32_t width = (*cur)->width;
  96. uint32_t height = (*cur)->height;
  97. GLenum textureFormat = GL_RGBA;
  98. if (this->m_header->freeImageFormat != FREE_IMAGE_FORMAT::FIF_UNKNOWN)
  99. {
  100. memory = FreeImage_OpenMemory (reinterpret_cast <BYTE *> ((*cur)->uncompressedData), (*cur)->uncompressedSize);
  101. // load the image and setup pointers so they can be used
  102. bitmap = FreeImage_LoadFromMemory (this->m_header->freeImageFormat, memory);
  103. // flip the image vertically
  104. FreeImage_FlipVertical (bitmap);
  105. // convert to a 32bits bytearray
  106. converted = FreeImage_ConvertTo32Bits (bitmap);
  107. dataptr = FreeImage_GetBits (converted);
  108. width = FreeImage_GetWidth (converted);
  109. height = FreeImage_GetHeight (converted);
  110. textureFormat = GL_BGRA;
  111. }
  112. else
  113. {
  114. if (this->m_header->format == TextureFormat::RG88)
  115. textureFormat = GL_RG;
  116. else if (this->m_header->format == TextureFormat::R8)
  117. textureFormat = GL_R;
  118. }
  119. switch (internalFormat)
  120. {
  121. case GL_RGBA8:
  122. glTexImage2D (GL_TEXTURE_2D, level, internalFormat,
  123. width, height, 0,
  124. textureFormat, GL_UNSIGNED_BYTE,
  125. dataptr
  126. );
  127. break;
  128. case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
  129. case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
  130. case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
  131. glCompressedTexImage2D (
  132. GL_TEXTURE_2D, level, internalFormat,
  133. (*cur)->width, (*cur)->height, 0,
  134. (*cur)->uncompressedSize, dataptr
  135. );
  136. break;
  137. }
  138. // freeimage buffer won't be used anymore, so free memory
  139. if (this->m_header->freeImageFormat != FREE_IMAGE_FORMAT::FIF_UNKNOWN)
  140. {
  141. FreeImage_Unload (bitmap);
  142. FreeImage_Unload (converted);
  143. FreeImage_CloseMemory (memory);
  144. }
  145. }
  146. }
  147. CTexture::~CTexture ()
  148. {
  149. if (this->getHeader () == nullptr)
  150. return;
  151. // free the header if it was allocated
  152. delete this->getHeader ();
  153. }
  154. const GLuint CTexture::getTextureID () const
  155. {
  156. return this->m_textureID;
  157. }
  158. const uint32_t CTexture::getTextureWidth () const
  159. {
  160. return this->getHeader ()->textureWidth;
  161. }
  162. const uint32_t CTexture::getTextureHeight () const
  163. {
  164. return this->getHeader ()->textureHeight;
  165. }
  166. const uint32_t CTexture::getRealWidth () const
  167. {
  168. return this->getHeader ()->width;
  169. }
  170. const uint32_t CTexture::getRealHeight () const
  171. {
  172. return this->getHeader ()->height;
  173. }
  174. const ITexture::TextureFormat CTexture::getFormat () const
  175. {
  176. return this->getHeader ()->format;
  177. }
  178. const CTexture::TextureHeader* CTexture::getHeader () const
  179. {
  180. return this->m_header;
  181. }
  182. const glm::vec4* CTexture::getResolution () const
  183. {
  184. return &this->m_resolution;
  185. }
  186. CTexture::TextureMipmap::TextureMipmap ()
  187. {
  188. }
  189. CTexture::TextureMipmap::~TextureMipmap ()
  190. {
  191. if (this->compression == 1)
  192. delete this->compressedData;
  193. delete this->uncompressedData;
  194. }
  195. void CTexture::TextureMipmap::decompressData ()
  196. {
  197. if (this->compression == 1)
  198. {
  199. this->uncompressedData = new char [this->uncompressedSize];
  200. int result = LZ4_decompress_safe (
  201. this->compressedData, this->uncompressedData,
  202. this->compressedSize, this->uncompressedSize
  203. );
  204. if (!result)
  205. throw std::runtime_error ("Cannot decompress texture data");
  206. }
  207. }
  208. CTexture::TextureHeader::TextureHeader ()
  209. {
  210. }
  211. CTexture::TextureHeader::~TextureHeader ()
  212. {
  213. auto cur = this->mipmaps.begin ();
  214. auto end = this->mipmaps.end ();
  215. for (; cur != end; cur ++)
  216. delete *cur;
  217. }
  218. void CTexture::parseHeader (char* fileData)
  219. {
  220. // check the magic value on the header first
  221. if (memcmp (fileData, "TEXV0005", 9) != 0)
  222. throw std::runtime_error ("unexpected texture container type");
  223. // jump to the next value
  224. fileData += 9;
  225. // check the sub-magic value on the header
  226. if (memcmp (fileData, "TEXI0001", 9) != 0)
  227. throw std::runtime_error ("unexpected texture sub-container type");
  228. // jump through the string again
  229. fileData += 9;
  230. this->m_header = new TextureHeader;
  231. uint32_t* pointer = reinterpret_cast <uint32_t*> (fileData);
  232. this->m_header->format = static_cast <TextureFormat>(*pointer ++);
  233. this->m_header->flags = static_cast <TextureFlags> (*pointer ++);
  234. this->m_header->textureWidth = *pointer ++;
  235. this->m_header->textureHeight = *pointer ++;
  236. this->m_header->width = *pointer ++;
  237. this->m_header->height = *pointer ++;
  238. pointer ++; // ignore some more bytes
  239. // now we're going to parse some more data that is string
  240. // so get the current position back as string
  241. fileData = reinterpret_cast <char*> (pointer);
  242. // get the position of what comes after the texture data
  243. pointer = reinterpret_cast <uint32_t*> (fileData + 9);
  244. if (memcmp (fileData, "TEXB0003", 9) == 0)
  245. {
  246. this->m_header->containerVersion = ContainerVersion::TEXB0003;
  247. // get back the pointer and use it
  248. pointer ++;
  249. this->m_header->freeImageFormat = static_cast <FREE_IMAGE_FORMAT> (*pointer++);
  250. }
  251. else if(memcmp (fileData, "TEXB0002", 9) == 0)
  252. {
  253. this->m_header->containerVersion = ContainerVersion::TEXB0002;
  254. // skip 4 bytes
  255. pointer ++;
  256. }
  257. else if (memcmp (fileData, "TEXB0001", 9) == 0)
  258. {
  259. this->m_header->containerVersion = ContainerVersion::TEXB0001;
  260. // skip 4 bytes
  261. pointer ++;
  262. }
  263. else
  264. {
  265. delete this->m_header;
  266. this->m_header = nullptr;
  267. throw std::runtime_error ("unknown texture format type");
  268. }
  269. // read the number of mipmaps available
  270. this->m_header->mipmapCount = *pointer ++;
  271. fileData = reinterpret_cast <char*> (pointer);
  272. for (uint32_t i = 0; i < this->m_header->mipmapCount; i ++)
  273. this->m_header->mipmaps.emplace_back (this->parseMipmap (this->m_header, &fileData));
  274. }
  275. CTexture::TextureMipmap* CTexture::parseMipmap (TextureHeader* header, char** originalFileData)
  276. {
  277. TextureMipmap* mipmap = new TextureMipmap ();
  278. // get the current position
  279. char* fileData = *originalFileData;
  280. // get an integer pointer
  281. uint32_t* pointer = reinterpret_cast <uint32_t*> (fileData);
  282. mipmap->width = *pointer++;
  283. mipmap->height = *pointer++;
  284. if (header->containerVersion == ContainerVersion::TEXB0002 ||
  285. header->containerVersion == ContainerVersion::TEXB0003)
  286. {
  287. mipmap->compression = *pointer++;
  288. mipmap->uncompressedSize = *pointer++;
  289. }
  290. mipmap->compressedSize = *pointer++;
  291. // get back a normal char pointer
  292. fileData = reinterpret_cast <char*> (pointer);
  293. if (mipmap->compression == 0)
  294. {
  295. // this might be better named as mipmap_bytes_size instead of compressedSize
  296. // as in uncompressed files this variable actually holds the file length
  297. mipmap->uncompressedSize = mipmap->compressedSize;
  298. }
  299. mipmap->uncompressedData = new char [mipmap->uncompressedSize];
  300. if (mipmap->compression == 1)
  301. {
  302. mipmap->compressedData = new char [mipmap->compressedSize];
  303. memcpy (mipmap->compressedData, fileData, mipmap->compressedSize);
  304. mipmap->decompressData ();
  305. // advance to the end of the mipmap
  306. fileData += mipmap->compressedSize;
  307. }
  308. else
  309. {
  310. memcpy (mipmap->uncompressedData, fileData, mipmap->uncompressedSize);
  311. // advance to the end of the mipmap
  312. fileData += mipmap->uncompressedSize;
  313. }
  314. // ensure the pointer is updated with the latest position when reading the data
  315. *originalFileData = fileData;
  316. return mipmap;
  317. }