123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293 |
- #include "VTF.h"
- #include <kapusha/core/Core.h>
- #include <kapusha/core/Log.h>
- #include <kapusha/io/Stream.h>
- #include <kapusha/render/Texture.h>
- using namespace kapusha;
- #pragma pack(push)
- #pragma pack(1)
- struct vtf_file_header_t {
- char magic[4];
- u32 version[2];
- u32 headerSize;
- };
- struct vtf_70_header_t {
- u16 width;
- u16 height;
- u32 flags;
- u16 frames;
- u16 firstFrame;
- u8 pad0[4];
- float reflectivity[3];
- u8 pad1[4];
- float bumpmapScale;
- u32 format;
- u8 mipmaps;
- u32 lowresFormat;
- u8 lowresWidth;
- u8 lowresHeight;
- u8 pad2;
- enum Format
- {
- FormatNone = -1,
- FormatRGBA8888 = 0,
- FormatABGR8888,
- FormatRGB888,
- FormatBGR888,
- FormatRGB565,
- FormatI8,
- FormatIA88,
- FormatP8,
- FormatA8,
- FormatRGB888Blue,
- FormatBGR888Blue,
- FormatARGB8888,
- FormatBGRA8888,
- FormatDXT1,
- FormatDXT3,
- FormatDXT5,
- FormatBGRX8888,
- FormatBGR565,
- FormatBGRX5551,
- FormatBGRA4444,
- FormatDXT1Alpha,
- FormatBGRA5551,
- FormatUV88,
- FormatUVWQ8888,
- FormatRGBA16161616F,
- FormatRGBA16161616,
- FormatUVLX8888
- };
- };
- #pragma pack(pop)
- unsigned imageSize(u32 format, int width, int height)
- {
- int pixels = width * height;
- int pixelsize = 0;
- switch(format)
- {
- case vtf_70_header_t::FormatDXT3:
- case vtf_70_header_t::FormatDXT5:
- pixelsize += 8;
- case vtf_70_header_t::FormatDXT1Alpha:
- case vtf_70_header_t::FormatDXT1:
- pixelsize += 8;
- pixels = (width / 4 + ((width % 4) ? 1 : 0))
- * (height / 4 + ((height % 4) ? 1 : 0));
- break;
- case vtf_70_header_t::FormatABGR8888:
- case vtf_70_header_t::FormatRGBA8888:
- case vtf_70_header_t::FormatARGB8888:
- case vtf_70_header_t::FormatBGRA8888:
- case vtf_70_header_t::FormatBGRX8888:
- case vtf_70_header_t::FormatUVWQ8888:
- case vtf_70_header_t::FormatUVLX8888:
- pixelsize = 4;
- break;
- case vtf_70_header_t::FormatRGB888:
- case vtf_70_header_t::FormatBGR888:
- case vtf_70_header_t::FormatRGB888Blue:
- case vtf_70_header_t::FormatBGR888Blue:
- pixelsize = 3;
- break;
- default:
- KP_ASSERT(!"Unsupported format");
- }
- return pixels * pixelsize;
- }
- struct Image : public kapusha::Texture::Meta {
- u8 *pixels;
- //Image() : width(0), height(0), format(FormatNone), pixels(0) {}
- Image(int _width, int _height)
- : Meta(_width, _height, BGRA8888)
- {
- pixels = new u8[size.x * size.y * 4];
- }
- ~Image()
- {
- delete pixels;
- }
- struct ColorRGB565 {
- u16 c;
- static const int maskRed = 0xf800;
- static const int maskGreen = 0x07e0;
- static const int maskBlue = 0x001f;
- kapusha::vec4f asRGBAf() const
- {
- return kapusha::vec4f((c >> 11) / 31.f,
- (((c >> 5)) & 63) / 63.f,
- (c & 31) / 31.f,
- 1.f);
- }
- u32 asBGRA32() const {
- u32 c32 = c;
- //return 0xff | (c32 & 0xf8) | ((c32 << 13) & 0xfc) | ((c32 << 27) & 0xf8);
- return 0xff000000 | ((c32 & maskRed) << 8) | ((c32 & maskGreen) << 5) | ((c32 & maskBlue) << 3);
- }
- static ColorRGB565 weightedSum(ColorRGB565 a, ColorRGB565 b,
- int A, int B, int D)
- {
- u32
- aRB = a.c & (maskRed | maskBlue),
- aG = a.c & maskGreen,
- bRB = b.c & (maskRed | maskBlue),
- bG = b.c & maskGreen;
- //! \todo overflow check? shouldn't happen with A,B,D used here
- aRB = ((A * aRB + B * bRB) / D) & (maskRed | maskBlue);
- aG = ((A * aG + B * bG) / D) & maskGreen;
- ColorRGB565 ret;
- ret.c = aRB | aG;
- return ret;
- }
- };
- bool readFromDXT1(kapusha::Stream& stream)
- {
- KP_ASSERT(size.x > 0);
- KP_ASSERT(size.y > 0);
- KP_ASSERT(format == BGRA8888);
- for (int y = 0; y < size.y; y += 4)
- {
- u32 *p = reinterpret_cast<u32*>(pixels) + y * size.x;
- for (int x = 0; x < size.x; x += 4, p += 4)
- {
- struct {
- ColorRGB565 c[2];
- u8 map[4];
- } chunk;
- u32 c[4];
- if (Stream::ErrorNone != stream.copy(&chunk, 8))
- return false;
- c[0] = chunk.c[0].asBGRA32();
- c[1] = chunk.c[1].asBGRA32();
- if (chunk.c[0].c > chunk.c[1].c)
- {
- c[2] = ColorRGB565::weightedSum(chunk.c[0], chunk.c[1],
- 2, 1, 3).asBGRA32();
- c[3] = ColorRGB565::weightedSum(chunk.c[0], chunk.c[1],
- 1, 2, 3).asBGRA32();
- } else {
- c[2] = ColorRGB565::weightedSum(chunk.c[0], chunk.c[1],
- 1, 1, 2).asBGRA32();
- c[3] = 0;
- }
- u32* pp = p;
- for(int row = 0; row < 4 && ((row+y) < size.y); ++row, pp += size.x-4)
- {
- *pp++ = c[chunk.map[row] >> 6];
- if(x+1 < size.x) *pp = c[(chunk.map[row] >> 4) & 3];
- ++pp;
- if(x+2 < size.x) *pp = c[(chunk.map[row] >> 2) & 3];
- ++pp;
- if(x+3 < size.x) *pp = c[chunk.map[row] & 3];
- ++pp;
- }
- }
- }
- return true;
- }
- };
- VTF::VTF(void)
- {
- }
- VTF::~VTF(void)
- {
- }
- kapusha::Texture *VTF::load(kapusha::StreamSeekable& stream)
- {
- vtf_file_header_t file_header;
- if(Stream::ErrorNone != stream.copy(&file_header, sizeof file_header))
- {
- return 0;
- }
-
- if (file_header.magic[0] != 'V' || file_header.magic[1] != 'T' ||
- file_header.magic[2] != 'F' || file_header.magic[3] != 0)
- return 0;
- L("vtf image ver %d.%d", file_header.version[0], file_header.version[1]);
- if (!(file_header.version[0] == 7 && (file_header.version[1] == 0 || file_header.version[1] == 1)))
- {
- L("versions other than 7.0, 7.1 are not supported");
- return 0;
- }
- vtf_70_header_t header;
- int szh = sizeof(header), szfh = sizeof(file_header);
- L("%d %d", szh, szfh);
- KP_ENSURE(Stream::ErrorNone == stream.copy(&header, sizeof header));
- KP_ASSERT(file_header.headerSize == (sizeof(header) + sizeof(file_header)));
- L("\timage format %d size %dx%d",
- header.format, header.width, header.height);
- L("\tflags %08x", header.flags);
- L("\tframes %d->%d", header.firstFrame, header.frames);
- L("\treflect %f %f %f, bump %f",
- header.reflectivity[0], header.reflectivity[1], header.reflectivity[2],
- header.bumpmapScale);
- L("\tlowres image format %d size %dx%d",
- header.lowresFormat, header.lowresWidth, header.lowresHeight);
- KP_ASSERT(header.lowresFormat == vtf_70_header_t::FormatDXT1);
- Image lowres(header.lowresWidth, header.lowresHeight);
- KP_ENSURE(lowres.readFromDXT1(stream));
- kapusha::Texture *ret = new kapusha::Texture();
- // skip everything until last one
- KP_ASSERT((header.flags & 0x4000) == 0); //! \todo envmap support
- for (int mipmap = header.mipmaps; mipmap > 0; --mipmap)
- for (int frame = header.firstFrame; frame <= header.frames; ++frame)
- for (int face = 0; face < 1; ++face)
- for (int depth = 0; depth < 1; ++depth) //! \todo layers support
- {
- stream.read(imageSize(header.format,
- header.width >> mipmap, header.height >> mipmap));
- KP_ASSERT(kapusha::Stream::ErrorNone == stream.error_);
- }
- if (header.format == vtf_70_header_t::FormatDXT1)
- {
- Image image(header.width, header.height);
- KP_ENSURE(image.readFromDXT1(stream));
- ret->upload(image, image.pixels);
- } else if (header.format == vtf_70_header_t::FormatBGRX8888)
- {
- Image image(header.width, header.height);
- KP_ENSURE(kapusha::Stream::ErrorNone ==
- stream.copy(image.pixels,
- imageSize(header.width, header.height, header.format)));
- ret->upload(image, image.pixels);
- } else
- {
- L("Unsupported image format %d, using dxt1 thumbnail", header.format);
- ret->upload(lowres, lowres.pixels);
- }
- size_.x = header.width;
- size_.y = header.height;
- return ret;
- }
|