Merge commit 'refs/pull/1/head' of https://github.com/xonotic/netradient
authorRudolf Polzer <divverent@xonotic.org>
Mon, 1 Jun 2015 08:45:24 +0000 (10:45 +0200)
committerRudolf Polzer <divverent@xonotic.org>
Mon, 1 Jun 2015 08:45:24 +0000 (10:45 +0200)
Makefile
download-gamepacks.sh
libs/bytestreamutils.h
libs/etclib.c [new file with mode: 0644]
libs/etclib.h [new file with mode: 0644]
plugins/image/image.cpp
plugins/image/ktx.cpp [new file with mode: 0644]
plugins/image/ktx.h [new file with mode: 0644]
tools/quake3/common/imagelib.c
tools/quake3/common/imagelib.h
tools/quake3/q3map2/image.c

index 9eee121..51b3fa9 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -536,6 +536,7 @@ $(INSTALLDIR)/q3map2.$(EXE): \
        tools/quake3/q3map2/vis.o \
        tools/quake3/q3map2/writebsp.o \
        libddslib.$(A) \
+       libetclib.$(A) \
        libfilematch.$(A) \
        libl_net.$(A) \
        libmathlib.$(A) \
@@ -585,6 +586,10 @@ libddslib.$(A): CPPFLAGS_EXTRA := -Ilibs
 libddslib.$(A): \
        libs/ddslib/ddslib.o \
 
+libetclib.$(A): CPPFLAGS_EXTRA := -Ilibs
+libetclib.$(A): \
+       libs/etclib.o \
+
 $(INSTALLDIR)/q3data.$(EXE): LIBS_EXTRA := $(LIBS_XML) $(LIBS_GLIB) $(LIBS_ZLIB)
 $(INSTALLDIR)/q3data.$(EXE): CPPFLAGS_EXTRA := $(CPPFLAGS_XML) $(CPPFLAGS_GLIB) $(CPPFLAGS_ZLIB) -Itools/quake3/common -Ilibs -Iinclude
 $(INSTALLDIR)/q3data.$(EXE): \
@@ -609,6 +614,7 @@ $(INSTALLDIR)/q3data.$(EXE): \
        tools/quake3/q3data/stripper.o \
        tools/quake3/q3data/video.o \
        libfilematch.$(A) \
+       libetclib.$(A) \
        libl_net.$(A) \
        libmathlib.$(A) \
        $(if $(findstring $(OS),Win32),icons/q3data.o,) \
@@ -803,9 +809,11 @@ $(INSTALLDIR)/modules/image.$(DLL): \
        plugins/image/dds.o \
        plugins/image/image.o \
        plugins/image/jpeg.o \
+       plugins/image/ktx.o \
        plugins/image/pcx.o \
        plugins/image/tga.o \
        libddslib.$(A) \
+       libetclib.$(A) \
 
 $(INSTALLDIR)/modules/imageq2.$(DLL): CPPFLAGS_EXTRA := -Ilibs -Iinclude
 $(INSTALLDIR)/modules/imageq2.$(DLL): \
index 7717cd2..ff5b984 100755 (executable)
@@ -158,5 +158,6 @@ pack QuakePack       GPL         zip1   http://ingar.satgnu.net/files/gtkradiant
 pack TremulousPack   proprietary zip1   http://ingar.satgnu.net/files/gtkradiant/gamepacks/TremulousPack.zip
 pack UFOAIPack       proprietary svn    svn://svn.icculus.org/gtkradiant-gamepacks/UFOAIPack/branches/1.5/
 #pack WarsowPack     GPL         svn    https://svn.bountysource.com/wswpack/trunk/netradiant/games/WarsowPack/
-pack WarsowPack      GPL         zip1   http://ingar.satgnu.net/files/gtkradiant/gamepacks/WarsowPack.zip
+#pack WarsowPack     GPL         zip1   http://ingar.satgnu.net/files/gtkradiant/gamepacks/WarsowPack.zip
+pack WarsowPack      GPL         git    https://github.com/Warsow/NetRadiantPack.git
 pack XonoticPack     GPL         git    http://git.xonotic.org/xonotic/netradiant-xonoticpack.git
index 156333a..90a9048 100644 (file)
@@ -83,6 +83,13 @@ inline int16_t istream_read_int16_le( InputStreamType& istream ){
 }
 
 template<typename InputStreamType>
