X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=plugins%2Fimage%2Fktx.cpp;h=9d2205359d3329bb32dc0216e5d8ae3a000fdf90;hb=9dfae1c9b270ee369c6362903a9205b30751b95f;hp=582d0b8b3652eeee74b321c3522e5bd983e09126;hpb=d68faa1fc39c38df0c5fac2a85d7a6897dcb3cc4;p=xonotic%2Fnetradiant.git diff --git a/plugins/image/ktx.cpp b/plugins/image/ktx.cpp index 582d0b8b..9d220535 100644 --- a/plugins/image/ktx.cpp +++ b/plugins/image/ktx.cpp @@ -29,387 +29,403 @@ #include "imagelib.h" -#define KTX_TYPE_UNSIGNED_BYTE 0x1401 -#define KTX_TYPE_UNSIGNED_SHORT_4_4_4_4 0x8033 -#define KTX_TYPE_UNSIGNED_SHORT_5_5_5_1 0x8034 -#define KTX_TYPE_UNSIGNED_SHORT_5_6_5 0x8363 - -#define KTX_FORMAT_ALPHA 0x1906 -#define KTX_FORMAT_RGB 0x1907 -#define KTX_FORMAT_RGBA 0x1908 -#define KTX_FORMAT_LUMINANCE 0x1909 -#define KTX_FORMAT_LUMINANCE_ALPHA 0x190A -#define KTX_FORMAT_BGR 0x80E0 -#define KTX_FORMAT_BGRA 0x80E1 - -#define KTX_FORMAT_ETC1_RGB8 0x8D64 - -class KTX_Decoder -{ +const int KTX_TYPE_UNSIGNED_BYTE = 0x1401; +const int KTX_TYPE_UNSIGNED_SHORT_4_4_4_4 = 0x8033; +const int KTX_TYPE_UNSIGNED_SHORT_5_5_5_1 = 0x8034; +const int KTX_TYPE_UNSIGNED_SHORT_5_6_5 = 0x8363; + +const int KTX_FORMAT_ALPHA = 0x1906; +const int KTX_FORMAT_RGB = 0x1907; +const int KTX_FORMAT_RGBA = 0x1908; +const int KTX_FORMAT_LUMINANCE = 0x1909; +const int KTX_FORMAT_LUMINANCE_ALPHA = 0x190A; +const int KTX_FORMAT_BGR = 0x80E0; +const int KTX_FORMAT_BGRA = 0x80E1; + +const int KTX_FORMAT_ETC1_RGB8 = 0x8D64; + +class KTX_Decoder { public: - virtual void Decode( PointerInputStream& istream, byte* out ) = 0; - virtual unsigned int GetPixelSize() = 0; + virtual ~KTX_Decoder() = default; + + virtual void Decode(PointerInputStream &istream, byte *out) = 0; + + virtual unsigned int GetPixelSize() = 0; }; -class KTX_Decoder_A8 : public KTX_Decoder -{ +class KTX_Decoder_A8 : public KTX_Decoder { public: - virtual void Decode( PointerInputStream& istream, byte* out ){ - out[0] = out[1] = out[2] = 0; - out[3] = istream_read_byte( istream ); - } - virtual unsigned int GetPixelSize(){ - return 1; - } + virtual void Decode(PointerInputStream &istream, byte *out) + { + out[0] = out[1] = out[2] = 0; + out[3] = istream_read_byte(istream); + } + + virtual unsigned int GetPixelSize() + { + return 1; + } }; -class KTX_Decoder_RGB8 : public KTX_Decoder -{ +class KTX_Decoder_RGB8 : public KTX_Decoder { public: - virtual void Decode( PointerInputStream& istream, byte* out ){ - istream.read( out, 3 ); - out[3] = 255; - } - virtual unsigned int GetPixelSize(){ - return 3; - } + virtual void Decode(PointerInputStream &istream, byte *out) + { + istream.read(out, 3); + out[3] = 255; + } + + virtual unsigned int GetPixelSize() + { + return 3; + } }; -class KTX_Decoder_RGBA8 : public KTX_Decoder -{ +class KTX_Decoder_RGBA8 : public KTX_Decoder { public: - virtual void Decode( PointerInputStream& istream, byte* out ){ - istream.read( out, 4 ); - } - virtual unsigned int GetPixelSize(){ - return 4; - } + virtual void Decode(PointerInputStream &istream, byte *out) + { + istream.read(out, 4); + } + + virtual unsigned int GetPixelSize() + { + return 4; + } }; -class KTX_Decoder_L8 : public KTX_Decoder -{ +class KTX_Decoder_L8 : public KTX_Decoder { public: - virtual void Decode( PointerInputStream& istream, byte* out ){ - byte l = istream_read_byte( istream ); - out[0] = out[1] = out[2] = l; - out[3] = 255; - } - virtual unsigned int GetPixelSize(){ - return 1; - } + virtual void Decode(PointerInputStream &istream, byte *out) + { + byte l = istream_read_byte(istream); + out[0] = out[1] = out[2] = l; + out[3] = 255; + } + + virtual unsigned int GetPixelSize() + { + return 1; + } }; -class KTX_Decoder_LA8 : public KTX_Decoder -{ +class KTX_Decoder_LA8 : public KTX_Decoder { public: - virtual void Decode( PointerInputStream& istream, byte* out ){ - byte la[2]; - istream.read( la, 2 ); - out[0] = out[1] = out[2] = la[0]; - out[3] = la[1]; - } - virtual unsigned int GetPixelSize(){ - return 2; - } + virtual void Decode(PointerInputStream &istream, byte *out) + { + byte la[2]; + istream.read(la, 2); + out[0] = out[1] = out[2] = la[0]; + out[3] = la[1]; + } + + virtual unsigned int GetPixelSize() + { + return 2; + } }; -class KTX_Decoder_BGR8 : public KTX_Decoder -{ +class KTX_Decoder_BGR8 : public KTX_Decoder { public: - virtual void Decode( PointerInputStream& istream, byte* out ){ - byte bgr[3]; - istream.read( bgr, 3 ); - out[0] = bgr[2]; - out[1] = bgr[1]; - out[2] = bgr[0]; - out[3] = 255; - } - virtual unsigned int GetPixelSize(){ - return 3; - } + virtual void Decode(PointerInputStream &istream, byte *out) + { + byte bgr[3]; + istream.read(bgr, 3); + out[0] = bgr[2]; + out[1] = bgr[1]; + out[2] = bgr[0]; + out[3] = 255; + } + + virtual unsigned int GetPixelSize() + { + return 3; + } }; -class KTX_Decoder_BGRA8 : public KTX_Decoder -{ +class KTX_Decoder_BGRA8 : public KTX_Decoder { public: - virtual void Decode( PointerInputStream& istream, byte* out ){ - byte bgra[4]; - istream.read( bgra, 4 ); - out[0] = bgra[2]; - out[1] = bgra[1]; - out[2] = bgra[0]; - out[3] = bgra[3]; - } - virtual unsigned int GetPixelSize(){ - return 4; - } + virtual void Decode(PointerInputStream &istream, byte *out) + { + byte bgra[4]; + istream.read(bgra, 4); + out[0] = bgra[2]; + out[1] = bgra[1]; + out[2] = bgra[0]; + out[3] = bgra[3]; + } + + virtual unsigned int GetPixelSize() + { + return 4; + } }; -class KTX_Decoder_RGBA4 : public KTX_Decoder -{ +class KTX_Decoder_RGBA4 : public KTX_Decoder { protected: - bool m_bigEndian; + bool m_bigEndian; public: - KTX_Decoder_RGBA4( bool bigEndian ) : m_bigEndian( bigEndian ){} - virtual void Decode( PointerInputStream& istream, byte* out ){ - uint16_t rgba; - if ( m_bigEndian ) { - rgba = istream_read_uint16_be( istream ); - } - else { - rgba = istream_read_uint16_le( istream ); - } - int r = ( rgba >> 12 ) & 0xf; - int g = ( rgba >> 8 ) & 0xf; - int b = ( rgba >> 4 ) & 0xf; - int a = rgba & 0xf; - out[0] = ( r << 4 ) | r; - out[1] = ( g << 4 ) | g; - out[2] = ( b << 4 ) | b; - out[3] = ( a << 4 ) | a; - } - virtual unsigned int GetPixelSize(){ - return 2; - } + KTX_Decoder_RGBA4(bool bigEndian) : m_bigEndian(bigEndian) + {} + + virtual void Decode(PointerInputStream &istream, byte *out) + { + uint16_t rgba; + if (m_bigEndian) { + rgba = istream_read_uint16_be(istream); + } else { + rgba = istream_read_uint16_le(istream); + } + int r = (rgba >> 12) & 0xf; + int g = (rgba >> 8) & 0xf; + int b = (rgba >> 4) & 0xf; + int a = rgba & 0xf; + out[0] = (r << 4) | r; + out[1] = (g << 4) | g; + out[2] = (b << 4) | b; + out[3] = (a << 4) | a; + } + + virtual unsigned int GetPixelSize() + { + return 2; + } }; -class KTX_Decoder_RGBA5 : public KTX_Decoder -{ +class KTX_Decoder_RGBA5 : public KTX_Decoder { protected: - bool m_bigEndian; + bool m_bigEndian; public: - KTX_Decoder_RGBA5( bool bigEndian ) : m_bigEndian( bigEndian ){} - virtual void Decode( PointerInputStream& istream, byte* out ){ - uint16_t rgba; - if ( m_bigEndian ) { - rgba = istream_read_uint16_be( istream ); - } - else { - rgba = istream_read_uint16_le( istream ); - } - int r = ( rgba >> 11 ) & 0x1f; - int g = ( rgba >> 6 ) & 0x1f; - int b = ( rgba >> 1 ) & 0x1f; - out[0] = ( r << 3 ) | ( r >> 2 ); - out[1] = ( g << 3 ) | ( g >> 2 ); - out[2] = ( b << 3 ) | ( b >> 2 ); - out[3] = ( rgba & 1 ) * 255; - } - virtual unsigned int GetPixelSize(){ - return 2; - } + KTX_Decoder_RGBA5(bool bigEndian) : m_bigEndian(bigEndian) + {} + + virtual void Decode(PointerInputStream &istream, byte *out) + { + uint16_t rgba; + if (m_bigEndian) { + rgba = istream_read_uint16_be(istream); + } else { + rgba = istream_read_uint16_le(istream); + } + int r = (rgba >> 11) & 0x1f; + int g = (rgba >> 6) & 0x1f; + int b = (rgba >> 1) & 0x1f; + out[0] = (r << 3) | (r >> 2); + out[1] = (g << 3) | (g >> 2); + out[2] = (b << 3) | (b >> 2); + out[3] = (rgba & 1) * 255; + } + + virtual unsigned int GetPixelSize() + { + return 2; + } }; -class KTX_Decoder_RGB5 : public KTX_Decoder -{ +class KTX_Decoder_RGB5 : public KTX_Decoder { protected: - bool m_bigEndian; + bool m_bigEndian; public: - KTX_Decoder_RGB5( bool bigEndian ) : m_bigEndian( bigEndian ){} - virtual void Decode( PointerInputStream& istream, byte* out ){ - uint16_t rgb; - if ( m_bigEndian ) { - rgb = istream_read_uint16_be( istream ); - } - else { - rgb = istream_read_uint16_le( istream ); - } - int r = ( rgb >> 11 ) & 0x1f; - int g = ( rgb >> 5 ) & 0x3f; - int b = rgb & 0x1f; - out[0] = ( r << 3 ) | ( r >> 2 ); - out[1] = ( g << 2 ) | ( g >> 4 ); - out[2] = ( b << 3 ) | ( b >> 2 ); - out[3] = 255; - } - virtual unsigned int GetPixelSize(){ - return 2; - } + KTX_Decoder_RGB5(bool bigEndian) : m_bigEndian(bigEndian) + {} + + virtual void Decode(PointerInputStream &istream, byte *out) + { + uint16_t rgb; + if (m_bigEndian) { + rgb = istream_read_uint16_be(istream); + } else { + rgb = istream_read_uint16_le(istream); + } + int r = (rgb >> 11) & 0x1f; + int g = (rgb >> 5) & 0x3f; + int b = rgb & 0x1f; + out[0] = (r << 3) | (r >> 2); + out[1] = (g << 2) | (g >> 4); + out[2] = (b << 3) | (b >> 2); + out[3] = 255; + } + + virtual unsigned int GetPixelSize() + { + return 2; + } }; -static void KTX_DecodeETC1( PointerInputStream& istream, Image& image ){ - unsigned int width = image.getWidth(), height = image.getHeight(); - unsigned int stride = width * 4; - byte* pixbuf = image.getRGBAPixels(); - byte etc[8], rgba[64]; - - for ( unsigned int y = 0; y < height; y += 4, pixbuf += stride * 4 ) - { - unsigned int blockrows = height - y; - if ( blockrows > 4 ) { - blockrows = 4; - } - - byte* p = pixbuf; - for ( unsigned int x = 0; x < width; x += 4, p += 16 ) - { - istream.read( etc, 8 ); - ETC_DecodeETC1Block( etc, rgba, qtrue ); - - unsigned int blockrowsize = width - x; - if ( blockrowsize > 4 ) { - blockrowsize = 4; - } - blockrowsize *= 4; - for ( unsigned int blockrow = 0; blockrow < blockrows; blockrow++ ) - { - memcpy( p + blockrow * stride, rgba + blockrow * 16, blockrowsize ); - } - } - } +static void KTX_DecodeETC1(PointerInputStream &istream, Image &image) +{ + unsigned int width = image.getWidth(), height = image.getHeight(); + unsigned int stride = width * 4; + byte *pixbuf = image.getRGBAPixels(); + byte etc[8], rgba[64]; + + for (unsigned int y = 0; y < height; y += 4, pixbuf += stride * 4) { + unsigned int blockrows = height - y; + if (blockrows > 4) { + blockrows = 4; + } + + byte *p = pixbuf; + for (unsigned int x = 0; x < width; x += 4, p += 16) { + istream.read(etc, 8); + ETC_DecodeETC1Block(etc, rgba, qtrue); + + unsigned int blockrowsize = width - x; + if (blockrowsize > 4) { + blockrowsize = 4; + } + blockrowsize *= 4; + for (unsigned int blockrow = 0; blockrow < blockrows; blockrow++) { + memcpy(p + blockrow * stride, rgba + blockrow * 16, blockrowsize); + } + } + } } -Image* LoadKTXBuff( PointerInputStream& istream ){ - byte identifier[12]; - istream.read( identifier, 12 ); - if ( memcmp( identifier, "\xABKTX 11\xBB\r\n\x1A\n", 12 ) ) { - globalErrorStream() << "LoadKTX: Image has the wrong identifier\n"; - return 0; - } - - bool bigEndian = ( istream_read_uint32_le( istream ) == 0x01020304 ); - - unsigned int type; - if ( bigEndian ) { - type = istream_read_uint32_be( istream ); - } - else { - type = istream_read_uint32_le( istream ); - } - - // For compressed textures, the format is in glInternalFormat. - // For uncompressed textures, it's in glBaseInternalFormat. - istream.seek( ( type ? 3 : 2 ) * sizeof( uint32_t ) ); - unsigned int format; - if ( bigEndian ) { - format = istream_read_uint32_be( istream ); - } - else { - format = istream_read_uint32_le( istream ); - } - if ( !type ) { - istream.seek( sizeof( uint32_t ) ); - } - - unsigned int width, height; - if ( bigEndian ) { - width = istream_read_uint32_be( istream ); - height = istream_read_uint32_be( istream ); - } - else { - width = istream_read_uint32_le( istream ); - height = istream_read_uint32_le( istream ); - } - if ( !width ) { - globalErrorStream() << "LoadKTX: Image has zero width\n"; - return 0; - } - if ( !height ) { - height = 1; - } - - // Skip the key/values and load the first 2D image in the texture. - // Since KTXorientation is only a hint and has no effect on the texture data and coordinates, it must be ignored. - istream.seek( 4 * sizeof( uint32_t ) ); - unsigned int bytesOfKeyValueData; - if ( bigEndian ) { - bytesOfKeyValueData = istream_read_uint32_be( istream ); - } - else { - bytesOfKeyValueData = istream_read_uint32_le( istream ); - } - istream.seek( bytesOfKeyValueData + sizeof( uint32_t ) ); - - RGBAImage* image = new RGBAImage( width, height ); - - if ( type ) { - KTX_Decoder* decoder = NULL; - switch ( type ) - { - case KTX_TYPE_UNSIGNED_BYTE: - switch ( format ) - { - case KTX_FORMAT_ALPHA: - decoder = new KTX_Decoder_A8(); - break; - case KTX_FORMAT_RGB: - decoder = new KTX_Decoder_RGB8(); - break; - case KTX_FORMAT_RGBA: - decoder = new KTX_Decoder_RGBA8(); - break; - case KTX_FORMAT_LUMINANCE: - decoder = new KTX_Decoder_L8(); - break; - case KTX_FORMAT_LUMINANCE_ALPHA: - decoder = new KTX_Decoder_LA8(); - break; - case KTX_FORMAT_BGR: - decoder = new KTX_Decoder_BGR8(); - break; - case KTX_FORMAT_BGRA: - decoder = new KTX_Decoder_BGRA8(); - break; - } - break; - case KTX_TYPE_UNSIGNED_SHORT_4_4_4_4: - if ( format == KTX_FORMAT_RGBA ) { - decoder = new KTX_Decoder_RGBA4( bigEndian ); - } - break; - case KTX_TYPE_UNSIGNED_SHORT_5_5_5_1: - if ( format == KTX_FORMAT_RGBA ) { - decoder = new KTX_Decoder_RGBA5( bigEndian ); - } - break; - case KTX_TYPE_UNSIGNED_SHORT_5_6_5: - if ( format == KTX_FORMAT_RGB ) { - decoder = new KTX_Decoder_RGB5( bigEndian ); - } - break; - } - - if ( !decoder ) { - globalErrorStream() << "LoadKTX: Image has an unsupported pixel type " << type << " or format " << format << "\n"; - image->release(); - return 0; - } - - unsigned int inRowLength = width * decoder->GetPixelSize(); - unsigned int inPadding = ( ( inRowLength + 3 ) & ~3 ) - inRowLength; - byte* out = image->getRGBAPixels(); - for ( unsigned int y = 0; y < height; y++ ) - { - for ( unsigned int x = 0; x < width; x++, out += 4 ) - { - decoder->Decode( istream, out ); - } - - if ( inPadding ) { - istream.seek( inPadding ); - } - } - - delete decoder; - } - else { - switch ( format ) - { - case KTX_FORMAT_ETC1_RGB8: - KTX_DecodeETC1( istream, *image ); - break; - default: - globalErrorStream() << "LoadKTX: Image has an unsupported compressed format " << format << "\n"; - image->release(); - return 0; - } - } - - return image; +Image *LoadKTXBuff(PointerInputStream &istream) +{ + byte identifier[12]; + istream.read(identifier, 12); + if (memcmp(identifier, "\xABKTX 11\xBB\r\n\x1A\n", 12)) { + globalErrorStream() << "LoadKTX: Image has the wrong identifier\n"; + return 0; + } + + bool bigEndian = (istream_read_uint32_le(istream) == 0x01020304); + + unsigned int type; + if (bigEndian) { + type = istream_read_uint32_be(istream); + } else { + type = istream_read_uint32_le(istream); + } + + // For compressed textures, the format is in glInternalFormat. + // For uncompressed textures, it's in glBaseInternalFormat. + istream.seek((type ? 3 : 2) * sizeof(uint32_t)); + unsigned int format; + if (bigEndian) { + format = istream_read_uint32_be(istream); + } else { + format = istream_read_uint32_le(istream); + } + if (!type) { + istream.seek(sizeof(uint32_t)); + } + + unsigned int width, height; + if (bigEndian) { + width = istream_read_uint32_be(istream); + height = istream_read_uint32_be(istream); + } else { + width = istream_read_uint32_le(istream); + height = istream_read_uint32_le(istream); + } + if (!width) { + globalErrorStream() << "LoadKTX: Image has zero width\n"; + return 0; + } + if (!height) { + height = 1; + } + + // Skip the key/values and load the first 2D image in the texture. + // Since KTXorientation is only a hint and has no effect on the texture data and coordinates, it must be ignored. + istream.seek(4 * sizeof(uint32_t)); + unsigned int bytesOfKeyValueData; + if (bigEndian) { + bytesOfKeyValueData = istream_read_uint32_be(istream); + } else { + bytesOfKeyValueData = istream_read_uint32_le(istream); + } + istream.seek(bytesOfKeyValueData + sizeof(uint32_t)); + + RGBAImage *image = new RGBAImage(width, height); + + if (type) { + KTX_Decoder *decoder = NULL; + switch (type) { + case KTX_TYPE_UNSIGNED_BYTE: + switch (format) { + case KTX_FORMAT_ALPHA: + decoder = new KTX_Decoder_A8(); + break; + case KTX_FORMAT_RGB: + decoder = new KTX_Decoder_RGB8(); + break; + case KTX_FORMAT_RGBA: + decoder = new KTX_Decoder_RGBA8(); + break; + case KTX_FORMAT_LUMINANCE: + decoder = new KTX_Decoder_L8(); + break; + case KTX_FORMAT_LUMINANCE_ALPHA: + decoder = new KTX_Decoder_LA8(); + break; + case KTX_FORMAT_BGR: + decoder = new KTX_Decoder_BGR8(); + break; + case KTX_FORMAT_BGRA: + decoder = new KTX_Decoder_BGRA8(); + break; + } + break; + case KTX_TYPE_UNSIGNED_SHORT_4_4_4_4: + if (format == KTX_FORMAT_RGBA) { + decoder = new KTX_Decoder_RGBA4(bigEndian); + } + break; + case KTX_TYPE_UNSIGNED_SHORT_5_5_5_1: + if (format == KTX_FORMAT_RGBA) { + decoder = new KTX_Decoder_RGBA5(bigEndian); + } + break; + case KTX_TYPE_UNSIGNED_SHORT_5_6_5: + if (format == KTX_FORMAT_RGB) { + decoder = new KTX_Decoder_RGB5(bigEndian); + } + break; + } + + if (!decoder) { + globalErrorStream() << "LoadKTX: Image has an unsupported pixel type " << type << " or format " << format + << "\n"; + image->release(); + return 0; + } + + unsigned int inRowLength = width * decoder->GetPixelSize(); + unsigned int inPadding = ((inRowLength + 3) & ~3) - inRowLength; + byte *out = image->getRGBAPixels(); + for (unsigned int y = 0; y < height; y++) { + for (unsigned int x = 0; x < width; x++, out += 4) { + decoder->Decode(istream, out); + } + + if (inPadding) { + istream.seek(inPadding); + } + } + + delete decoder; + } else { + switch (format) { + case KTX_FORMAT_ETC1_RGB8: + KTX_DecodeETC1(istream, *image); + break; + default: + globalErrorStream() << "LoadKTX: Image has an unsupported compressed format " << format << "\n"; + image->release(); + return 0; + } + } + + return image; } -Image* LoadKTX( ArchiveFile& file ){ - ScopedArchiveBuffer buffer( file ); - PointerInputStream istream( buffer.buffer ); - return LoadKTXBuff( istream ); +Image *LoadKTX(ArchiveFile &file) +{ + ScopedArchiveBuffer buffer(file); + PointerInputStream istream(buffer.buffer); + return LoadKTXBuff(istream); }