CTexture.cpp 11 KB

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