summaryrefslogtreecommitdiff
path: root/Runtime/Graphics/S3Decompression.cpp
diff options
context:
space:
mode:
authorchai <chaifix@163.com>2019-08-14 22:50:43 +0800
committerchai <chaifix@163.com>2019-08-14 22:50:43 +0800
commit15740faf9fe9fe4be08965098bbf2947e096aeeb (patch)
treea730ec236656cc8cab5b13f088adfaed6bb218fb /Runtime/Graphics/S3Decompression.cpp
+Unity Runtime codeHEADmaster
Diffstat (limited to 'Runtime/Graphics/S3Decompression.cpp')
-rw-r--r--Runtime/Graphics/S3Decompression.cpp1882
1 files changed, 1882 insertions, 0 deletions
diff --git a/Runtime/Graphics/S3Decompression.cpp b/Runtime/Graphics/S3Decompression.cpp
new file mode 100644
index 0000000..b2f518d
--- /dev/null
+++ b/Runtime/Graphics/S3Decompression.cpp
@@ -0,0 +1,1882 @@
+#include "UnityPrefix.h"
+#include "S3Decompression.h"
+#include "Runtime/Utilities/LogAssert.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/Graphics/Image.h"
+#include "Runtime/Graphics/FlashATFDecompression.h"
+#include "Runtime/Graphics/ETC2Decompression.h"
+
+#define PRINT_DECOMPRESSION_TIMES 0
+#if PRINT_DECOMPRESSION_TIMES
+#include "Runtime/Input/TimeManager.h"
+#endif
+
+
+// \todo [2010-02-09 petri] Define in some config file?
+#define HAS_ETC_DECOMPRESSOR (UNITY_ANDROID && !i386 || UNITY_EDITOR || UNITY_BB10 || UNITY_TIZEN)
+#define HAS_ATC_DECOMPRESSOR (UNITY_ANDROID && !i386 || UNITY_EDITOR)
+
+#define HAS_ASTC_DECOMPRESSOR (UNITY_ANDROID || UNITY_EDITOR)
+
+#if HAS_ATC_DECOMPRESSOR
+ #include "External/Qualcomm_TextureConverter/TextureConverter.h"
+ #if UNITY_WIN
+ #include <process.h>
+ // On Windows, Qonvert is compiled with MSVCRT, but we link against LIBCMT; filling the gaps here
+ extern "C" int _imp___getpid(void){ return _getpid(); }
+ #elif UNITY_OSX
+ // On OSX, Qonvert is compiled with -fstack-protector, but we don't use it for the MacEditor
+ extern "C"
+ {
+ void* __stack_chk_guard = (void*)0xdeadc0de;
+ void __stack_chk_fail() { ErrorString("stack_chk_fail failed"); }
+ }
+ #elif defined(ARM_ARCH_VFP)
+ // On Android, Qonvert is compiled with armv5 (no vfp), but we don't want to include the softfp emulation
+ extern "C" unsigned __aeabi_f2uiz(float f){return (int)f;}
+ #endif
+#endif
+
+// ------------------------------------------------------------------------
+// DXT
+
+
+#if HAS_DXT_DECOMPRESSOR
+
+struct DXTColBlock
+{
+ UInt16 col0;
+ UInt16 col1;
+ UInt8 row[4];
+};
+
+struct DXTAlphaBlockExplicit
+{
+ UInt16 row[4];
+};
+
+struct DXTAlphaBlock3BitLinear
+{
+ UInt8 alpha0;
+ UInt8 alpha1;
+ UInt8 stuff[6];
+};
+
+#if UNITY_BIG_ENDIAN
+
+struct Color8888
+{
+ UInt8 b;
+ UInt8 g;
+ UInt8 r;
+ UInt8 a;
+};
+
+static inline UInt16 GetByteSwap16( UInt16 i )
+{
+ return static_cast<UInt16>((i << 8) | (i >> 8));
+}
+
+static inline UInt32 GetByteSwap32( UInt32 i )
+{
+ return static_cast<UInt32>((i >> 24) | (i >> 8) & 0x0000ff00 | (i << 8) & 0x00ff0000 | (i << 24));
+}
+
+struct Color565
+{
+ unsigned b : 5;
+ unsigned g : 6;
+ unsigned r : 5;
+};
+
+#else
+
+static inline UInt16 GetByteSwap16( UInt16 i ) { return i; }
+static inline UInt32 GetByteSwap32( UInt32 i ) { return i; }
+
+struct Color8888 // EH? This seems to be wrong!
+{
+ UInt8 r;
+ UInt8 g;
+ UInt8 b;
+ UInt8 a;
+};
+
+struct Color565
+{
+ unsigned b : 5;
+ unsigned g : 6;
+ unsigned r : 5;
+};
+
+#endif
+
+
+inline void GetColorBlockColors( const DXTColBlock* block, Color8888 colors[4] )
+{
+ union
+ {
+ Color565 color565;
+ UInt16 color16;
+ } col0, col1;
+ col0.color16 = GetByteSwap16( block->col0 );
+ col1.color16 = GetByteSwap16( block->col1 );
+
+ const Color565* col;
+
+ // It's not enough just to shift bits to full 8 bit precision - the lower bits
+ // must also be filled to match the way hardware does the rounding.
+ col = &col0.color565;
+ colors[0].r = ( col->r << 3 ) | ( col->r >> 2 );
+ colors[0].g = ( col->g << 2 ) | ( col->g >> 4 );
+ colors[0].b = ( col->b << 3 ) | ( col->b >> 2 );
+ colors[0].a = 0xff;
+
+ col = &col1.color565;
+ colors[1].r = ( col->r << 3 ) | ( col->r >> 2 );
+ colors[1].g = ( col->g << 2 ) | ( col->g >> 4 );
+ colors[1].b = ( col->b << 3 ) | ( col->b >> 2 );
+ colors[1].a = 0xff;
+
+ if( col0.color16 > col1.color16 )
+ {
+ // Four-color block: derive the other two colors.
+ // 00 = color_0, 01 = color_1, 10 = color_2, 11 = color_3
+ // These two bit codes correspond to the 2-bit fields
+ // stored in the 64-bit block.
+
+ colors[2].r = (UInt8)(((UInt16)colors[0].r * 2 + (UInt16)colors[1].r )/3);
+ colors[2].g = (UInt8)(((UInt16)colors[0].g * 2 + (UInt16)colors[1].g )/3);
+ colors[2].b = (UInt8)(((UInt16)colors[0].b * 2 + (UInt16)colors[1].b )/3);
+ colors[2].a = 0xff;
+
+ colors[3].r = (UInt8)(((UInt16)colors[0].r + (UInt16)colors[1].r *2 )/3);
+ colors[3].g = (UInt8)(((UInt16)colors[0].g + (UInt16)colors[1].g *2 )/3);
+ colors[3].b = (UInt8)(((UInt16)colors[0].b + (UInt16)colors[1].b *2 )/3);
+ colors[3].a = 0xff;
+ }
+ else
+ {
+ // Three-color block: derive the other color.
+ // 00 = color_0, 01 = color_1, 10 = color_2, 11 = transparent.
+ // These two bit codes correspond to the 2-bit fields
+ // stored in the 64-bit block.
+
+ colors[2].r = (UInt8)(((UInt16)colors[0].r + (UInt16)colors[1].r )/2);
+ colors[2].g = (UInt8)(((UInt16)colors[0].g + (UInt16)colors[1].g )/2);
+ colors[2].b = (UInt8)(((UInt16)colors[0].b + (UInt16)colors[1].b )/2);
+ colors[2].a = 0xff;
+
+ // set transparent to black to match DXT specs
+ colors[3].r = 0x00;
+ colors[3].g = 0x00;
+ colors[3].b = 0x00;
+ colors[3].a = 0x00;
+ }
+}
+
+// width is width of destination image in pixels
+inline void DecodeColorBlock( UInt32* dest, const DXTColBlock& colorBlock, int width, const UInt32 colors[4] )
+{
+ // r steps through lines in y
+ for( int r=0; r < 4; r++, dest += width-4 )
+ {
+ // width * 4 bytes per pixel per line
+ // each j block row is 4 lines of pixels
+
+ // Do four pixels, step in twos because n is only used for the shift
+ // n steps through pixels
+ for( int n = 0; n < 8; n += 2 )
+ {
+ UInt32 bits = (colorBlock.row[r] >> n) & 3;
+ DebugAssert (bits <= 3);
+ *dest = colors[bits];
+ ++dest;
+ }
+ }
+}
+
+inline void DecodeAlphaExplicit( UInt32* dest, const DXTAlphaBlockExplicit& alphaBlock, int width, UInt32 alphazero )
+{
+ // alphazero is a bit mask that when ANDed with the image color
+ // will zero the alpha bits, so if the image DWORDs are
+ // ARGB then alphazero will be 0x00FFFFFF or if
+ // RGBA then alphazero will be 0xFFFFFF00
+ // alphazero constructed automatically from field order of Color8888 structure
+
+ union
+ {
+ Color8888 col;
+ UInt32 col32;
+ } u;
+ u.col.r = u.col.g = u.col.b = 0;
+
+ for( int row=0; row < 4; row++, dest += width-4 )
+ {
+ UInt16 wrd = GetByteSwap16( alphaBlock.row[ row ] );
+
+ for( int pix = 0; pix < 4; ++pix )
+ {
+ // zero the alpha bits of image pixel
+ *dest &= alphazero;
+
+ u.col.a = wrd & 0x000f; // get lowest 4 bits
+ u.col.a = u.col.a | (u.col.a << 4);
+
+ *dest |= u.col32; // OR into the previously nulled alpha
+
+ wrd >>= 4;
+ ++dest;
+ }
+ }
+}
+
+inline void DecodeAlpha3BitLinear( UInt32* dest, const DXTAlphaBlock3BitLinear& alphaBlock, int width, UInt32 alphazero )
+{
+ union
+ {
+ Color8888 alphaCol[4][4];
+ UInt32 alphaCol32[4][4];
+ } u;
+ UInt32 alphamask = ~alphazero;
+ UInt16 alphas[8];
+
+ alphas[0] = alphaBlock.alpha0;
+ alphas[1] = alphaBlock.alpha1;
+
+ // 8-alpha or 6-alpha block?
+ if( alphas[0] > alphas[1] )
+ {
+ // 8-alpha block: derive the other 6 alphas.
+ // 000 = alpha[0], 001 = alpha[1], others are interpolated
+
+ alphas[2] = ( 6 * alphas[0] + alphas[1] + 3) / 7; // bit code 010
+ alphas[3] = ( 5 * alphas[0] + 2 * alphas[1] + 3) / 7; // Bit code 011
+ alphas[4] = ( 4 * alphas[0] + 3 * alphas[1] + 3) / 7; // Bit code 100
+ alphas[5] = ( 3 * alphas[0] + 4 * alphas[1] + 3) / 7; // Bit code 101
+ alphas[6] = ( 2 * alphas[0] + 5 * alphas[1] + 3) / 7; // Bit code 110
+ alphas[7] = ( alphas[0] + 6 * alphas[1] + 3) / 7; // Bit code 111
+ }
+ else
+ {
+ // 6-alpha block: derive the other alphas.
+ // 000 = alpha[0], 001 = alpha[1], others are interpolated
+
+ alphas[2] = (4 * alphas[0] + alphas[1] + 2) / 5; // Bit code 010
+ alphas[3] = (3 * alphas[0] + 2 * alphas[1] + 2) / 5; // Bit code 011
+ alphas[4] = (2 * alphas[0] + 3 * alphas[1] + 2) / 5; // Bit code 100
+ alphas[5] = ( alphas[0] + 4 * alphas[1] + 2) / 5; // Bit code 101
+ alphas[6] = 0; // Bit code 110
+ alphas[7] = 255; // Bit code 111
+ }
+
+ // Decode 3-bit fields into array of 16 bytes with same value
+ UInt8 blockBits[4][4];
+
+ // first two rows of 4 pixels each:
+ const UInt32 mask = 7; // three bits
+
+ // TBD: Ouch! Unaligned reads there! Poor old PPC!
+
+ UInt32 bits = GetByteSwap32( *(UInt32*)&alphaBlock.stuff[0] ); // first 3 bytes
+
+ blockBits[0][0] = (UInt8)( bits & mask );
+ bits >>= 3;
+ blockBits[0][1] = (UInt8)( bits & mask );
+ bits >>= 3;
+ blockBits[0][2] = (UInt8)( bits & mask );
+ bits >>= 3;
+ blockBits[0][3] = (UInt8)( bits & mask );
+ bits >>= 3;
+ blockBits[1][0] = (UInt8)( bits & mask );
+ bits >>= 3;
+ blockBits[1][1] = (UInt8)( bits & mask );
+ bits >>= 3;
+ blockBits[1][2] = (UInt8)( bits & mask );
+ bits >>= 3;
+ blockBits[1][3] = (UInt8)( bits & mask );
+
+ // now last two rows
+ // it's ok to fetch one byte too much because there will always be color block after alpha
+ bits = GetByteSwap32( *(UInt32*)&alphaBlock.stuff[3] ); // last 3 bytes
+
+ blockBits[2][0] = (UInt8)( bits & mask );
+ bits >>= 3;
+ blockBits[2][1] = (UInt8)( bits & mask );
+ bits >>= 3;
+ blockBits[2][2] = (UInt8)( bits & mask );
+ bits >>= 3;
+ blockBits[2][3] = (UInt8)( bits & mask );
+ bits >>= 3;
+ blockBits[3][0] = (UInt8)( bits & mask );
+ bits >>= 3;
+ blockBits[3][1] = (UInt8)( bits & mask );
+ bits >>= 3;
+ blockBits[3][2] = (UInt8)( bits & mask );
+ bits >>= 3;
+ blockBits[3][3] = (UInt8)( bits & mask );
+
+ // decode the codes into alpha values
+ int row, pix;
+
+ for( row = 0; row < 4; row++ )
+ {
+ for( pix=0; pix < 4; pix++ )
+ {
+ u.alphaCol[row][pix].a = (UInt8) alphas[ blockBits[row][pix] ];
+ }
+ }
+
+ // Write out alpha values to the image bits
+ for( row=0; row < 4; row++, dest += width-4 )
+ {
+ for( pix = 0; pix < 4; pix++ )
+ {
+ *dest &= alphazero; // zero the alpha bits of image pixel
+ *dest |= u.alphaCol32[row][pix] & alphamask; // or the bits into the prev. nulled alpha
+ dest++;
+ }
+ }
+}
+
+
+void DecompressDXT1( int xblocks, int yblocks, int destWidth, const UInt32* m_pCompBytes, UInt32* decompBytes )
+{
+ union
+ {
+ Color8888 colors8888[4];
+ UInt32 colors32[4];
+ } u;
+
+ for( int j = 0; j < yblocks; ++j )
+ {
+ const DXTColBlock* block = (const DXTColBlock*)( m_pCompBytes + j * xblocks * 2 ); // 8 bytes per block
+
+ for( int i = 0; i < xblocks; ++i, ++block )
+ {
+ GetColorBlockColors( block, u.colors8888 );
+ UInt32* dest = (UInt32*)((UInt8*)decompBytes + i*16 + (j*4) * destWidth * 4 );
+ DecodeColorBlock( dest, *block, destWidth, u.colors32 );
+ }
+ }
+}
+
+
+void DecompressDXT3( int xblocks, int yblocks, int destWidth, const UInt32* m_pCompBytes, UInt32* decompBytes )
+{
+ union
+ {
+ Color8888 colors[4];
+ UInt32 colors32[4];
+ } u;
+
+ // fill alphazero with appropriate value to zero out alpha when
+ // alphazero is ANDed with the image color
+ u.colors[0].a = 0;
+ u.colors[0].r = u.colors[0].g = u.colors[0].b = 0xff;
+ UInt32 alphazero = u.colors32[0];
+
+ for( int j = 0; j < yblocks; ++j )
+ {
+ const DXTColBlock* block = (const DXTColBlock*)( m_pCompBytes + j * xblocks * 4 ); // 16 bytes for alpha+color block
+
+ for( int i = 0; i < xblocks; ++i, ++block )
+ {
+ // Get alpha block
+ const DXTAlphaBlockExplicit* alphaBlock = (const DXTAlphaBlockExplicit*)block;
+ ++block;
+
+ // Get color block & colors
+ GetColorBlockColors( block, u.colors );
+
+ // Decode the color block into the bitmap bits
+ UInt32* dest = (UInt32*)((UInt8*)decompBytes + i*16 + (j*4) * destWidth * 4 );
+ DecodeColorBlock( dest, *block, destWidth, u.colors32 );
+
+ // Overwrite the previous alpha bits with the alpha block info
+ DecodeAlphaExplicit( dest, *alphaBlock, destWidth, alphazero );
+ }
+ }
+}
+
+
+void DecompressDXT5( int xblocks, int yblocks, int destWidth, const UInt32* m_pCompBytes, UInt32* decompBytes )
+{
+ union
+ {
+ Color8888 colors[4];
+ UInt32 colors32[4];
+ } u;
+
+ // fill alphazero with appropriate value to zero out alpha when
+ // alphazero is ANDed with the image color 32 bit
+ u.colors[0].a = 0;
+ u.colors[0].r = u.colors[0].g = u.colors[0].b = 0xff;
+ UInt32 alphazero = u.colors32[0];
+
+ for( int j = 0; j < yblocks; ++j )
+ {
+ const DXTColBlock* block = (const DXTColBlock*) ( m_pCompBytes + j * xblocks * 4 ); // 16 bytes for alpha+color block
+
+ for( int i = 0; i < xblocks; ++i, ++block )
+ {
+ // Get alpha block
+ const DXTAlphaBlock3BitLinear* alphaBlock = (const DXTAlphaBlock3BitLinear*)block;
+ block++;
+
+ // Get color block & colors
+ GetColorBlockColors( block, u.colors );
+
+ // Decode the color block into the bitmap bits
+ UInt32* dest = (UInt32*)((UInt8*)decompBytes + i*16 + (j*4) * destWidth * 4 );
+ DecodeColorBlock( dest, *block, destWidth, u.colors32 );
+
+ // Overwrite the previous alpha bits with the alpha block info
+ DecodeAlpha3BitLinear( dest, *alphaBlock, destWidth, alphazero );
+ }
+ }
+}
+
+#endif
+
+
+
+// ------------------------------------------------------------------------
+// PVRTC
+
+
+
+#if HAS_PVRTC_DECOMPRESSOR
+
+const int kPVRSizeX2 = 8; // block width 8 pixels in 2BPP case
+const int kPVRSizeX4 = 4; // block width 4 pixels in 4BPP case
+const int kPVRBlockSizeY = 4; // block height always 4 pixels
+
+const int kPVRPunchThroughIndex = 2;
+
+#define PVR_WRAP_POT_COORD(Val, Size) ((Val) & ((Size)-1))
+
+#define CLAMP(X, lower, upper) (std::min(std::max((X),(lower)), (upper)))
+
+#define PVR_LIMIT_COORD(Val, Size, AssumeImageTiles) \
+ ((AssumeImageTiles) ? PVR_WRAP_POT_COORD((Val), (Size)) : CLAMP((Val), 0, (Size)-1))
+
+// 64 bits per PVRTC block
+struct PVRTCBlock
+{
+ UInt32 packedData[2];
+};
+
+
+
+// Given a block, extract the color information and convert to 5554 formats
+static void Unpack5554Colour (const PVRTCBlock *pBlock, int ABColours[2][4])
+{
+ UInt32 RawBits[2];
+
+ int i;
+
+ /*
+ // Extract A and B
+ */
+ RawBits[0] = pBlock->packedData[1] & (0xFFFE); /*15 bits (shifted up by one)*/
+ RawBits[1] = pBlock->packedData[1] >> 16; /*16 bits*/
+
+ /*
+ //step through both colours
+ */
+ for(i = 0; i < 2; i++)
+ {
+ /*
+ // if completely opaque
+ */
+ if(RawBits[i] & (1<<15))
+ {
+ /*
+ // Extract R and G (both 5 bit)
+ */
+ ABColours[i][0] = (RawBits[i] >> 10) & 0x1F;
+ ABColours[i][1] = (RawBits[i] >> 5) & 0x1F;
+
+ /*
+ // The precision of Blue depends on A or B. If A then we need to
+ // replicate the top bit to get 5 bits in total
+ */
+ ABColours[i][2] = RawBits[i] & 0x1F;
+ if(i==0)
+ {
+ ABColours[0][2] |= ABColours[0][2] >> 4;
+ }
+
+ /*
+ // set 4bit alpha fully on...
+ */
+ ABColours[i][3] = 0xF;
+ }
+ /*
+ // Else if colour has variable translucency
+ */
+ else
+ {
+ /*
+ // Extract R and G (both 4 bit).
+ // (Leave a space on the end for the replication of bits
+ */
+ ABColours[i][0] = (RawBits[i] >> (8-1)) & 0x1E;
+ ABColours[i][1] = (RawBits[i] >> (4-1)) & 0x1E;
+
+ /*
+ // replicate bits to truly expand to 5 bits
+ */
+ ABColours[i][0] |= ABColours[i][0] >> 4;
+ ABColours[i][1] |= ABColours[i][1] >> 4;
+
+ /*
+ // grab the 3(+padding) or 4 bits of blue and add an extra padding bit
+ */
+ ABColours[i][2] = (RawBits[i] & 0xF) << 1;
+
+ /*
+ // expand from 3 to 5 bits if this is from colour A, or 4 to 5 bits if from
+ // colour B
+ */
+ if(i==0)
+ {
+ ABColours[0][2] |= ABColours[0][2] >> 3;
+ }
+ else
+ {
+ ABColours[0][2] |= ABColours[0][2] >> 4;
+ }
+
+ /*
+ // Set the alpha bits to be 3 + a zero on the end
+ */
+ ABColours[i][3] = (RawBits[i] >> 11) & 0xE;
+ }/*end if variable alpha*/
+ }/*end for i*/
+
+}
+
+
+// Given the block and the texture type and it's relative position in the
+// 2x2 group of blocks, extract the bit patterns for the fully defined pixels.
+template<bool Do2bitMode>
+static void UnpackModulations(const PVRTCBlock *pBlock,
+ int ModulationVals[8][16],
+ int ModulationModes[8][16],
+ int StartX,
+ int StartY)
+{
+ int BlockModMode= pBlock->packedData[1] & 1;
+ UInt32 ModulationBits = pBlock->packedData[0];
+
+ // if it's in an interpolated mode
+ if(Do2bitMode && BlockModMode)
+ {
+ // run through all the pixels in the block. Note we can now treat all the
+ // "stored" values as if they have 2bits (even when they didn't!)
+ for(int y = 0; y < kPVRBlockSizeY; y++)
+ {
+ for(int x = 0; x < kPVRSizeX2; x++)
+ {
+ ModulationModes[y+StartY][x+StartX] = BlockModMode;
+
+ // if this is a stored value...
+ if(((x^y)&1) == 0)
+ {
+ ModulationVals[y+StartY][x+StartX] = ModulationBits & 3;
+ ModulationBits >>= 2;
+ }
+ }
+ }
+ }
+ // else if direct encoded 2bit mode - i.e. 1 mode bit per pixel
+ else if(Do2bitMode)
+ {
+ for(int y = 0; y < kPVRBlockSizeY; y++)
+ {
+ for(int x = 0; x < kPVRSizeX2; x++)
+ {
+ ModulationModes[y+StartY][x+StartX] = BlockModMode;
+
+ // double the bits so 0=> 00, and 1=>11
+ if(ModulationBits & 1)
+ {
+ ModulationVals[y+StartY][x+StartX] = 0x3;
+ }
+ else
+ {
+ ModulationVals[y+StartY][x+StartX] = 0x0;
+ }
+ ModulationBits >>= 1;
+ }
+ }
+ }
+ // else its the 4bpp mode so each value has 2 bits
+ else
+ {
+ for(int y = 0; y < kPVRBlockSizeY; y++)
+ {
+ for(int x = 0; x < kPVRSizeX4; x++)
+ {
+ ModulationModes[y+StartY][x+StartX] = BlockModMode;
+ ModulationVals[y+StartY][x+StartX] = ModulationBits & 3;
+ ModulationBits >>= 2;
+ }
+ }
+ }
+
+ // make sure nothing is left over
+ DebugAssert(ModulationBits==0);
+}
+
+
+template<bool do2bitMode>
+static int GetUCoordPVR (int x)
+{
+ if (do2bitMode)
+ {
+ int u = (x & 0x7) | ((~x & 0x4) << 1);
+ return u - kPVRSizeX2/2;
+ }
+ else
+ {
+ int u = (x & 0x3) | ((~x & 0x2) << 1);
+ return u - kPVRSizeX4/2;
+ }
+}
+
+static int GetVCoordPVR (int y)
+{
+ int v = (y & 0x3) | ((~y & 0x2) << 1);
+ return v - kPVRBlockSizeY/2;
+}
+
+
+// Performs a HW bit accurate interpolation of either the
+// A or B colors for a particular pixel
+//
+// NOTE: It is assumed that the source colors are in ARGB 5554 format -
+// This means that some "preparation" of the values will be necessary.
+// NOTE: QP is Q-P, SR is S-R
+template<bool Do2bitMode>
+static void InterpolateColoursPVRTC(const int* __restrict P,
+ const int* __restrict QP,
+ const int* __restrict R,
+ const int* __restrict SR,
+ const int u,
+ const int v,
+ int* __restrict Result)
+{
+ int k;
+ int tmp1, tmp2;
+
+ int uscale = Do2bitMode ? 8 : 4;
+
+ for(k = 0; k < 4; k++)
+ {
+ tmp1 = P[k] * uscale + u * QP[k];
+ tmp2 = R[k] * uscale + u * SR[k];
+
+ tmp1 = tmp1 * 4 + v * (tmp2 - tmp1);
+
+ Result[k] = tmp1;
+ }
+
+ /*
+ // Lop off the appropriate number of bits to get us to 8 bit precision
+ */
+ if(Do2bitMode)
+ {
+ /*
+ // do RGB
+ */
+ for(k = 0; k < 3; k++)
+ {
+ Result[k] >>= 2;
+ }
+
+ Result[3] >>= 1;
+ }
+ else
+ {
+ /*
+ // do RGB (A is ok)
+ */
+ for(k = 0; k < 3; k++)
+ {
+ Result[k] >>= 1;
+ }
+ }
+
+ /*
+ // sanity check
+ */
+ for(k = 0; k < 4; k++)
+ {
+ DebugAssert(Result[k] < 256);
+ }
+
+
+ /*
+ // Convert from 5554 to 8888
+ //
+ // do RGB 5.3 => 8
+ */
+ for(k = 0; k < 3; k++)
+ {
+ Result[k] += Result[k] >> 5;
+ }
+ Result[3] += Result[3] >> 4;
+
+ /*
+ // 2nd sanity check
+ */
+ for(k = 0; k < 4; k++)
+ {
+ DebugAssert(Result[k] < 256);
+ }
+
+}
+
+
+// Get the modulation value as a numerator of a fraction of 8ths
+template<bool Do2bitMode>
+static void GetModulationValue(int x,
+ int y,
+ const int ModulationVals[8][16],
+ const int ModulationModes[8][16],
+ int *Mod,
+ int *DoPT)
+{
+ static const int RepVals0[4] = {0, 3, 5, 8};
+ static const int RepVals1[4] = {0, 4, 4, 8};
+
+ int ModVal;
+
+ /*
+ // Map X and Y into the local 2x2 block
+ */
+ y = (y & 0x3) | ((~y & 0x2) << 1);
+ if(Do2bitMode)
+ {
+ x = (x & 0x7) | ((~x & 0x4) << 1);
+
+ }
+ else
+ {
+ x = (x & 0x3) | ((~x & 0x2) << 1);
+ }
+
+ /*
+ // assume no PT for now
+ */
+ *DoPT = 0;
+
+ /*
+ // extract the modulation value. If a simple encoding
+ */
+ if(ModulationModes[y][x]==0)
+ {
+ ModVal = RepVals0[ModulationVals[y][x]];
+ }
+ else if(Do2bitMode)
+ {
+ /*
+ // if this is a stored value
+ */
+ if(((x^y)&1)==0)
+ {
+ ModVal = RepVals0[ModulationVals[y][x]];
+ }
+ /*
+ // else average from the neighbours
+ //
+ // if H&V interpolation...
+ */
+ else if(ModulationModes[y][x] == 1)
+ {
+ ModVal = (RepVals0[ModulationVals[y-1][x]] +
+ RepVals0[ModulationVals[y+1][x]] +
+ RepVals0[ModulationVals[y][x-1]] +
+ RepVals0[ModulationVals[y][x+1]] + 2) / 4;
+ }
+ /*
+ // else if H-Only
+ */
+ else if(ModulationModes[y][x] == 2)
+ {
+ ModVal = (RepVals0[ModulationVals[y][x-1]] +
+ RepVals0[ModulationVals[y][x+1]] + 1) / 2;
+ }
+ /*
+ // else it's V-Only
+ */
+ else
+ {
+ ModVal = (RepVals0[ModulationVals[y-1][x]] +
+ RepVals0[ModulationVals[y+1][x]] + 1) / 2;
+
+ }/*end if/then/else*/
+ }
+ /*
+ // else it's 4BPP and PT encoding
+ */
+ else
+ {
+ ModVal = RepVals1[ModulationVals[y][x]];
+
+ *DoPT = ModulationVals[y][x] == kPVRPunchThroughIndex;
+ }
+
+ *Mod =ModVal;
+}
+
+
+
+// PVRTC UV twiddling interleaves bits of Y & X, like: XYXYXYXYXY.
+// If size of one coordinate is larger, those bits are just copied into higher bits;
+// e.g. XXXX,YYYYYYYY = YYYYXYXYXYXY.
+
+static UInt32 TwiddleY_PVRTC(UInt32 YSize, UInt32 XSize, UInt32 YPos)
+{
+ UInt32 Twiddled;
+
+ UInt32 MinDimension;
+
+ UInt32 SrcBitPos;
+ UInt32 DstBitPos;
+
+ int ShiftCount;
+
+ DebugAssert(YPos < YSize);
+ DebugAssert(IsPowerOfTwo(YSize));
+ DebugAssert(IsPowerOfTwo(XSize));
+
+ if (YSize < XSize)
+ MinDimension = YSize;
+ else
+ MinDimension = XSize;
+
+ // Step through all the bits in the "minimum" dimension
+ SrcBitPos = 1;
+ DstBitPos = 1;
+ Twiddled = 0;
+ ShiftCount = 0;
+
+ while (SrcBitPos < MinDimension)
+ {
+ if(YPos & SrcBitPos)
+ Twiddled |= DstBitPos;
+
+ SrcBitPos <<= 1;
+ DstBitPos <<= 2;
+ ShiftCount += 1;
+ }
+
+ // prepend any unused bits, if they were from Y
+ if (YSize >= XSize)
+ {
+ YPos >>= ShiftCount;
+ Twiddled |= (YPos << (2*ShiftCount));
+ }
+
+ return Twiddled;
+}
+
+
+static UInt32 TwiddleX_PVRTC(UInt32 YSize, UInt32 XSize, UInt32 XPos)
+{
+ UInt32 Twiddled;
+
+ UInt32 MinDimension;
+
+ UInt32 SrcBitPos;
+ UInt32 DstBitPos;
+
+ int ShiftCount;
+
+ DebugAssert(XPos < XSize);
+ DebugAssert(IsPowerOfTwo(YSize));
+ DebugAssert(IsPowerOfTwo(XSize));
+
+ if (YSize < XSize)
+ MinDimension = YSize;
+ else
+ MinDimension = XSize;
+
+ // Step through all the bits in the "minimum" dimension
+ SrcBitPos = 1;
+ DstBitPos = 2;
+ Twiddled = 0;
+ ShiftCount = 0;
+
+ while (SrcBitPos < MinDimension)
+ {
+ if (XPos & SrcBitPos)
+ {
+ Twiddled |= DstBitPos;
+ }
+
+ SrcBitPos <<= 1;
+ DstBitPos <<= 2;
+ ShiftCount += 1;
+ }
+
+ // prepend any unused bits, if they were from X
+ if (YSize < XSize)
+ {
+ XPos >>= ShiftCount;
+ Twiddled |= (XPos << (2*ShiftCount));
+ }
+
+ return Twiddled;
+}
+
+
+
+static UInt32 TwiddleUVPVRTC(UInt32 YSize, UInt32 XSize, UInt32 YPos, UInt32 XPos)
+{
+ return TwiddleY_PVRTC (YSize, XSize, YPos) + TwiddleX_PVRTC (YSize, XSize, XPos);
+}
+
+
+
+template<bool Do2bitMode, bool AssumeImageTiles>
+static void DecompressPVRTC(const PVRTCBlock *pCompressedData,
+ const int XDim,
+ const int YDim,
+ unsigned char* pResultImage)
+{
+ int BlkX, BlkY;
+ int BlkXp1, BlkYp1;
+ int BlkXDim, BlkYDim;
+
+ int ModulationVals[8][16];
+ int ModulationModes[8][16];
+
+ int Mod, DoPT;
+
+ // local neighbourhood of blocks
+ const PVRTCBlock *pBlocks[2][2];
+
+ const PVRTCBlock *pPrevious[2][2] = {{NULL, NULL}, {NULL, NULL}};
+
+ // Low precision colors extracted from the blocks.
+ // Rightmost colors have leftmost values subtracted from them.
+ struct PVRBlockColors {
+ int Reps[2][4];
+ };
+ PVRBlockColors Colours5554[2][2];
+
+ // Interpolated A and B colours for the pixel
+ int ASig[4], BSig[4];
+
+ const int XBlockSize = Do2bitMode ? kPVRSizeX2 : kPVRSizeX4;
+
+
+ // For MBX don't allow the sizes to get too small
+ BlkXDim = std::max(2, XDim / XBlockSize);
+ BlkYDim = std::max(2, YDim / kPVRBlockSizeY);
+
+ // Step through the pixels of the image decompressing each one in turn
+ //
+ // Note that this is a hideously inefficient way to do this!
+ for (int y = 0; y < YDim; y++)
+ {
+ BlkY = (y - kPVRBlockSizeY/2);
+ BlkY = PVR_LIMIT_COORD(BlkY, YDim, AssumeImageTiles);
+ BlkY /= kPVRBlockSizeY;
+ //BlkY = PVR_LIMIT_COORD(BlkY, BlkYDim, AssumeImageTiles);
+ BlkYp1 = PVR_LIMIT_COORD(BlkY+1, BlkYDim, AssumeImageTiles);
+
+ // Since Y & X coordinates twiddle into separate bits,
+ // we can compute Y block twiddle mask here for the whole row.
+ const PVRTCBlock* blockPointerY = pCompressedData + TwiddleY_PVRTC (BlkYDim, BlkXDim, BlkY);
+ const PVRTCBlock* blockPointerY1 = pCompressedData + TwiddleY_PVRTC (BlkYDim, BlkXDim, BlkYp1);
+
+ int coordV = GetVCoordPVR(y);
+
+ for (int x = 0; x < XDim; x++)
+ {
+ // map this pixel to the top left neighbourhood of blocks
+ BlkX = (x - XBlockSize/2);
+ BlkX = PVR_LIMIT_COORD(BlkX, XDim, AssumeImageTiles);
+ BlkX /= XBlockSize;
+ //BlkX = PVR_LIMIT_COORD(BlkX, BlkXDim, AssumeImageTiles);
+
+
+ // compute the positions of the other 3 blocks
+ BlkXp1 = PVR_LIMIT_COORD(BlkX+1, BlkXDim, AssumeImageTiles);
+
+ // Map to block memory locations
+
+ // block offsets, X bits
+ UInt32 blockOffsetX = TwiddleX_PVRTC (BlkYDim, BlkXDim, BlkX);
+ UInt32 blockOffsetX1 = TwiddleX_PVRTC (BlkYDim, BlkXDim, BlkXp1);
+
+ pBlocks[0][0] = blockPointerY + blockOffsetX;
+ pBlocks[0][1] = blockPointerY + blockOffsetX1;
+ pBlocks[1][0] = blockPointerY1 + blockOffsetX;
+ pBlocks[1][1] = blockPointerY1 + blockOffsetX1;
+
+
+ // extract the colours and the modulation information IF the previous values
+ // have changed.
+ if (pPrevious[0][0]!=pBlocks[0][0]||pPrevious[0][1]!=pBlocks[0][1]||
+ pPrevious[1][0]!=pBlocks[1][0]||pPrevious[1][1]!=pBlocks[1][1])
+ {
+ int StartY = 0;
+ for (int i = 0; i < 2; i++)
+ {
+ int StartX = 0;
+ for (int j = 0; j < 2; j++)
+ {
+ Unpack5554Colour(pBlocks[i][j], Colours5554[i][j].Reps);
+
+ UnpackModulations<Do2bitMode>(pBlocks[i][j],
+ ModulationVals,
+ ModulationModes,
+ StartX, StartY);
+
+ StartX += XBlockSize;
+ } // end for j
+
+ // for rightmost color block, subtract leftmost colors now
+ for (int ab = 0; ab < 2; ++ab)
+ {
+ for (int c = 0; c < 4; ++c)
+ Colours5554[i][1].Reps[ab][c] -= Colours5554[i][0].Reps[ab][c];
+ }
+
+ StartY += kPVRBlockSizeY;
+ } // end for i
+
+ // make a copy of the new pointers
+ pPrevious[0][0] = pBlocks[0][0];
+ pPrevious[0][1] = pBlocks[0][1];
+ pPrevious[1][0] = pBlocks[1][0];
+ pPrevious[1][1] = pBlocks[1][1];
+ } // end if the blocks have changed
+
+
+ // decompress the pixel. First compute the interpolated A and B signals
+ int coordU = GetUCoordPVR<Do2bitMode>(x);
+ InterpolateColoursPVRTC<Do2bitMode>(Colours5554[0][0].Reps[0],
+ Colours5554[0][1].Reps[0],
+ Colours5554[1][0].Reps[0],
+ Colours5554[1][1].Reps[0],
+ coordU, coordV,
+ ASig);
+
+ InterpolateColoursPVRTC<Do2bitMode>(Colours5554[0][0].Reps[1],
+ Colours5554[0][1].Reps[1],
+ Colours5554[1][0].Reps[1],
+ Colours5554[1][1].Reps[1],
+ coordU, coordV,
+ BSig);
+
+ GetModulationValue<Do2bitMode>(x,y, ModulationVals, ModulationModes,
+ &Mod, &DoPT);
+
+
+ // compute the modulated color and store in output image
+ for (int i = 0; i < 4; i++)
+ {
+ int res = (ASig[i] * 8 + Mod * (BSig[i] - ASig[i])) >> 3;
+ pResultImage[i] = (UInt8)res;
+ }
+ if (DoPT)
+ {
+ pResultImage[3] = 0;
+ }
+ pResultImage += 4;
+
+ } // end for x
+ } // end for y
+}
+#endif
+
+
+
+
+// ------------------------------------------------------------------------
+// ETC
+
+
+
+#if HAS_ETC_DECOMPRESSOR
+
+#define GETBITS(source, size, startpos) (( (source) >> ((startpos)-(size)+1) ) & ((1<<(size)) -1))
+#define GETBITSHIGH(source, size, startpos) (( (source) >> (((startpos)-32)-(size)+1) ) & ((1<<(size)) -1))
+inline int clampUByte(int v) { return (v < 0) ? 0 : ((v > 255) ? 255 : v); }
+
+inline void STORE_RGB(UInt32* img, int width, int x, int y, int r, int g, int b)
+{
+ Assert(r >= 0 && r <= 255);
+ Assert(g >= 0 && g <= 255);
+ Assert(b >= 0 && b <= 255);
+ Assert(x >= 0 && x < width);
+ img[y*width + x] = (0xFF<<24) | (r<<0) | (g<<8) | (b<<16);
+}
+
+void decompressBlockDiffFlip(unsigned int block_part1, unsigned int block_part2, UInt32* dstImg, int width, int startx, int starty)
+{
+ static const int unscramble[4] = {2, 3, 1, 0};
+
+ int compressParams[16][4];
+
+ compressParams[0][0] = -8; compressParams[0][1] = -2; compressParams[0][2] = 2; compressParams[0][3] = 8;
+ compressParams[1][0] = -8; compressParams[1][1] = -2; compressParams[1][2] = 2; compressParams[1][3] = 8;
+ compressParams[2][0] = -17; compressParams[2][1] = -5; compressParams[2][2] = 5; compressParams[2][3] = 17;
+ compressParams[3][0] = -17; compressParams[3][1] = -5; compressParams[3][2] = 5; compressParams[3][3] = 17;
+ compressParams[4][0] = -29; compressParams[4][1] = -9; compressParams[4][2] = 9; compressParams[4][3] = 29;
+ compressParams[5][0] = -29; compressParams[5][1] = -9; compressParams[5][2] = 9; compressParams[5][3] = 29;
+ compressParams[6][0] = -42; compressParams[6][1] = -13; compressParams[6][2] = 13; compressParams[6][3] = 42;
+ compressParams[7][0] = -42; compressParams[7][1] = -13; compressParams[7][2] = 13; compressParams[7][3] = 42;
+ compressParams[8][0] = -60; compressParams[8][1] = -18; compressParams[8][2] = 18; compressParams[8][3] = 60;
+ compressParams[9][0] = -60; compressParams[9][1] = -18; compressParams[9][2] = 18; compressParams[9][3] = 60;
+ compressParams[10][0] = -80; compressParams[10][1] = -24; compressParams[10][2] = 24; compressParams[10][3] = 80;
+ compressParams[11][0] = -80; compressParams[11][1] = -24; compressParams[11][2] = 24; compressParams[11][3] = 80;
+ compressParams[12][0] =-106; compressParams[12][1] = -33; compressParams[12][2] = 33; compressParams[12][3] = 106;
+ compressParams[13][0] =-106; compressParams[13][1] = -33; compressParams[13][2] = 33; compressParams[13][3] = 106;
+ compressParams[14][0] =-183; compressParams[14][1] = -47; compressParams[14][2] = 47; compressParams[14][3] = 183;
+ compressParams[15][0] =-183; compressParams[15][1] = -47; compressParams[15][2] = 47; compressParams[15][3] = 183;
+
+ UInt8 avg_color[3], enc_color1[3], enc_color2[3];
+ signed char diff[3];
+ int table;
+ int index,shift;
+ int r,g,b;
+ int diffbit;
+ int flipbit;
+
+ diffbit = (GETBITSHIGH(block_part1, 1, 33));
+ flipbit = (GETBITSHIGH(block_part1, 1, 32));
+
+ if( !diffbit )
+ {
+
+ // We have diffbit = 0.
+
+ // First decode left part of block.
+ avg_color[0]= GETBITSHIGH(block_part1, 4, 63);
+ avg_color[1]= GETBITSHIGH(block_part1, 4, 55);
+ avg_color[2]= GETBITSHIGH(block_part1, 4, 47);
+
+ // Here, we should really multiply by 17 instead of 16. This can
+ // be done by just copying the four lower bits to the upper ones
+ // while keeping the lower bits.
+ avg_color[0] |= (avg_color[0] <<4);
+ avg_color[1] |= (avg_color[1] <<4);
+ avg_color[2] |= (avg_color[2] <<4);
+
+ table = GETBITSHIGH(block_part1, 3, 39) << 1;
+
+
+ unsigned int pixel_indices_MSB, pixel_indices_LSB;
+
+ pixel_indices_MSB = GETBITS(block_part2, 16, 31);
+ pixel_indices_LSB = GETBITS(block_part2, 16, 15);
+
+ if( (flipbit) == 0 )
+ {
+ // We should not flip
+ shift = 0;
+ for(int x=startx; x<startx+2; x++)
+ {
+ for(int y=starty; y<starty+4; y++)
+ {
+ index = ((pixel_indices_MSB >> shift) & 1) << 1;
+ index |= ((pixel_indices_LSB >> shift) & 1);
+ shift++;
+ index=unscramble[index];
+
+ r=clampUByte(avg_color[0]+compressParams[table][index]);
+ g=clampUByte(avg_color[1]+compressParams[table][index]);
+ b=clampUByte(avg_color[2]+compressParams[table][index]);
+ STORE_RGB(dstImg, width, x, y, r, g, b);
+ }
+ }
+ }
+ else
+ {
+ // We should flip
+ shift = 0;
+ for(int x=startx; x<startx+4; x++)
+ {
+ for(int y=starty; y<starty+2; y++)
+ {
+ index = ((pixel_indices_MSB >> shift) & 1) << 1;
+ index |= ((pixel_indices_LSB >> shift) & 1);
+ shift++;
+ index=unscramble[index];
+
+ r=clampUByte(avg_color[0]+compressParams[table][index]);
+ g=clampUByte(avg_color[1]+compressParams[table][index]);
+ b=clampUByte(avg_color[2]+compressParams[table][index]);
+ STORE_RGB(dstImg, width, x, y, r, g, b);
+ }
+ shift+=2;
+ }
+ }
+
+ // Now decode other part of block.
+ avg_color[0]= GETBITSHIGH(block_part1, 4, 59);
+ avg_color[1]= GETBITSHIGH(block_part1, 4, 51);
+ avg_color[2]= GETBITSHIGH(block_part1, 4, 43);
+
+ // Here, we should really multiply by 17 instead of 16. This can
+ // be done by just copying the four lower bits to the upper ones
+ // while keeping the lower bits.
+ avg_color[0] |= (avg_color[0] <<4);
+ avg_color[1] |= (avg_color[1] <<4);
+ avg_color[2] |= (avg_color[2] <<4);
+
+ table = GETBITSHIGH(block_part1, 3, 36) << 1;
+ pixel_indices_MSB = GETBITS(block_part2, 16, 31);
+ pixel_indices_LSB = GETBITS(block_part2, 16, 15);
+
+ if( (flipbit) == 0 )
+ {
+ // We should not flip
+ shift=8;
+ for(int x=startx+2; x<startx+4; x++)
+ {
+ for(int y=starty; y<starty+4; y++)
+ {
+ index = ((pixel_indices_MSB >> shift) & 1) << 1;
+ index |= ((pixel_indices_LSB >> shift) & 1);
+ shift++;
+ index=unscramble[index];
+
+ r=clampUByte(avg_color[0]+compressParams[table][index]);
+ g=clampUByte(avg_color[1]+compressParams[table][index]);
+ b=clampUByte(avg_color[2]+compressParams[table][index]);
+ STORE_RGB(dstImg, width, x, y, r, g, b);
+ }
+ }
+ }
+ else
+ {
+ // We should flip
+ shift=2;
+ for(int x=startx; x<startx+4; x++)
+ {
+ for(int y=starty+2; y<starty+4; y++)
+ {
+ index = ((pixel_indices_MSB >> shift) & 1) << 1;
+ index |= ((pixel_indices_LSB >> shift) & 1);
+ shift++;
+ index=unscramble[index];
+
+ r=clampUByte(avg_color[0]+compressParams[table][index]);
+ g=clampUByte(avg_color[1]+compressParams[table][index]);
+ b=clampUByte(avg_color[2]+compressParams[table][index]);
+ STORE_RGB(dstImg, width, x, y, r, g, b);
+ }
+ shift += 2;
+ }
+ }
+
+ }
+ else
+ {
+ // We have diffbit = 1.
+
+ // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32
+ // ---------------------------------------------------------------------------------------------------
+ // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip|
+ // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit |
+ // ---------------------------------------------------------------------------------------------------
+ //
+ //
+ // c) bit layout in bits 31 through 0 (in both cases)
+ //
+ // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+ // --------------------------------------------------------------------------------------------------
+ // | most significant pixel index bits | least significant pixel index bits |
+ // | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a |
+ // --------------------------------------------------------------------------------------------------
+
+
+ // First decode left part of block.
+ enc_color1[0]= GETBITSHIGH(block_part1, 5, 63);
+ enc_color1[1]= GETBITSHIGH(block_part1, 5, 55);
+ enc_color1[2]= GETBITSHIGH(block_part1, 5, 47);
+
+
+ // Expand from 5 to 8 bits
+ avg_color[0] = (enc_color1[0] <<3) | (enc_color1[0] >> 2);
+ avg_color[1] = (enc_color1[1] <<3) | (enc_color1[1] >> 2);
+ avg_color[2] = (enc_color1[2] <<3) | (enc_color1[2] >> 2);
+
+
+ table = GETBITSHIGH(block_part1, 3, 39) << 1;
+
+ unsigned int pixel_indices_MSB, pixel_indices_LSB;
+
+ pixel_indices_MSB = GETBITS(block_part2, 16, 31);
+ pixel_indices_LSB = GETBITS(block_part2, 16, 15);
+
+ if( (flipbit) == 0 )
+ {
+ // We should not flip
+ shift = 0;
+ for(int x=startx; x<startx+2; x++)
+ {
+ for(int y=starty; y<starty+4; y++)
+ {
+ index = ((pixel_indices_MSB >> shift) & 1) << 1;
+ index |= ((pixel_indices_LSB >> shift) & 1);
+ shift++;
+ index=unscramble[index];
+
+ r=clampUByte(avg_color[0]+compressParams[table][index]);
+ g=clampUByte(avg_color[1]+compressParams[table][index]);
+ b=clampUByte(avg_color[2]+compressParams[table][index]);
+ STORE_RGB(dstImg, width, x, y, r, g, b);
+ }
+ }
+ }
+ else
+ {
+ // We should flip
+ shift = 0;
+ for(int x=startx; x<startx+4; x++)
+ {
+ for(int y=starty; y<starty+2; y++)
+ {
+ index = ((pixel_indices_MSB >> shift) & 1) << 1;
+ index |= ((pixel_indices_LSB >> shift) & 1);
+ shift++;
+ index=unscramble[index];
+
+ r=clampUByte(avg_color[0]+compressParams[table][index]);
+ g=clampUByte(avg_color[1]+compressParams[table][index]);
+ b=clampUByte(avg_color[2]+compressParams[table][index]);
+ STORE_RGB(dstImg, width, x, y, r, g, b);
+ }
+ shift+=2;
+ }
+ }
+
+
+ // Now decode right part of block.
+
+
+ diff[0]= GETBITSHIGH(block_part1, 3, 58);
+ diff[1]= GETBITSHIGH(block_part1, 3, 50);
+ diff[2]= GETBITSHIGH(block_part1, 3, 42);
+
+ enc_color2[0]= enc_color1[0] + diff[0];
+ enc_color2[1]= enc_color1[1] + diff[1];
+ enc_color2[2]= enc_color1[2] + diff[2];
+
+ // Extend sign bit to entire byte.
+ diff[0] = (diff[0] << 5);
+ diff[1] = (diff[1] << 5);
+ diff[2] = (diff[2] << 5);
+ diff[0] = diff[0] >> 5;
+ diff[1] = diff[1] >> 5;
+ diff[2] = diff[2] >> 5;
+
+ // Calculale second color
+ enc_color2[0]= enc_color1[0] + diff[0];
+ enc_color2[1]= enc_color1[1] + diff[1];
+ enc_color2[2]= enc_color1[2] + diff[2];
+
+ // Expand from 5 to 8 bits
+ avg_color[0] = (enc_color2[0] <<3) | (enc_color2[0] >> 2);
+ avg_color[1] = (enc_color2[1] <<3) | (enc_color2[1] >> 2);
+ avg_color[2] = (enc_color2[2] <<3) | (enc_color2[2] >> 2);
+
+
+ table = GETBITSHIGH(block_part1, 3, 36) << 1;
+ pixel_indices_MSB = GETBITS(block_part2, 16, 31);
+ pixel_indices_LSB = GETBITS(block_part2, 16, 15);
+
+ if( (flipbit) == 0 )
+ {
+ // We should not flip
+ shift=8;
+ for(int x=startx+2; x<startx+4; x++)
+ {
+ for(int y=starty; y<starty+4; y++)
+ {
+ index = ((pixel_indices_MSB >> shift) & 1) << 1;
+ index |= ((pixel_indices_LSB >> shift) & 1);
+ shift++;
+ index=unscramble[index];
+
+ r=clampUByte(avg_color[0]+compressParams[table][index]);
+ g=clampUByte(avg_color[1]+compressParams[table][index]);
+ b=clampUByte(avg_color[2]+compressParams[table][index]);
+ STORE_RGB(dstImg, width, x, y, r, g, b);
+ }
+ }
+ }
+ else
+ {
+ // We should flip
+ shift=2;
+ for(int x=startx; x<startx+4; x++)
+ {
+ for(int y=starty+2; y<starty+4; y++)
+ {
+ index = ((pixel_indices_MSB >> shift) & 1) << 1;
+ index |= ((pixel_indices_LSB >> shift) & 1);
+ shift++;
+ index=unscramble[index];
+
+ r=clampUByte(avg_color[0]+compressParams[table][index]);
+ g=clampUByte(avg_color[1]+compressParams[table][index]);
+ b=clampUByte(avg_color[2]+compressParams[table][index]);
+ STORE_RGB(dstImg, width, x, y, r, g, b);
+ }
+ shift += 2;
+ }
+ }
+ }
+}
+
+
+static void DecompressETC_RGB4 ( int xblocks, int yblocks, int destWidth, const UInt32* m_pCompBytes, UInt32* m_pDecompBytes )
+{
+ const UInt8* srcPtr = (const UInt8*)m_pCompBytes;
+ UInt32* dstPtr = m_pDecompBytes;
+
+ for (int y = 0; y < yblocks; y++)
+ {
+ for (int x = 0; x < xblocks; x++)
+ {
+ UInt32 part1 = (srcPtr[0]<<24) | (srcPtr[1]<<16) | (srcPtr[2]<<8) | (srcPtr[3]<<0);
+ UInt32 part2 = (srcPtr[4]<<24) | (srcPtr[5]<<16) | (srcPtr[6]<<8) | (srcPtr[7]<<0);
+ decompressBlockDiffFlip(part1, part2, dstPtr, destWidth, x*4, y*4);
+ srcPtr += 8;
+ }
+ }
+}
+
+#endif // HAS_ETC_DECOMPRESSOR
+
+
+#if HAS_ATC_DECOMPRESSOR
+
+template<int textureFormat>
+static void DecompressATC ( int xblocks, int yblocks, int destWidth, const UInt32* m_pCompBytes, UInt32* m_pDecompBytes )
+{
+ bool doAlpha = (textureFormat == kTexFormatATC_RGBA8);
+
+ TQonvertImage src, dst;
+ src.nWidth = xblocks * 4;
+ src.nHeight = yblocks * 4;
+ src.nFormat = doAlpha ? Q_FORMAT_ATC_RGBA_INTERPOLATED_ALPHA : Q_FORMAT_ATC_RGB;
+ src.pFormatFlags = NULL;
+ src.nDataSize = CalculateImageSize (src.nWidth, src.nHeight, textureFormat);
+ src.pData = (unsigned char*)m_pCompBytes;
+
+ dst.nWidth = destWidth;
+ dst.nHeight = yblocks * 4;
+ dst.nFormat = Q_FORMAT_RGBA_8888;
+ dst.pFormatFlags = NULL;
+ dst.nDataSize = CalculateImageSize (dst.nWidth, dst.nHeight, kTexFormatRGBA32);
+ dst.pData = (unsigned char*)m_pDecompBytes;
+
+ int nRet = Qonvert(&src, &dst);
+ if (nRet != Q_SUCCESS)
+ {
+ ErrorStringMsg("DecompressETC2 failed with nRet = %i", nRet);
+ }
+}
+
+#endif // HAS_ATC_DECOMPRESSOR
+
+#if HAS_ASTC_DECOMPRESSOR
+
+// ASTC decoding
+
+#include "External/astcenc/Source/astc_codec_internals.h"
+
+// Needed by astcenc
+void astc_codec_internal_error(const char *filename, int linenum)
+{
+ printf_console("ASTCenc: Internal error: File=%s Line=%d\n", filename, linenum);
+}
+// Define an unlink() function in terms of the Unity DeleteFile function.
+int astc_codec_unlink(const char *filename)
+{
+/* bool res = DeleteFile(filename);
+ return (res ? 0 : -1);*/
+
+ // Not used in runtime
+ return -1;
+}
+int rgb_force_use_of_hdr = 0;
+int alpha_force_use_of_hdr = 0;
+int perform_srgb_transform = 0;
+int print_tile_errors = 0;
+
+// TODO: Make threadsafe when we have multithreaded renderer. Doesn't really hurt to do multiple inits though.
+static int astcenc_initialized = 0;
+
+static void DecompressASTC(const UInt32 *srcData, int destWidth, int destHeight, UInt32 *destData, int blockWidth, int blockHeight)
+{
+ swizzlepattern swz_decode = { 0, 1, 2, 3 };
+
+ if(!astcenc_initialized)
+ {
+ // initialization routines
+ prepare_angular_tables();
+ build_quantization_mode_table();
+ astcenc_initialized = 1;
+ }
+
+
+ astc_codec_image *img = allocate_image(8, destWidth, destHeight, 1, 0);
+ initialize_image(img);
+
+ const int zblocks = 1;
+ const int yblocks = CeilfToInt((float)destHeight / (float)blockHeight);
+ const int xblocks = CeilfToInt((float)destWidth / (float)blockWidth);
+
+ const int xdim = blockWidth;
+ const int ydim = blockHeight;
+ const int zdim = 1;
+
+ int x, y, z;
+
+ imageblock pb;
+ for (z = 0; z < zblocks; z++)
+ for (y = 0; y < yblocks; y++)
+ for (x = 0; x < xblocks; x++)
+ {
+ int offset = (((z * yblocks + y) * xblocks) + x) * 16;
+ uint8_t *bp = ((uint8_t *)srcData) + offset;
+ physical_compressed_block pcb = *(physical_compressed_block *) bp;
+ symbolic_compressed_block scb;
+ physical_to_symbolic(xdim, ydim, zdim, pcb, &scb);
+ decompress_symbolic_block(DECODE_LDR, xdim, ydim, zdim, x * xdim, y * ydim, z * zdim, &scb, &pb);
+ write_imageblock(img, &pb, xdim, ydim, zdim, x * xdim, y * ydim, z * zdim, swz_decode);
+ }
+
+ for(y = 0; y < destHeight; y++)
+ {
+ memcpy(&destData[y*destWidth], img->imagedata8[0][y], destWidth * 4);
+ }
+ destroy_image(img);
+
+}
+
+#endif // HAS_ASTC_DECOMPRESSOR
+
+// ------------------------------------------------------------------------
+// Common
+
+
+bool DecompressNativeTextureFormatWithMipLevel( TextureFormat srcFormat, int srcWidth, int srcHeight, int mipLevel, const UInt32* sourceData,
+ int destWidth, int destHeight, UInt32* destData )
+{
+
+#if HAS_FLASH_ATF_DECOMPRESSOR
+ // Flash requires that we pass the mipLevel explicitly
+ if (IsCompressedFlashATFTextureFormat(srcFormat))
+ {
+ DecompressFlashATFTexture((const UInt8*)sourceData, destWidth, destHeight, mipLevel, (UInt8*)destData);
+ return true;
+ }
+#endif
+
+ return DecompressNativeTextureFormat (srcFormat, srcWidth, srcHeight, (UInt32*)sourceData, destWidth, destHeight, destData);
+}
+
+
+
+bool DecompressNativeTextureFormat( TextureFormat srcFormat, int srcWidth, int srcHeight, const UInt32* sourceData,
+ int destWidth, int destHeight, UInt32* destData )
+{
+ Assert( IsAnyCompressedTextureFormat(srcFormat) );
+ Assert( destWidth >= srcWidth && destHeight >= srcHeight );
+ Assert( destWidth % 4 == 0 && destHeight % 4 == 0 );
+
+#if UNITY_XENON || HAS_DXT_DECOMPRESSOR || HAS_ETC_DECOMPRESSOR || HAS_ATC_DECOMPRESSOR
+ int xblocks = (srcWidth + 3) / 4;
+ int yblocks = (srcHeight + 3) / 4;
+#endif
+
+ const UInt32* srcData = sourceData;
+
+ #if UNITY_XENON
+ UInt32 dataSize = (xblocks * yblocks * ((kTexFormatDXT1 == srcFormat) ? 8 : 16)) >> 1;
+ UInt16 *s = (UInt16*)srcData;
+ UInt16 *d = new UInt16[dataSize];
+ for(int i=0;i<dataSize;i++)
+ d[i]=GetByteSwap16(s[i]);
+ srcData = (UInt32*)d;
+ #endif
+
+ #if PRINT_DECOMPRESSION_TIMES
+ double t0 = GetTimeSinceStartup();
+ #endif
+
+
+ switch( srcFormat )
+ {
+ #if HAS_DXT_DECOMPRESSOR
+ case kTexFormatDXT1: DecompressDXT1( xblocks, yblocks, destWidth, srcData, destData ); break;
+ case kTexFormatDXT3: DecompressDXT3( xblocks, yblocks, destWidth, srcData, destData ); break;
+ case kTexFormatDXT5: DecompressDXT5( xblocks, yblocks, destWidth, srcData, destData ); break;
+ #endif
+
+ #if HAS_ETC_DECOMPRESSOR
+ case kTexFormatETC_RGB4: DecompressETC_RGB4(xblocks, yblocks, destWidth, srcData, destData ); break;
+ #endif
+
+ #if HAS_ATC_DECOMPRESSOR
+ case kTexFormatATC_RGB4: DecompressATC<kTexFormatATC_RGB4>(xblocks, yblocks, destWidth, srcData, destData); break;
+ case kTexFormatATC_RGBA8: DecompressATC<kTexFormatATC_RGBA8>(xblocks, yblocks, destWidth, srcData, destData); break;
+ #endif
+
+ case kTexFormatETC2_RGB: DecompressETC2_RGB8 ((UInt8*)destData, (const UInt8*)srcData, destWidth, destHeight); break;
+ case kTexFormatETC2_RGBA1: DecompressETC2_RGB8_A1 ((UInt8*)destData, (const UInt8*)srcData, destWidth, destHeight); break;
+ case kTexFormatETC2_RGBA8: DecompressETC2_RGBA8 ((UInt8*)destData, (const UInt8*)srcData, destWidth, destHeight); break;
+
+ #if HAS_PVRTC_DECOMPRESSOR
+ case kTexFormatPVRTC_RGB4:
+ case kTexFormatPVRTC_RGBA4: DecompressPVRTC<false,true>( reinterpret_cast<const PVRTCBlock*>(srcData), srcWidth, srcHeight, reinterpret_cast<unsigned char*> (destData) ); break;
+
+ case kTexFormatPVRTC_RGB2:
+ case kTexFormatPVRTC_RGBA2: DecompressPVRTC<true,true>( reinterpret_cast<const PVRTCBlock*>(srcData), srcWidth, srcHeight, reinterpret_cast<unsigned char*> (destData) ); break;
+ #endif
+
+ #if HAS_FLASH_ATF_DECOMPRESSOR
+ case kTexFormatFlashATF_RGB_DXT1:
+ case kTexFormatFlashATF_RGB_JPG:
+ case kTexFormatFlashATF_RGBA_JPG:
+ DecompressFlashATFTexture ((const UInt8*)srcData, destWidth, destHeight, 0, (UInt8*)destData);
+ break;
+ #endif
+
+#if HAS_ASTC_DECOMPRESSOR
+#define DO_ASTC(bx,by) case kTexFormatASTC_RGB_##bx##x##by : case kTexFormatASTC_RGBA_##bx##x##by : DecompressASTC(srcData, destWidth, destHeight, destData, bx, by); break
+
+ DO_ASTC(4, 4);
+ DO_ASTC(5, 5);
+ DO_ASTC(6, 6);
+ DO_ASTC(8, 8);
+ DO_ASTC(10, 10);
+ DO_ASTC(12, 12);
+
+#undef DO_ASTC
+#endif
+
+ default:
+ AssertString( "Unknown compressed texture format!" );
+ #if UNITY_XENON
+ delete[] srcData;
+ #endif
+ return false;
+ }
+
+#if UNITY_XENON
+ delete[] srcData;
+#endif
+
+ #if PRINT_DECOMPRESSION_TIMES
+ double t1 = GetTimeSinceStartup();
+ if (srcWidth >= 512 && srcHeight >= 512)
+ {
+ printf_console("Decompress %x size %ix%i fmt %i time %.3fs\n", srcData, srcWidth, srcHeight, (int)srcFormat, t1-t0);
+ }
+ #endif
+
+ return true;
+}
+
+
+
+// ------------------------------------------------------------------------
+// Unit Tests
+
+
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+SUITE (ImageDecompressionTests)
+{
+ #if UNITY_LITTLE_ENDIAN
+ TEST (DecodeDXT5AlphaPalette8a)
+ {
+ UInt32 res[16];
+ memset (res, 0xCC, sizeof(res));
+ UInt8 b[] = { 17, 13, 177, 109, 155, 54, 105, 82, 255 };
+ DecodeAlpha3BitLinear (res, *(DXTAlphaBlock3BitLinear*)b, 4, 0x00FFFFFF);
+ UInt8 r[] = { 13,14,14,14, 14,14,14,15, 14,14,15,15, 14,15,15,16 };
+ for (int i = 0; i < 16; ++i) {
+ CHECK_EQUAL ((int)r[i], (res[i]&0xFF000000)>>24);
+ }
+ }
+ TEST (DecodeDXT5AlphaPalette8b)
+ {
+ UInt32 res[16];
+ memset (res, 0xCD, sizeof(res));
+ UInt8 b[] = { 251, 5, 179, 109, 113, 54, 107, 84, 255 };
+ DecodeAlpha3BitLinear (res, *(DXTAlphaBlock3BitLinear*)b, 4, 0x00FFFFFF);
+ UInt8 r[] = { 181,75,75,75, 75,216,146,181, 75,75,146,110, 75,251,110,216 };
+ for (int i = 0; i < 16; ++i) {
+ CHECK_EQUAL ((int)r[i], (res[i]&0xFF000000)>>24);
+ }
+ }
+ TEST (DecodeDXT5AlphaPalette6)
+ {
+ UInt32 res[16];
+ memset (res, 0xCC, sizeof(res));
+ UInt8 b[] = { 15, 18, 0, 4, 72, 144, 8, 137, 255 };
+ DecodeAlpha3BitLinear (res, *(DXTAlphaBlock3BitLinear*)b, 4, 0x00FFFFFF);
+ UInt8 r[] = { 15,15,15,16, 15,15,16,16, 15,16,16,17, 15,16,16,17 };
+ for (int i = 0; i < 16; ++i) {
+ CHECK_EQUAL ((int)r[i], (res[i]&0xFF000000)>>24);
+ }
+ }
+ #if HAS_PVRTC_DECOMPRESSOR
+ TEST (DecodePVRTC_2_16x16)
+ {
+ const int kSize = 16;
+ UInt32 src[kSize*kSize*2/32] = {
+ 0xeeeeeeee, 0x83eed400, 0xeeeeeeee, 0x8befb006, 0xfefefefe, 0xeed9801e, 0x00fefefe, 0xcf18801e,
+ 0x0eeeeeee, 0x9ff4fc00, 0x00fefefe, 0xaff58000, 0x00ffffff, 0x83add404, 0x00ffffff, 0xb77d8000,
+ };
+ UInt32 expected[kSize*kSize] = {
+ 0xff3f002b,0xffadeb59,0xffa3f24b,0xff99f83b,0xff000056,0xff99f83b,0xffa3f24b,0xffadeb59,0xff3f002b,0xffc0dd77,0xffcbd787,0xffd4d095,0xffdecaa5,0xffd4d095,0xffcbd787,0xffc0dd77,
+ 0xff5f0040,0xffa0e756,0xff96ef40,0xff8cf72b,0xff000081,0xff8cf72b,0xff96ef40,0xffa0e756,0xff5f0040,0xffb6d780,0xffc0cf96,0xffcbc7ac,0xffd6bfc1,0xffcbc7ac,0xffc0cf96,0xffb6d780,
+ 0xff7f0056,0xff95e353,0xff8aed37,0xff7ef61b,0xff0000ad,0xff7ef61b,0xff8aed37,0xff95e353,0xff7f0056,0xffacd18b,0xffb7c8a7,0xffc2bec2,0xffceb5de,0xffc2bec2,0xffb7c8a7,0xffacd18b,
+ 0xff86004d,0xff95e44f,0xff8bee36,0xff7ff61c,0xff0c009a,0xff7ff61c,0xff8bee36,0xff95e44f,0xff86004d,0xffabd381,0xffb6cb9b,0xffc0c1b4,0xffccb9ce,0xffc0c1b4,0xffb6cb9b,0xffabd381,
+ 0xff8c0044,0xff96e74c,0xff8cef35,0xff81f71e,0xff180088,0xff81f71e,0xff8cef35,0xff96e74c,0xff8c0044,0xffabd679,0xffb5ce90,0xffbfc6a7,0xffcabdbd,0xffbfc6a7,0xffb5ce90,0xffabd679,
+ 0xff92003a,0xff96e848,0xff8df034,0xff82f71f,0xff250075,0xff82f71f,0xff8df034,0xff96e848,0xff92003a,0xffaad870,0xffb4d185,0xffbdc998,0xffc8c1ad,0xffbdc998,0xffb4d185,0xffaad870,
+ 0xff980031,0xff97ea45,0xff8ef133,0xff85f822,0xff310063,0xff85f822,0xff8ef133,0xff97ea45,0xff980031,0xffaadb68,0xffb3d479,0xffbccd8b,0xffc6c69c,0xffbccd8b,0xffb3d479,0xffaadb68,
+ 0xff76005a,0xff95ed3c,0xff90f331,0xff8bf926,0xff25008a,0xff8bf926,0xff90f331,0xff95ed3c,0xff76005a,0xff8a004e,0xff9e0043,0xffb30036,0xffc8002b,0xffb30036,0xff9e0043,0xff8a004e,
+ 0xff540084,0xff93f134,0xff92f62f,0xff91fa2a,0xff1800b1,0xff91fa2a,0xff92f62f,0xff93f134,0xff94ed39,0xff95e83e,0xff96e344,0xff97de49,0xff98da4e,0xff97de49,0xff96e344,0xff95e83e,
+ 0xff3200ad,0xff91f52c,0xff94f82d,0xff97fb2e,0xff0c00d8,0xff97fb2e,0xff94f82d,0xff91f52c,0xff8ef22b,0xff8bee2a,0xff88eb29,0xff85e828,0xff81e427,0xff85e828,0xff88eb29,0xff8bee2a,
+ 0xff1000d6,0xff8ff924,0xff96fb2b,0xff9dfd32,0xff0000ff,0xff9dfd32,0xff96fb2b,0xff8ff924,0xff88f71c,0xff80f515,0xff79f30e,0xff72f107,0xff6bef00,0xff72f107,0xff79f30e,0xff80f515,
+ 0xff0c00a0,0xff9cf732,0xff9ffa37,0xffa2fc3c,0xff0000bf,0xff0300b7,0xff0600b0,0xff0900a8,0xff0c00a0,0xff0f0098,0xff120091,0xff150089,0xff180081,0xff150089,0xff120091,0xff0f0098,
+ 0xff08006b,0xffaaf642,0xffaaf945,0xffa9fc47,0xffa9ff4a,0xffa9fc47,0xffaaf945,0xffaaf642,0xffabf33f,0xffabf03c,0xffaced3a,0xffacea37,0xffade735,0xffacea37,0xffaced3a,0xffabf03c,
+ 0xff040035,0xffb7f451,0xffb3f851,0xffaffb51,0xffabff52,0xffaffb51,0xffb3f851,0xffb7f451,0xffbcf151,0xffc0ed50,0xffc4ea50,0xffc9e550,0xffcee250,0xffc9e550,0xffc4ea50,0xffc0ed50,
+ 0xff000000,0xffc6f360,0xffbdf75e,0xffb5fb5c,0xffadff5a,0xffb5fb5c,0xffbdf75e,0xffc6f360,0xffceef63,0xffd6eb65,0xffdee767,0xffe7e269,0xffefde6b,0xffe7e269,0xffdee767,0xffd6eb65,
+ 0xff1f0015,0xff17001a,0xff0f001f,0xff070025,0xff00002b,0xff070025,0xff0f001f,0xff17001a,0xff1f0015,0xff27000f,0xff2f000a,0xff370005,0xff3f0000,0xff370005,0xff2f000a,0xff27000f,
+ };
+ UInt32 res[kSize*kSize];
+ DecompressPVRTC<true,true> (reinterpret_cast<const PVRTCBlock*>(src), kSize, kSize, reinterpret_cast<UInt8*>(res));
+ CHECK_ARRAY_EQUAL (expected, res, kSize*kSize);
+ }
+ TEST (DecodePVRTC_4_16x16)
+ {
+ const int kSize = 16;
+ UInt32 src[kSize*kSize*4/32] = {
+ 0x32323230,0x7faa30e7,0x32323232,0x7fbc40f9,0x03030303,0x050230f6,0x03030303,0x060330f4,
+ 0x32323232,0x7faa40f7,0xa802f232,0xffff30e7,0xff030303,0x0f0040e6,0xaa00ff00,0xff9f40e9,
+ 0x0303035b,0x300f6aca,0xff030303,0x300f68ca,0x409094aa,0x68af5bba,0xff000040,0x200f58ca,
+ 0xff000000,0x2c0140e6,0xaa00ff00,0xffff41db,0xff000000,0x1c0140e8,0xaa00ff00,0xffff40bb,
+ };
+ UInt32 expected[kSize*kSize] = {
+ 0x7f92d62f,0x727ee217,0xf6d2d6ff,0x6a70f100,0xbbb1a5d4,0x727af500,0x777ff700,0x838eef17,0x99c67994,0xa2cca070,0xadd1b56a,0xaed1b971,0xbbe2b688,0xc1e2c193,0xc8e2cc9f,0xc6d4d19f,
+ 0x00b6cb8d,0x747ee223,0xf2bbc1ff,0x686df200,0x998a7dbe,0x6c72f800,0x6e75fb00,0x8187f121,0x66a93c5e,0xa7aadc64,0xbbbbd286,0xb4bbcd88,0xaebbc88a,0xb2d2b58c,0xb5d2be98,0xc2d0c49e,
+ 0x00b1c192,0x777fe22f,0xeea5adff,0x666bf300,0x776356a9,0x666bfb00,0x666bff00,0x7f7ff32b,0x338c0029,0xb2a9da81,0xccbdcead,0xc3bdcab1,0xbbbdc6b5,0xb2bdc1b9,0xb6d2b7aa,0xbdccb79e,
+ 0x00b6b986,0x7d89e72c,0xeeadb1ff,0x6c72f600,0x776958ab,0x6868fc00,0x6663ff00,0x7f79f329,0x338e002b,0xb2a7da7b,0xccbdcea5,0xc3bdcba8,0xbbbdc8ab,0xb2bdc4ae,0xaabdc1b1,0xa7c1b588,
+ 0x00bbb27a,0x8392eb29,0xeeb5b5ff,0x7279f900,0x776f5aad,0x6a65fd00,0x665aff00,0x7f73f327,0x3390002d,0xb2a5da75,0xccbdce9c,0xc3bdcc9e,0xbbbdcaa0,0xb2bdc8a2,0xaabdc6a5,0x9ec4ac78,
+ 0x00c0aa6e,0x8a9bef26,0xeebdb9ff,0x7980fc00,0x77755caf,0x6c61fe00,0x6652ff00,0x7f6df325,0x3392002f,0xb2a2da6f,0xccbdce94,0xc3bdcd95,0xbbbdcc96,0xb2bdcb97,0xaabdca98,0x9fb2d672,
+ 0x00c5a262,0x90a5f323,0xeec6bdff,0x7f88ff00,0x777b5eb1,0x6e5eff00,0x664aff00,0x7f67f323,0x33940031,0xb2a0da69,0xccbdce8c,0xc3bdce8c,0xbbbdce8c,0xb2bdce8c,0xaabdce8c,0xa1b5da69,
+ 0x00afa366,0x8e99f519,0xeebdb9ff,0x8180fe00,0x77715cc4,0x7461fc00,0x6e52fb00,0x8168f219,0x2e71005e,0x46970049,0x5dbd0033,0x55bd0033,0x4cbd0033,0x44bd0033,0x3bbd0033,0x68bd2e66,
+ 0x009aa46b,0x8c8ef711,0xeeb5b5ff,0x8379fd00,0x77675ad8,0x7b65f900,0x775af700,0x8369f111,0x9077eb23,0x9d86e434,0xaa94de46,0xa59ade46,0xa1a0de46,0x9da7de46,0x99adde46,0x94a2e734,
+ 0x0084a56f,0x8a82f908,0xeeadb1ff,0x8572fc00,0x775c58ec,0x8168f600,0x7f63f300,0x856af008,0x8c71ed11,0x9278ea19,0x997fe723,0x9689e723,0x9492e723,0x929be723,0x90a5e723,0x8e99ed19,
+ 0x006fa673,0x8877fb00,0xeea5adff,0x886bfb00,0x775256ff,0x886bf300,0x886bef00,0x886bef00,0x886bef00,0x886bef00,0x886bef00,0x8877ef00,0x8884ef00,0x8890ef00,0x889cef00,0x8890f300,
+ 0x0085b776,0x8178f500,0xf2bbc1ff,0x816df800,0x997d7dff,0x6c5e5bff,0x3f3f39ff,0x4c433af6,0x59463ced,0x66493de3,0x724c3fda,0x6c4c3fda,0x664c3fda,0x5f4c3fda,0x594c3fda,0x7f685fe3,
+ 0x009bc979,0x7b7aef00,0xf6d2d6ff,0x7b70f500,0x7f75f300,0x837af100,0x887fef00,0x8884ed02,0x8888eb04,0x888ce906,0x8890e708,0x8896e206,0x889cde04,0x88a2da02,0x88a9d600,0x8399de00,
+ 0x00b1d97c,0x747be900,0xfae9ebff,0xebdedbff,0xddd4ccff,0xcecabcff,0xbfbfadff,0xc3c0b1fc,0xc7c1b6f9,0xccc2baf6,0xd0c3bff3,0xcec3bff3,0xccc3bff3,0xc9c3bff3,0xc7c3bff3,0xd4cdcaf6,
+ 0x00c7ea7f,0x6e7de200,0x666bef00,0x6e75ef00,0x777fef00,0x7f8aef00,0x8894ef00,0x889ceb04,0x88a5e708,0x88ade20c,0x88b5de10,0x88b5d60c,0x88b5ce08,0x88b5c604,0x88b5bd00,0x7fa2ca00,
+ 0x7b91d617,0x00b5e681,0x00aaed7f,0x00a9e57a,0x00a9de75,0x00a8d76f,0x00a9d06a,0x00b5cf6e,0x00c1ce72,0x00cecd76,0x00dacc7b,0x00dace7e,0x00dad082,0x00dad185,0x00dad488,0x00cdda86,
+ };
+ UInt32 res[kSize*kSize];
+ DecompressPVRTC<false,true> (reinterpret_cast<const PVRTCBlock*>(src), kSize, kSize, reinterpret_cast<UInt8*>(res));
+ CHECK_ARRAY_EQUAL (expected, res, kSize*kSize);
+ }
+ TEST (DecodePVRTC_4_8x8)
+ {
+ const int kSize = 8;
+ UInt32 src[kSize*kSize*4/32] = {
+ 0x4c4c4c4c,0x63fb3494,0x00fc4c4c,0x68fc2352,0xa9fefefe,0x5bac1078,0x00ff5555,0x68dc2072,
+ };
+ UInt32 expected[kSize*kSize] = {
+ 0x444a751c,0xc7bff170,0x55357739,0x7a6fa444,0x9394b95b,0xbfc3d493,0xbbc6c6a5,0xbfc3d493,0x445e7e1e,0xc5bcee61,0x5d3f8a3d,0x7b77ab41,0x909ab858,0xb8c2cb96,0xb2c6b9b1,0xb8c2cb96,
+ 0x44738821,0xc3b9eb54,0x664a9c42,0x7e80b33e,0x8ea1b856,0xb2c1c19a,0xaac6adbd,0xb2c1c19a,0x445e7e1e,0xc5bcee61,0x5d3f8a3d,0x7b77ab41,0x7282a141,0x87a2ab63,0x7faa9e6e,0x87a2ab63,
+ 0x444a751c,0xc7bff170,0x55357739,0x7a6fa444,0x73769d41,0x6c7d983f,0x6685923d,0x6c7d983f,0x44356c1a,0xc9c2f47d,0x4c2b6535,0x78669c47,0x756a9a42,0x716d983d,0x6e719639,0x716d983d,
+ 0x44216318,0xccc6f78c,0xccc6ff8c,0xccc6f78c,0xccc6ef8c,0xccc6e78c,0xccc6de8c,0xccc6e78c,0x44356c1a,0x48306828,0x4c2b6535,0x48306828,0x44356c1a,0x3f3a6f0d,0x3b3f7300,0x3f3a6f0d,
+ };
+ UInt32 res[kSize*kSize];
+ DecompressPVRTC<false,true> (reinterpret_cast<const PVRTCBlock*>(src), kSize, kSize, reinterpret_cast<UInt8*>(res));
+ CHECK_ARRAY_EQUAL (expected, res, kSize*kSize);
+ }
+ TEST (DecodePVRTC_4_8x16)
+ {
+ const int kSizeX = 8;
+ const int kSizeY = 16;
+ UInt32 src[kSizeX*kSizeY*4/32] = {
+ 0x4c4c4c4c,0x63fb3494,0x00fc4c4c,0x68fc2352,0xa9fefefe,0x5bac1078,0x00ff5555,0x68dc2072,
+ 0x32323232,0x7faa40f7,0xa802f232,0xffff30e7,0xff030303,0x0f0040e6,0xaa00ff00,0xff9f40e9,
+ };
+ UInt32 expected[kSizeX*kSizeY] = {
+ 0x5d79bb10,0xe1dcf2aa,0x665ac621,0x9194d24e,0xadb8d47a,0xd8e0d7cd,0xd4e2cade,0xd8e0d7cd,0x5076a118,0xd2cbee7e,0x6652b131,0x878ac246,0x9dadc668,0xc5d1ccb3,0xbfd4bbce,0xc5d1ccb3,
+ 0x44738821,0xc3b9eb54,0x664a9c42,0x7e80b33e,0x8ea1b856,0xb2c1c19a,0xaac6adbd,0xb2c1c19a,0x445e7e1e,0xc5bcee61,0x5d3f8a3d,0x7b77ab41,0x7282a141,0x87a2ab63,0x7faa9e6e,0x87a2ab63,
+ 0x444a751c,0xc7bff170,0x55357739,0x7a6fa444,0x73769d41,0x6c7d983f,0x6685923d,0x6c7d983f,0x44356c1a,0xc9c2f47d,0x4c2b6535,0x78669c47,0x756a9a42,0x716d983d,0x6e719639,0x716d983d,
+ 0x44216318,0xccc6f78c,0xccc6ff8c,0xccc6f78c,0xccc6ef8c,0xccc6e78c,0xccc6de8c,0xccc6e78c,0x55338812,0x5533821b,0x55337d25,0x5533821b,0x55338812,0x55338d09,0x55339200,0x55338d09,
+ 0x0069a769,0x6646ab12,0xddb5d6c6,0x6646ab12,0xa18ca2c6,0x6646af06,0x6646b100,0x6646af06,0x0063a774,0x7758d309,0xe5adc1e2,0x7758d309,0x8c6f7ce2,0x7758d103,0x7758d000,0x7758d103,
+ 0x005ea67f,0x886bfb00,0xeea5adff,0x886bfb00,0x775256ff,0x886bf300,0x886bef00,0x886bf300,0x0076b97f,0x816df800,0xf2bbc1ff,0x816df800,0x997d7dff,0x6c5e5bff,0x3f3f39ff,0x6c5e5bff,
+ 0x008fcc7f,0x7b70f500,0xf6d2d6ff,0x7b70f500,0x7f75f300,0x837af100,0x887fef00,0x837af100,0x00a7de7f,0x7472f200,0xfae9ebff,0xebdedbff,0xddd4ccff,0xcecabcff,0xbfbfadff,0xcecabcff,
+ 0x00bff17f,0x6e75ef00,0x666bef00,0x6e75ef00,0x777fef00,0x7f8aef00,0x8894ef00,0x7f8aef00,0x6a7cd508,0x00aee670,0x00a8ec6e,0x00aee670,0x00b5e072,0x00bcd974,0x00c3d477,0x00bcd974,
+ };
+ UInt32 res[kSizeX*kSizeY];
+ DecompressPVRTC<false,true> (reinterpret_cast<const PVRTCBlock*>(src), kSizeX, kSizeY, reinterpret_cast<UInt8*>(res));
+ CHECK_ARRAY_EQUAL (expected, res, kSizeX*kSizeY);
+ }
+ TEST (DecodePVRTC_4_16x8)
+ {
+ const int kSizeX = 16;
+ const int kSizeY = 8;
+ UInt32 src[kSizeX*kSizeY*4/32] = {
+ 0x4c4c4c4c,0x63fb3494,0x00fc4c4c,0x68fc2352,0xa9fefefe,0x5bac1078,0x00ff5555,0x68dc2072,
+ 0x32323232,0x7faa40f7,0xa802f232,0xffff30e7,0xff030303,0x0f0040e6,0xaa00ff00,0xff9f40e9,
+ };
+ UInt32 expected[kSizeX*kSizeY] = {
+ 0x6e5ab31c,0xb8aedc87,0x55357739,0x7a6fa444,0x9394b95b,0xbfc3d493,0xbbc6c6a5,0xc9c9cabb,0x0098c169,0x6668d600,0xf6d2d6ff,0x7b70f500,0xbba9a5ff,0x837af100,0x887fef00,0x7b6dd10e,
+ 0x725abc1e,0xa89ace75,0x5d3f8a3d,0x7b77ab41,0x909ab858,0xb8c2cb96,0xb2c6b9b1,0xc2c2bbc4,0x009aba6c,0x6a6fd900,0xf2bbc1ff,0x816df800,0x997d7dff,0x8572f200,0x8875ef00,0x7d68d50f,
+ 0x775ac621,0x9988bf65,0x664a9c42,0x7e80b33e,0x8ea1b856,0xb2c1c19a,0xaac6adbd,0xbbbdadce,0x009cb36f,0x6e77dc00,0xeea5adff,0x886bfb00,0x775256ff,0x886bf300,0x886bef00,0x7f63da10,
+ 0x725abc1e,0xa89ace75,0x5d3f8a3d,0x7b77ab41,0x7282a141,0x87a2ab63,0x7faa9e6e,0x90a6ac7a,0x009aba6c,0x6a6fd900,0xf2bbc1ff,0x816df800,0x997d7dff,0x6c5e5bff,0x3f3f39ff,0x625d6bd1,
+ 0x6e5ab31c,0xb8aedc87,0x55357739,0x7a6fa444,0x73769d41,0x6c7d983f,0x6685923d,0x7588a846,0x0098c169,0x6668d600,0xf6d2d6ff,0x7b70f500,0x7f75f300,0x837af100,0x887fef00,0x7b6dd10e,
+ 0x6a5aaa1a,0xc8c0eb97,0x4c2b6535,0x78669c47,0x756a9a42,0x716d983d,0x6e719639,0x7b7bac42,0x0096c866,0x615fd300,0xfae9ebff,0xebdedbff,0xddd4ccff,0xcecabcff,0xbfbfadff,0xc2bfc1dc,
+ 0x665aa018,0xd8d4f9a9,0xccc6ff8c,0xccc6f78c,0xccc6ef8c,0xccc6e78c,0xccc6de8c,0xd8d4e7a9,0x0094d063,0x5d58d000,0x666bef00,0x6e75ef00,0x777fef00,0x7f8aef00,0x8894ef00,0x7777c80c,
+ 0x6a5aaa1a,0x5b438728,0x4c2b6535,0x48306828,0x44356c1a,0x3f3a6f0d,0x3b3f7300,0x484a9300,0x5555b300,0x009fdb72,0x00aaef7f,0x00a8e67f,0x00a7de7f,0x00a5d67f,0x00a4ce7f,0x0098c674,
+ };
+ UInt32 res[kSizeX*kSizeY];
+ DecompressPVRTC<false,true> (reinterpret_cast<const PVRTCBlock*>(src), kSizeX, kSizeY, reinterpret_cast<UInt8*>(res));
+ CHECK_ARRAY_EQUAL (expected, res, kSizeX*kSizeY);
+ }
+ TEST (TwiddleUVPVRTC)
+ {
+ // 00000000, 11111111 = 0101010101010101
+ CHECK_EQUAL (0x5555, TwiddleUVPVRTC (0x100, 0x100, 0xFF, 0x00));
+ // 00011011, 11110000 = 0101011110001010
+ CHECK_EQUAL (0x578A, TwiddleUVPVRTC (0x100, 0x100, 0xF0, 0x1B));
+
+ // 10100000, 1111 = 101001010101
+ CHECK_EQUAL (0xA55, TwiddleUVPVRTC (0x10, 0x100, 0xF, 0xA0));
+ // 0000, 11101111 = 111001010101
+ CHECK_EQUAL (0xE55, TwiddleUVPVRTC (0x100, 0x10, 0xEF, 0x0));
+ }
+ #endif // HAS_PVRTC_DECOMPRESSOR
+ #endif
+
+} // SUITE
+
+#endif // ENABLE_UNIT_TESTS