+inline int16_t istream_read_int16_be( InputStreamType& istream ){
+       int16_t value;
+       istream_read_big_endian( istream, value );
+       return value;
+}
+
+template<typename InputStreamType>
 inline uint16_t istream_read_uint16_le( InputStreamType& istream ){
        uint16_t value;
        istream_read_little_endian( istream, value );
@@ -90,6 +97,13 @@ inline uint16_t istream_read_uint16_le( InputStreamType& istream ){
 }
 
 template<typename InputStreamType>
+inline uint16_t istream_read_uint16_be( InputStreamType& istream ){
+       uint16_t value;
+       istream_read_big_endian( istream, value );
+       return value;
+}
+
+template<typename InputStreamType>
 inline int32_t istream_read_int32_le( InputStreamType& istream ){
        int32_t value;
        istream_read_little_endian( istream, value );
@@ -97,6 +111,13 @@ inline int32_t istream_read_int32_le( InputStreamType& istream ){
 }
 
 template<typename InputStreamType>
+inline int32_t istream_read_int32_be( InputStreamType& istream ){
+       int32_t value;
+       istream_read_big_endian( istream, value );
+       return value;
+}
+
+template<typename InputStreamType>
 inline uint32_t istream_read_uint32_le( InputStreamType& istream ){
        uint32_t value;
        istream_read_little_endian( istream, value );
@@ -104,6 +125,13 @@ inline uint32_t istream_read_uint32_le( InputStreamType& istream ){
 }
 
 template<typename InputStreamType>
+inline uint32_t istream_read_uint32_be( InputStreamType& istream ){
+       uint32_t value;
+       istream_read_big_endian( istream, value );
+       return value;
+}
+
+template<typename InputStreamType>
 inline float istream_read_float32_le( InputStreamType& istream ){
        float value;
        istream_read_little_endian( istream, value );
@@ -111,6 +139,13 @@ inline float istream_read_float32_le( InputStreamType& istream ){
 }
 
 template<typename InputStreamType>
+inline float istream_read_float32_be( InputStreamType& istream ){
+       float value;
+       istream_read_big_endian( istream, value );
+       return value;
+}
+
+template<typename InputStreamType>
 inline typename InputStreamType::byte_type istream_read_byte( InputStreamType& istream ){
        typename InputStreamType::byte_type b;
        istream.read( &b, sizeof( typename InputStreamType::byte_type ) );
diff --git a/libs/etclib.c b/libs/etclib.c
new file mode 100644 (file)
index 0000000..09a149e
--- /dev/null
@@ -0,0 +1,114 @@
+// 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 );
+}
diff --git a/libs/etclib.h b/libs/etclib.h
new file mode 100644 (file)
index 0000000..7d24074
--- /dev/null
@@ -0,0 +1,33 @@
+// 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.
+
+#ifndef INCLUDED_ETCLIB_H
+#define INCLUDED_ETCLIB_H
+
+#include "bytebool.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+void ETC_DecodeETC1Block( const byte* in, byte* out, qboolean outRGBA );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
index fcfdd01..4f3e07a 100644 (file)
@@ -29,6 +29,7 @@
 #include "bmp.h"
 #include "pcx.h"
 #include "dds.h"
+#include "ktx.h"
 
 
 #include "modulesystem/singletonmodule.h"
@@ -137,6 +138,26 @@ typedef SingletonModule<ImageDDSAPI, ImageDependencies> ImageDDSModule;
 ImageDDSModule g_ImageDDSModule;
 
 
+class ImageKTXAPI
+{
+_QERPlugImageTable m_imagektx;
+public:
+typedef _QERPlugImageTable Type;
+STRING_CONSTANT( Name, "ktx" );
+
+ImageKTXAPI(){
+       m_imagektx.loadImage = LoadKTX;
+}
+_QERPlugImageTable* getTable(){
+       return &m_imagektx;
+}
+};
+
+typedef SingletonModule<ImageKTXAPI, ImageDependencies> ImageKTXModule;
+
+ImageKTXModule g_ImageKTXModule;
+
+
 extern "C" void RADIANT_DLLEXPORT Radiant_RegisterModules( ModuleServer& server ){
        initialiseModule( server );
 
@@ -145,4 +166,5 @@ extern "C" void RADIANT_DLLEXPORT Radiant_RegisterModules( ModuleServer& server
        g_ImageBMPModule.selfRegister();
        g_ImagePCXModule.selfRegister();
        g_ImageDDSModule.selfRegister();
+       g_ImageKTXModule.selfRegister();
 }
diff --git a/plugins/image/ktx.cpp b/plugins/image/ktx.cpp
new file mode 100644 (file)
index 0000000..582d0b8
--- /dev/null
@@ -0,0 +1,415 @@
+/*
+   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;
+};
+
+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 );
+}
diff --git a/plugins/image/ktx.h b/plugins/image/ktx.h
new file mode 100644 (file)
index 0000000..6070077
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+   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
+ */
+
+#if !defined( INCLUDED_KTX_H )
+#define INCLUDED_KTX_H
+
+class Image;
+class ArchiveFile;
+
+Image* LoadKTX( ArchiveFile& file );
+
+#endif
index c349b49..3a59029 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "inout.h"
 #include "cmdlib.h"
+#include "etclib.h"
 #include "imagelib.h"
 #include "vfs.h"
 
@@ -1232,3 +1233,302 @@ void Load32BitImage( const char *name, unsigned **pixels,  int *width, int *heig
                }
        }
 }
+
+
+/*
+   ============================================================================
+
+   KHRONOS TEXTURE
+
+   ============================================================================
+ */
+
+
+#define KTX_UINT32_LE( buf ) ( ( unsigned int )( (buf)[0] | ( (buf)[1] << 8 ) | ( (buf)[2] << 16 ) | ( (buf)[3] << 24 ) ) )
+#define KTX_UINT32_BE( buf ) ( ( unsigned int )( (buf)[3] | ( (buf)[2] << 8 ) | ( (buf)[1] << 16 ) | ( (buf)[0] << 24 ) ) )
+
+#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
+
+static void KTX_DecodeA8( const byte *in, qboolean bigEndian, byte *out ){
+       out[0] = out[1] = out[2] = 0;
+       out[3] = in[0];
+}
+
+static void KTX_DecodeRGB8( const byte *in, qboolean bigEndian, byte *out ){
+       out[0] = in[0];
+       out[1] = in[1];
+       out[2] = in[2];
+       out[3] = 255;
+}
+
+static void KTX_DecodeRGBA8( const byte *in, qboolean bigEndian, byte *out ){
+       out[0] = in[0];
+       out[1] = in[1];
+       out[2] = in[2];
+       out[3] = in[3];
+}
+
+static void KTX_DecodeL8( const byte *in, qboolean bigEndian, byte *out ){
+       out[0] = out[1] = out[2] = in[0];
+       out[3] = 255;
+}
+
+static void KTX_DecodeLA8( const byte *in, qboolean bigEndian, byte *out ){
+       out[0] = out[1] = out[2] = in[0];
+       out[3] = in[1];
+}
+
+static void KTX_DecodeBGR8( const byte *in, qboolean bigEndian, byte *out ){
+       out[0] = in[2];
+       out[1] = in[1];
+       out[2] = in[0];
+       out[3] = 255;
+}
+
+static void KTX_DecodeBGRA8( const byte *in, qboolean bigEndian, byte *out ){
+       out[0] = in[2];
+       out[1] = in[1];
+       out[2] = in[0];
+       out[3] = in[3];
+}
+
+static void KTX_DecodeRGBA4( const byte *in, qboolean bigEndian, byte *out ){
+       unsigned short rgba;
+       int r, g, b, a;
+
+       if ( bigEndian ) {
+               rgba = ( in[0] << 8 ) | in[1];
+       }
+       else {
+               rgba = ( in[1] << 8 ) | in[0];
+       }
+
+       r = ( rgba >> 12 ) & 0xf;
+       g = ( rgba >> 8 ) & 0xf;
+       b = ( rgba >> 4 ) & 0xf;
+       a = rgba & 0xf;
+       out[0] = ( r << 4 ) | r;
+       out[1] = ( g << 4 ) | g;
+       out[2] = ( b << 4 ) | b;
+       out[3] = ( a << 4 ) | a;
+}
+
+static void KTX_DecodeRGBA5( const byte *in, qboolean bigEndian, byte *out ){
+       unsigned short rgba;
+       int r, g, b;
+
+       if ( bigEndian ) {
+               rgba = ( in[0] << 8 ) | in[1];
+       }
+       else {
+               rgba = ( in[1] << 8 ) | in[0];
+       }
+
+       r = ( rgba >> 11 ) & 0x1f;
+       g = ( rgba >> 6 ) & 0x1f;
+       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;
+}
+
+static void KTX_DecodeRGB5( const byte *in, qboolean bigEndian, byte *out ){
+       unsigned short rgba;
+       int r, g, b;
+
+       if ( bigEndian ) {
+               rgba = ( in[0] << 8 ) | in[1];
+       }
+       else {
+               rgba = ( in[1] << 8 ) | in[0];
+       }
+
+       r = ( rgba >> 11 ) & 0x1f;
+       g = ( rgba >> 5 ) & 0x3f;
+       b = rgba & 0x1f;
+       out[0] = ( r << 3 ) | ( r >> 2 );
+       out[1] = ( g << 2 ) | ( g >> 4 );
+       out[2] = ( b << 3 ) | ( b >> 2 );
+       out[3] = 255;
+}
+
+typedef struct
+{
+       unsigned int type;
+       unsigned int format;
+       unsigned int pixelSize;
+       void ( *decode )( const byte *in, qboolean bigEndian, byte *out );
+} KTX_UncompressedFormat_t;
+
+static const KTX_UncompressedFormat_t KTX_UncompressedFormats[] =
+{
+       { KTX_TYPE_UNSIGNED_BYTE, KTX_FORMAT_ALPHA, 1, KTX_DecodeA8 },
+       { KTX_TYPE_UNSIGNED_BYTE, KTX_FORMAT_RGB, 3, KTX_DecodeRGB8 },
+       { KTX_TYPE_UNSIGNED_BYTE, KTX_FORMAT_RGBA, 4, KTX_DecodeRGBA8 },
+       { KTX_TYPE_UNSIGNED_BYTE, KTX_FORMAT_LUMINANCE, 1, KTX_DecodeL8 },
+       { KTX_TYPE_UNSIGNED_BYTE, KTX_FORMAT_LUMINANCE_ALPHA, 2, KTX_DecodeLA8 },
+       { KTX_TYPE_UNSIGNED_BYTE, KTX_FORMAT_BGR, 3, KTX_DecodeBGR8 },
+       { KTX_TYPE_UNSIGNED_BYTE, KTX_FORMAT_BGRA, 4, KTX_DecodeBGRA8 },
+       { KTX_TYPE_UNSIGNED_SHORT_4_4_4_4, KTX_FORMAT_RGBA, 2, KTX_DecodeRGBA4 },
+       { KTX_TYPE_UNSIGNED_SHORT_5_5_5_1, KTX_FORMAT_RGBA, 2, KTX_DecodeRGBA5 },
+       { KTX_TYPE_UNSIGNED_SHORT_5_6_5, KTX_FORMAT_RGB, 2, KTX_DecodeRGB5 },
+       { 0, 0, 0, NULL }
+};
+
+static qboolean KTX_DecodeETC1( const byte* in, size_t inSize, unsigned int width, unsigned int height, byte* out ){
+       unsigned int y, stride = width * 4;
+       byte rgba[64];
+
+       if ( inSize < ( ( ( ( width + 3 ) & ~3 ) * ( ( height + 3 ) & ~3 ) ) >> 1 ) ) {
+               return qfalse;
+       }
+
+       for ( y = 0; y < height; y += 4, out += stride * 4 )
+       {
+               byte *p;
+               unsigned int x, blockrows;
+
+               blockrows = height - y;
+               if ( blockrows > 4 ) {
+                       blockrows = 4;
+               }
+
+               p = out;
+               for ( x = 0; x < width; x += 4, p += 16 )
+               {
+                       unsigned int blockrowsize, blockrow;
+
+                       ETC_DecodeETC1Block( in, rgba, qtrue );
+                       in += 8;
+
+                       blockrowsize = width - x;
+                       if ( blockrowsize > 4 ) {
+                               blockrowsize = 4;
+                       }
+                       blockrowsize *= 4;
+                       for ( blockrow = 0; blockrow < blockrows; blockrow++ )
+                       {
+                               memcpy( p + blockrow * stride, rgba + blockrow * 16, blockrowsize );
+                       }
+               }
+       }
+
+       return qtrue;
+}
+
+#define KTX_HEADER_UINT32( buf ) ( bigEndian ? KTX_UINT32_BE( buf ) : KTX_UINT32_LE( buf ) )
+
+void LoadKTXBufferFirstImage( const byte *buffer, size_t bufSize, byte **pic, int *picWidth, int *picHeight ){
+       unsigned int type, format, width, height, imageOffset;
+       byte *pixels;
+
+       if ( bufSize < 64 ) {
+               Error( "LoadKTX: Image doesn't have a header" );
+       }
+
+       if ( memcmp( buffer, "\xABKTX 11\xBB\r\n\x1A\n", 12 ) ) {
+               Error( "LoadKTX: Image has the wrong identifier" );
+       }
+
+       qboolean bigEndian = ( buffer[4] == 4 );
+
+       type = KTX_HEADER_UINT32( buffer + 16 );
+       if ( type ) {
+               format = KTX_HEADER_UINT32( buffer + 32 );
+       }
+       else {
+               format = KTX_HEADER_UINT32( buffer + 28 );
+       }
+
+       width = KTX_HEADER_UINT32( buffer + 36 );
+       height = KTX_HEADER_UINT32( buffer + 40 );
+       if ( !width ) {
+               Error( "LoadKTX: Image has zero width" );
+       }
+       if ( !height ) {
+               height = 1;
+       }
+       if ( picWidth ) {
+               *picWidth = width;
+       }
+       if ( picHeight ) {
+               *picHeight = height;
+       }
+
+       imageOffset = 64 + KTX_HEADER_UINT32( buffer + 60 ) + 4;
+       if ( bufSize < imageOffset ) {
+               Error( "LoadKTX: No image in the file" );
+       }
+       buffer += imageOffset;
+       bufSize -= imageOffset;
+
+       pixels = safe_malloc( width * height * 4 );
+       *pic = pixels;
+
+       if ( type ) {
+               const KTX_UncompressedFormat_t *ktxFormat = KTX_UncompressedFormats;
+               unsigned int pixelSize;
+               unsigned int inRowLength, inPadding;
+               unsigned int y;
+
+               while ( ktxFormat->type )
+               {
+                       if ( ktxFormat->type == type && ktxFormat->format == format ) {
+                               break;
+                       }
+                       ktxFormat++;
+               }
+               if ( !ktxFormat->type ) {
+                       Error( "LoadKTX: Image has an unsupported pixel type 0x%X or format 0x%X", type, format );
+               }
+
+               pixelSize = ktxFormat->pixelSize;
+               inRowLength = width * pixelSize;
+               inPadding = ( ( inRowLength + 3 ) & ~3 ) - inRowLength;
+
+               if ( bufSize < height * ( inRowLength + inPadding ) ) {
+                       Error( "LoadKTX: Image is truncated" );
+               }
+
+               for ( y = 0; y < height; y++ )
+               {
+                       unsigned int x;
+                       for ( x = 0; x < width; x++, buffer += pixelSize, pixels += 4 )
+                       {
+                               ktxFormat->decode( buffer, bigEndian, pixels );
+                       }
+                       buffer += inPadding;
+               }
+       }
+       else {
+               qboolean decoded = qfalse;
+
+               switch ( format )
+               {
+               case KTX_FORMAT_ETC1_RGB8:
+                       decoded = KTX_DecodeETC1( buffer, bufSize, width, height, pixels );
+                       break;
+               default:
+                       Error( "LoadKTX: Image has an unsupported compressed format format 0x%X", format );
+                       break;
+               }
+
+               if ( !decoded ) {
+                       Error( "LoadKTX: Image is truncated" );
+               }
+       }
+}
index 194ac5b..8c00f7c 100644 (file)
@@ -43,3 +43,5 @@ void WriteTGAGray( const char *filename, byte *data, int width, int height );
 int LoadJPGBuff( void *src_buffer, int src_size, unsigned char **pic, int *width, int *height );
 
 void Load32BitImage( const char *name, unsigned **pixels, int *width, int *height );
+
+void LoadKTXBufferFirstImage( const byte *buffer, size_t bufSize, byte **pic, int *picWidth, int *picHeight );
index 60062a1..c1c737f 100644 (file)
@@ -430,6 +430,16 @@ image_t *ImageLoad( const char *filename ){
                                        }
                                        #endif
                                }
+                               else
+                               {
+                                       /* attempt to load ktx */
+                                       StripExtension( name );
+                                       strcat( name, ".ktx" );
+                                       size = vfsLoadFile( (const char*) name, (void**) &buffer, 0 );
+                                       if ( size > 0 ) {
+                                               LoadKTXBufferFirstImage( buffer, size, &image->pixels, &image->width, &image->height );
+                                       }
+                               }
                        }
                }
        }