CTexture.cpp 9.1 KB

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