VTF.cpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. #include "VTF.h"
  2. #include <kapusha/core/Core.h>
  3. #include <kapusha/core/Log.h>
  4. #include <kapusha/io/Stream.h>
  5. #include <kapusha/render/Texture.h>
  6. using namespace kapusha;
  7. #pragma pack(push)
  8. #pragma pack(1)
  9. struct vtf_file_header_t {
  10. char magic[4];
  11. u32 version[2];
  12. u32 headerSize;
  13. };
  14. struct vtf_70_header_t {
  15. u16 width;
  16. u16 height;
  17. u32 flags;
  18. u16 frames;
  19. u16 firstFrame;
  20. u8 pad0[4];
  21. float reflectivity[3];
  22. u8 pad1[4];
  23. float bumpmapScale;
  24. u32 format;
  25. u8 mipmaps;
  26. u32 lowresFormat;
  27. u8 lowresWidth;
  28. u8 lowresHeight;
  29. u8 pad2;
  30. enum Format
  31. {
  32. FormatNone = -1,
  33. FormatRGBA8888 = 0,
  34. FormatABGR8888,
  35. FormatRGB888,
  36. FormatBGR888,
  37. FormatRGB565,
  38. FormatI8,
  39. FormatIA88,
  40. FormatP8,
  41. FormatA8,
  42. FormatRGB888Blue,
  43. FormatBGR888Blue,
  44. FormatARGB8888,
  45. FormatBGRA8888,
  46. FormatDXT1,
  47. FormatDXT3,
  48. FormatDXT5,
  49. FormatBGRX8888,
  50. FormatBGR565,
  51. FormatBGRX5551,
  52. FormatBGRA4444,
  53. FormatDXT1Alpha,
  54. FormatBGRA5551,
  55. FormatUV88,
  56. FormatUVWQ8888,
  57. FormatRGBA16161616F,
  58. FormatRGBA16161616,
  59. FormatUVLX8888
  60. };
  61. };
  62. #pragma pack(pop)
  63. unsigned imageSize(u32 format, int width, int height)
  64. {
  65. int pixels = width * height;
  66. int pixelsize = 0;
  67. switch(format)
  68. {
  69. case vtf_70_header_t::FormatDXT3:
  70. case vtf_70_header_t::FormatDXT5:
  71. pixelsize += 8;
  72. case vtf_70_header_t::FormatDXT1Alpha:
  73. case vtf_70_header_t::FormatDXT1:
  74. pixelsize += 8;
  75. pixels = (width / 4 + ((width % 4) ? 1 : 0))
  76. * (height / 4 + ((height % 4) ? 1 : 0));
  77. break;
  78. case vtf_70_header_t::FormatABGR8888:
  79. case vtf_70_header_t::FormatRGBA8888:
  80. case vtf_70_header_t::FormatARGB8888:
  81. case vtf_70_header_t::FormatBGRA8888:
  82. case vtf_70_header_t::FormatBGRX8888:
  83. case vtf_70_header_t::FormatUVWQ8888:
  84. case vtf_70_header_t::FormatUVLX8888:
  85. pixelsize = 4;
  86. break;
  87. case vtf_70_header_t::FormatRGB888:
  88. case vtf_70_header_t::FormatBGR888:
  89. case vtf_70_header_t::FormatRGB888Blue:
  90. case vtf_70_header_t::FormatBGR888Blue:
  91. pixelsize = 3;
  92. break;
  93. default:
  94. KP_ASSERT(!"Unsupported format");
  95. }
  96. return pixels * pixelsize;
  97. }
  98. struct Image : public kapusha::Texture::Meta {
  99. u8 *pixels;
  100. //Image() : width(0), height(0), format(FormatNone), pixels(0) {}
  101. Image(int _width, int _height)
  102. : Meta(_width, _height, BGRA8888)
  103. {
  104. pixels = new u8[size.x * size.y * 4];
  105. }
  106. ~Image()
  107. {
  108. delete pixels;
  109. }
  110. struct ColorRGB565 {
  111. u16 c;
  112. static const int maskRed = 0xf800;
  113. static const int maskGreen = 0x07e0;
  114. static const int maskBlue = 0x001f;
  115. kapusha::vec4f asRGBAf() const
  116. {
  117. return kapusha::vec4f((c >> 11) / 31.f,
  118. (((c >> 5)) & 63) / 63.f,
  119. (c & 31) / 31.f,
  120. 1.f);
  121. }
  122. u32 asBGRA32() const {
  123. u32 c32 = c;
  124. //return 0xff | (c32 & 0xf8) | ((c32 << 13) & 0xfc) | ((c32 << 27) & 0xf8);
  125. return 0xff000000 | ((c32 & maskRed) << 8) | ((c32 & maskGreen) << 5) | ((c32 & maskBlue) << 3);
  126. }
  127. static ColorRGB565 weightedSum(ColorRGB565 a, ColorRGB565 b,
  128. int A, int B, int D)
  129. {
  130. u32
  131. aRB = a.c & (maskRed | maskBlue),
  132. aG = a.c & maskGreen,
  133. bRB = b.c & (maskRed | maskBlue),
  134. bG = b.c & maskGreen;
  135. //! \todo overflow check? shouldn't happen with A,B,D used here
  136. aRB = ((A * aRB + B * bRB) / D) & (maskRed | maskBlue);
  137. aG = ((A * aG + B * bG) / D) & maskGreen;
  138. ColorRGB565 ret;
  139. ret.c = aRB | aG;
  140. return ret;
  141. }
  142. };
  143. bool readFromDXT1(kapusha::Stream& stream)
  144. {
  145. KP_ASSERT(size.x > 0);
  146. KP_ASSERT(size.y > 0);
  147. KP_ASSERT(format == BGRA8888);
  148. for (int y = 0; y < size.y; y += 4)
  149. {
  150. u32 *p = reinterpret_cast<u32*>(pixels) + y * size.x;
  151. for (int x = 0; x < size.x; x += 4, p += 4)
  152. {
  153. struct {
  154. ColorRGB565 c[2];
  155. u8 map[4];
  156. } chunk;
  157. u32 c[4];
  158. if (Stream::ErrorNone != stream.copy(&chunk, 8))
  159. return false;
  160. c[0] = chunk.c[0].asBGRA32();
  161. c[1] = chunk.c[1].asBGRA32();
  162. if (chunk.c[0].c > chunk.c[1].c)
  163. {
  164. c[2] = ColorRGB565::weightedSum(chunk.c[0], chunk.c[1],
  165. 2, 1, 3).asBGRA32();
  166. c[3] = ColorRGB565::weightedSum(chunk.c[0], chunk.c[1],
  167. 1, 2, 3).asBGRA32();
  168. } else {
  169. c[2] = ColorRGB565::weightedSum(chunk.c[0], chunk.c[1],
  170. 1, 1, 2).asBGRA32();
  171. c[3] = 0;
  172. }
  173. u32* pp = p;
  174. for(int row = 0; row < 4 && ((row+y) < size.y); ++row, pp += size.x-4)
  175. {
  176. *pp++ = c[chunk.map[row] >> 6];
  177. if(x+1 < size.x) *pp = c[(chunk.map[row] >> 4) & 3];
  178. ++pp;
  179. if(x+2 < size.x) *pp = c[(chunk.map[row] >> 2) & 3];
  180. ++pp;
  181. if(x+3 < size.x) *pp = c[chunk.map[row] & 3];
  182. ++pp;
  183. }
  184. }
  185. }
  186. return true;
  187. }
  188. };
  189. VTF::VTF(void)
  190. {
  191. }
  192. VTF::~VTF(void)
  193. {
  194. }
  195. kapusha::Texture *VTF::load(kapusha::StreamSeekable& stream)
  196. {
  197. vtf_file_header_t file_header;
  198. if(Stream::ErrorNone != stream.copy(&file_header, sizeof file_header))
  199. {
  200. return 0;
  201. }
  202. if (file_header.magic[0] != 'V' || file_header.magic[1] != 'T' ||
  203. file_header.magic[2] != 'F' || file_header.magic[3] != 0)
  204. return 0;
  205. L("vtf image ver %d.%d", file_header.version[0], file_header.version[1]);
  206. if (!(file_header.version[0] == 7 && (file_header.version[1] == 0 || file_header.version[1] == 1)))
  207. {
  208. L("versions other than 7.0, 7.1 are not supported");
  209. return 0;
  210. }
  211. vtf_70_header_t header;
  212. int szh = sizeof(header), szfh = sizeof(file_header);
  213. L("%d %d", szh, szfh);
  214. KP_ENSURE(Stream::ErrorNone == stream.copy(&header, sizeof header));
  215. KP_ASSERT(file_header.headerSize == (sizeof(header) + sizeof(file_header)));
  216. L("\timage format %d size %dx%d",
  217. header.format, header.width, header.height);
  218. L("\tflags %08x", header.flags);
  219. L("\tframes %d->%d", header.firstFrame, header.frames);
  220. L("\treflect %f %f %f, bump %f",
  221. header.reflectivity[0], header.reflectivity[1], header.reflectivity[2],
  222. header.bumpmapScale);
  223. L("\tlowres image format %d size %dx%d",
  224. header.lowresFormat, header.lowresWidth, header.lowresHeight);
  225. KP_ASSERT(header.lowresFormat == vtf_70_header_t::FormatDXT1);
  226. Image lowres(header.lowresWidth, header.lowresHeight);
  227. KP_ENSURE(lowres.readFromDXT1(stream));
  228. kapusha::Texture *ret = new kapusha::Texture();
  229. // skip everything until last one
  230. KP_ASSERT((header.flags & 0x4000) == 0); //! \todo envmap support
  231. for (int mipmap = header.mipmaps; mipmap > 0; --mipmap)
  232. for (int frame = header.firstFrame; frame <= header.frames; ++frame)
  233. for (int face = 0; face < 1; ++face)
  234. for (int depth = 0; depth < 1; ++depth) //! \todo layers support
  235. {
  236. stream.read(imageSize(header.format,
  237. header.width >> mipmap, header.height >> mipmap));
  238. KP_ASSERT(kapusha::Stream::ErrorNone == stream.error_);
  239. }
  240. if (header.format == vtf_70_header_t::FormatDXT1)
  241. {
  242. Image image(header.width, header.height);
  243. KP_ENSURE(image.readFromDXT1(stream));
  244. ret->upload(image, image.pixels);
  245. } else if (header.format == vtf_70_header_t::FormatBGRX8888)
  246. {
  247. Image image(header.width, header.height);
  248. KP_ENSURE(kapusha::Stream::ErrorNone ==
  249. stream.copy(image.pixels,
  250. imageSize(header.width, header.height, header.format)));
  251. ret->upload(image, image.pixels);
  252. } else
  253. {
  254. L("Unsupported image format %d, using dxt1 thumbnail", header.format);
  255. ret->upload(lowres, lowres.pixels);
  256. }
  257. size_.x = header.width;
  258. size_.y = header.height;
  259. return ret;
  260. }