X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fnetradiant.git;a=blobdiff_plain;f=plugins%2Fimage%2Fktx.cpp;fp=plugins%2Fimage%2Fktx.cpp;h=696ede55dfcaba4b9d5a27cdf6795adfe91f6acf;hp=9d2205359d3329bb32dc0216e5d8ae3a000fdf90;hb=cd6613e5171544b68d4ae70546c90a15c99b22a5;hpb=cd7ff1a1798cfae5d14811a310f56d2f1908490b diff --git a/plugins/image/ktx.cpp b/plugins/image/ktx.cpp index 9d220535..696ede55 100644 --- a/plugins/image/ktx.cpp +++ b/plugins/image/ktx.cpp @@ -29,403 +29,388 @@ #include "imagelib.h" -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 { +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 ~KTX_Decoder() = default; - - 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 ); }