--- /dev/null
+// Copyright 2009 Google Inc.
+//
+// Based on the code from Android ETC1Util.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "etclib.h"
+
+static void ETC_DecodeETC1SubBlock( byte *out, qboolean outRGBA, int r, int g, int b, int tableIndex, unsigned int low, qboolean second, qboolean flipped ){
+ int baseX = 0, baseY = 0;
+ const int modifierTable[] = {
+ 2, 8, -2, -8,
+ 5, 17, -5, -17,
+ 9, 29, -9, -29,
+ 13, 42, -13, -42,
+ 18, 60, -18, -60,
+ 24, 80, -24, -80,
+ 33, 106, -33, -106,
+ 47, 183, -47, -183
+ };
+ const int *table = modifierTable + tableIndex * 4;
+ int i;
+
+ if ( second ) {
+ if ( flipped ) {
+ baseY = 2;
+ }
+ else {
+ baseX = 2;
+ }
+ }
+
+ for ( i = 0; i < 8; i++ )
+ {
+ int x, y, k, delta;
+ int qr, qg, qb;
+ byte *q;
+
+ if ( flipped ) {
+ x = baseX + ( i >> 1 );
+ y = baseY + ( i & 1 );
+ }
+ else {
+ x = baseX + ( i >> 2 );
+ y = baseY + ( i & 3 );
+ }
+ k = y + ( x * 4 );
+ delta = table[( ( low >> k ) & 1 ) | ( ( low >> ( k + 15 ) ) & 2 )];
+
+ qr = r + delta;
+ qg = g + delta;
+ qb = b + delta;
+ if ( outRGBA ) {
+ q = out + 4 * ( x + 4 * y );
+ }
+ else {
+ q = out + 3 * ( x + 4 * y );
+ }
+ *( q++ ) = ( ( qr > 0 ) ? ( ( qr < 255 ) ? qr : 255 ) : 0 );
+ *( q++ ) = ( ( qg > 0 ) ? ( ( qg < 255 ) ? qg : 255 ) : 0 );
+ *( q++ ) = ( ( qb > 0 ) ? ( ( qb < 255 ) ? qb : 255 ) : 0 );
+ if ( outRGBA ) {
+ *( q++ ) = 255;
+ }
+ }
+}
+
+void ETC_DecodeETC1Block( const byte* in, byte* out, qboolean outRGBA ){
+ unsigned int high = ( in[0] << 24 ) | ( in[1] << 16 ) | ( in[2] << 8 ) | in[3];
+ unsigned int low = ( in[4] << 24 ) | ( in[5] << 16 ) | ( in[6] << 8 ) | in[7];
+ int r1, r2, g1, g2, b1, b2;
+ qboolean flipped = ( ( high & 1 ) != 0 );
+
+ if ( high & 2 ) {
+ int rBase, gBase, bBase;
+ const int lookup[] = { 0, 1, 2, 3, -4, -3, -2, -1 };
+
+ rBase = ( high >> 27 ) & 31;
+ r1 = ( rBase << 3 ) | ( rBase >> 2 );
+ rBase = ( rBase + ( lookup[( high >> 24 ) & 7] ) ) & 31;
+ r2 = ( rBase << 3 ) | ( rBase >> 2 );
+
+ gBase = ( high >> 19 ) & 31;
+ g1 = ( gBase << 3 ) | ( gBase >> 2 );
+ gBase = ( gBase + ( lookup[( high >> 16 ) & 7] ) ) & 31;
+ g2 = ( gBase << 3 ) | ( gBase >> 2 );
+
+ bBase = ( high >> 11 ) & 31;
+ b1 = ( bBase << 3 ) | ( bBase >> 2 );
+ bBase = ( bBase + ( lookup[( high >> 8 ) & 7] ) ) & 31;
+ b2 = ( bBase << 3 ) | ( bBase >> 2 );
+ }
+ else {
+ r1 = ( ( high >> 24 ) & 0xf0 ) | ( ( high >> 28 ) & 0xf );
+ r2 = ( ( high >> 20 ) & 0xf0 ) | ( ( high >> 24 ) & 0xf );
+ g1 = ( ( high >> 16 ) & 0xf0 ) | ( ( high >> 20 ) & 0xf );
+ g2 = ( ( high >> 12 ) & 0xf0 ) | ( ( high >> 16 ) & 0xf );
+ b1 = ( ( high >> 8 ) & 0xf0 ) | ( ( high >> 12 ) & 0xf );
+ b2 = ( ( high >> 4 ) & 0xf0 ) | ( ( high >> 8 ) & 0xf );
+ }
+
+ ETC_DecodeETC1SubBlock( out, outRGBA, r1, g1, b1, ( high >> 5 ) & 7, low, qfalse, flipped );
+ ETC_DecodeETC1SubBlock( out, outRGBA, r2, g2, b2, ( high >> 2 ) & 7, low, qtrue, flipped );
+}
--- /dev/null
+/*
+ Copyright (C) 2015, SiPlus, Chasseur de bots.
+ All Rights Reserved.
+
+ This file is part of GtkRadiant.
+
+ GtkRadiant is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ GtkRadiant is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GtkRadiant; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "ktx.h"
+
+#include <string.h>
+
+#include "bytestreamutils.h"
+#include "etclib.h"
+#include "ifilesystem.h"
+#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
+{
+public:
+ virtual void Decode( PointerInputStream& istream, byte* out ) = 0;
+ virtual unsigned int GetPixelSize() = 0;
+protected:
+ bool m_bigEndian;
+};
+
+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;
+ }
+};
+
+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;
+ }
+};
+
+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;
+ }
+};
+
+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;
+ }
+};
+
+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;
+ }
+};
+
+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;
+ }
+};
+
+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;
+ }
+};
+
+class KTX_Decoder_RGBA4 : public KTX_Decoder
+{
+protected:
+ 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;
+ }
+};
+
+class KTX_Decoder_RGBA5 : public KTX_Decoder
+{
+protected:
+ 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;
+ }
+};
+
+class KTX_Decoder_RGB5 : public KTX_Decoder
+{
+protected:
+ 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;
+ }
+};
+
+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* LoadKTX( ArchiveFile& file ){
+ ScopedArchiveBuffer buffer( file );
+ PointerInputStream istream( buffer.buffer );
+ return LoadKTXBuff( istream );
+}