/****************************************************************************** @File PVRTDecompress.cpp @Title PVRTDecompress @Version @Copyright Copyright (c) Imagination Technologies Limited. @Platform ANSI compatible @Description PVRTC Texture Decompression. ******************************************************************************/ /***************************************************************************** * INCLUDES *****************************************************************************/ #include <stdlib.h> #include <stdio.h> #include <limits.h> #include <math.h> #include <string.h> #include "PVRTDecompress.h" #include "PVRTTexture.h" #include "PVRTGlobal.h" /*********************************************************** DECOMPRESSION ROUTINES ************************************************************/ /***************************************************************************** * Useful structs *****************************************************************************/ struct Pixel32 { PVRTuint8 red,green,blue,alpha; }; struct Pixel128S { PVRTint32 red,green,blue,alpha; }; struct PVRTCWord { PVRTuint32 u32ModulationData; PVRTuint32 u32ColourData; }; struct PVRTCWordIndices { int P[2], Q[2], R[2], S[2]; }; /********************************************************************************/ /*!*********************************************************************** @Function getColourA @Input u32ColourData Colour information from a PVRTCWord. @Return Returns the first colour in a PVRTCWord's colour data. @Description Decodes the first colour in a PVRTCWord's colour data. *************************************************************************/ static Pixel32 getColourA(PVRTuint32 u32ColourData) { Pixel32 colour; // Opaque Colour Mode - RGB 554 if ((u32ColourData & 0x8000) != 0) { colour.red = (PVRTuint8)((u32ColourData & 0x7c00) >> 10); // 5->5 bits colour.green = (PVRTuint8)((u32ColourData & 0x3e0) >> 5); // 5->5 bits colour.blue = (PVRTuint8)(u32ColourData & 0x1e) | ((u32ColourData & 0x1e) >> 4); // 4->5 bits colour.alpha = (PVRTuint8)0xf;// 0->4 bits } // Transparent Colour Mode - ARGB 3443 else { colour.red = (PVRTuint8)((u32ColourData & 0xf00) >> 7) | ((u32ColourData & 0xf00) >> 11); // 4->5 bits colour.green = (PVRTuint8)((u32ColourData & 0xf0) >> 3) | ((u32ColourData & 0xf0) >> 7); // 4->5 bits colour.blue = (PVRTuint8)((u32ColourData & 0xe) << 1) | ((u32ColourData & 0xe) >> 2); // 3->5 bits colour.alpha = (PVRTuint8)((u32ColourData & 0x7000) >> 11);// 3->4 bits - note 0 at right } return colour; } /*!*********************************************************************** @Function getColourB @Input u32ColourData Colour information from a PVRTCWord. @Return Returns the second colour in a PVRTCWord's colour data. @Description Decodes the second colour in a PVRTCWord's colour data. *************************************************************************/ static Pixel32 getColourB(PVRTuint32 u32ColourData) { Pixel32 colour; // Opaque Colour Mode - RGB 555 if (u32ColourData & 0x80000000) { colour.red = (PVRTuint8)((u32ColourData & 0x7c000000) >> 26); // 5->5 bits colour.green = (PVRTuint8)((u32ColourData & 0x3e00000) >> 21); // 5->5 bits colour.blue = (PVRTuint8)((u32ColourData & 0x1f0000) >> 16); // 5->5 bits colour.alpha = (PVRTuint8)0xf;// 0 bits } // Transparent Colour Mode - ARGB 3444 else { colour.red = (PVRTuint8)(((u32ColourData & 0xf000000) >> 23) | ((u32ColourData & 0xf000000) >> 27)); // 4->5 bits colour.green = (PVRTuint8)(((u32ColourData & 0xf00000) >> 19) | ((u32ColourData & 0xf00000) >> 23)); // 4->5 bits colour.blue = (PVRTuint8)(((u32ColourData & 0xf0000) >> 15) | ((u32ColourData & 0xf0000) >> 19)); // 4->5 bits colour.alpha = (PVRTuint8)((u32ColourData & 0x70000000) >> 27);// 3->4 bits - note 0 at right } return colour; } /*!*********************************************************************** @Function interpolateColours @Input P,Q,R,S Low bit-rate colour values for each PVRTCWord. @Modified pPixel Output array for upscaled colour values. @Input ui8Bpp Number of bpp. @Description Bilinear upscale from 2x2 pixels to 4x4/8x4 pixels (depending on PVRTC bpp mode). *************************************************************************/ static void interpolateColours(Pixel32 P, Pixel32 Q, Pixel32 R, Pixel32 S, Pixel128S *pPixel, PVRTuint8 ui8Bpp) { PVRTuint32 ui32WordWidth=4; PVRTuint32 ui32WordHeight=4; if (ui8Bpp==2) ui32WordWidth=8; //Convert to int 32. Pixel128S hP = {(PVRTint32)P.red,(PVRTint32)P.green,(PVRTint32)P.blue,(PVRTint32)P.alpha}; Pixel128S hQ = {(PVRTint32)Q.red,(PVRTint32)Q.green,(PVRTint32)Q.blue,(PVRTint32)Q.alpha}; Pixel128S hR = {(PVRTint32)R.red,(PVRTint32)R.green,(PVRTint32)R.blue,(PVRTint32)R.alpha}; Pixel128S hS = {(PVRTint32)S.red,(PVRTint32)S.green,(PVRTint32)S.blue,(PVRTint32)S.alpha}; //Get vectors. Pixel128S QminusP = {hQ.red - hP.red, hQ.green - hP.green, hQ.blue - hP.blue, hQ.alpha - hP.alpha}; Pixel128S SminusR = {hS.red - hR.red, hS.green - hR.green, hS.blue - hR.blue, hS.alpha - hR.alpha}; //Multiply colours. hP.red *= ui32WordWidth; hP.green *= ui32WordWidth; hP.blue *= ui32WordWidth; hP.alpha *= ui32WordWidth; hR.red *= ui32WordWidth; hR.green *= ui32WordWidth; hR.blue *= ui32WordWidth; hR.alpha *= ui32WordWidth; if (ui8Bpp==2) { //Loop through pixels to achieve results. for (unsigned int x=0; x < ui32WordWidth; x++) { Pixel128S Result={4*hP.red, 4*hP.green, 4*hP.blue, 4*hP.alpha}; Pixel128S dY = {hR.red - hP.red, hR.green - hP.green, hR.blue - hP.blue, hR.alpha - hP.alpha}; for (unsigned int y=0; y < ui32WordHeight; y++) { pPixel[y*ui32WordWidth+x].red = (PVRTint32)((Result.red >> 7) + (Result.red >> 2)); pPixel[y*ui32WordWidth+x].green = (PVRTint32)((Result.green >> 7) + (Result.green >> 2)); pPixel[y*ui32WordWidth+x].blue = (PVRTint32)((Result.blue >> 7) + (Result.blue >> 2)); pPixel[y*ui32WordWidth+x].alpha = (PVRTint32)((Result.alpha >> 5) + (Result.alpha >> 1)); Result.red += dY.red; Result.green += dY.green; Result.blue += dY.blue; Result.alpha += dY.alpha; } hP.red += QminusP.red; hP.green += QminusP.green; hP.blue += QminusP.blue; hP.alpha += QminusP.alpha; hR.red += SminusR.red; hR.green += SminusR.green; hR.blue += SminusR.blue; hR.alpha += SminusR.alpha; } } else { //Loop through pixels to achieve results. for (unsigned int y=0; y < ui32WordHeight; y++) { Pixel128S Result={4*hP.red, 4*hP.green, 4*hP.blue, 4*hP.alpha}; Pixel128S dY = {hR.red - hP.red, hR.green - hP.green, hR.blue - hP.blue, hR.alpha - hP.alpha}; for (unsigned int x=0; x < ui32WordWidth; x++) { pPixel[y*ui32WordWidth+x].red = (PVRTint32)((Result.red >> 6) + (Result.red >> 1)); pPixel[y*ui32WordWidth+x].green = (PVRTint32)((Result.green >> 6) + (Result.green >> 1)); pPixel[y*ui32WordWidth+x].blue = (PVRTint32)((Result.blue >> 6) + (Result.blue >> 1)); pPixel[y*ui32WordWidth+x].alpha = (PVRTint32)((Result.alpha >> 4) + (Result.alpha)); Result.red += dY.red; Result.green += dY.green; Result.blue += dY.blue; Result.alpha += dY.alpha; } hP.red += QminusP.red; hP.green += QminusP.green; hP.blue += QminusP.blue; hP.alpha += QminusP.alpha; hR.red += SminusR.red; hR.green += SminusR.green; hR.blue += SminusR.blue; hR.alpha += SminusR.alpha; } } } /*!*********************************************************************** @Function unpackModulations @Input word PVRTCWord to be decompressed @Input offsetX X position within the PVRTCWord @Input offsetY Y position within the PVRTCWord @Modified i32ModulationValues The array of modulation values. @Modified i32ModulationModes The array of modulation modes. @Input ui8Bpp Number of bpp. @Description Reads out and decodes the modulation values within the a given PVRTCWord *************************************************************************/ static void unpackModulations(const PVRTCWord& word, int offsetX, int offsetY, PVRTint32 i32ModulationValues[16][8], PVRTint32 i32ModulationModes[16][8], PVRTuint8 ui8Bpp) { PVRTuint32 WordModMode = word.u32ColourData & 0x1; PVRTuint32 ModulationBits = word.u32ModulationData; // Unpack differently depending on 2bpp or 4bpp modes. if (ui8Bpp==2) { if(WordModMode) { // determine which of the three modes are in use: // If this is the either the H-only or V-only interpolation mode... if(ModulationBits & 0x1) { // look at the "LSB" for the "centre" (V=2,H=4) texel. Its LSB is now // actually used to indicate whether it's the H-only mode or the V-only... // The centre texel data is the at (y==2, x==4) and so its LSB is at bit 20. if(ModulationBits & (0x1 << 20)) { // This is the V-only mode WordModMode = 3; } else { // This is the H-only mode WordModMode = 2; } // Create an extra bit for the centre pixel so that it looks like // we have 2 actual bits for this texel. It makes later coding much easier. if(ModulationBits & (0x1 << 21)) { // set it to produce code for 1.0 ModulationBits |= (0x1 << 20); } else { // clear it to produce 0.0 code ModulationBits &= ~(0x1 << 20); } }// end if H-Only or V-Only interpolation mode was chosen if(ModulationBits & 0x2) { ModulationBits |= 0x1; /*set it*/ } else { ModulationBits &= ~0x1; /*clear it*/ } // 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 < 4; y++) { for(int x = 0; x < 8; x++) { i32ModulationModes[x+offsetX][y+offsetY] = WordModMode; // if this is a stored value... if(((x^y)&1) == 0) { i32ModulationValues[x+offsetX][y+offsetY] = ModulationBits & 3; ModulationBits >>= 2; } } } // end for y } // else if direct encoded 2bit mode - i.e. 1 mode bit per pixel else { for(int y = 0; y < 4; y++) { for(int x = 0; x < 8; x++) { i32ModulationModes[x+offsetX][y+offsetY] = WordModMode; /* // double the bits so 0=> 00, and 1=>11 */ if(ModulationBits & 1) { i32ModulationValues[x+offsetX][y+offsetY] = 0x3; } else { i32ModulationValues[x+offsetX][y+offsetY] = 0x0; } ModulationBits >>= 1; } }// end for y } } else { //Much simpler than the 2bpp decompression, only two modes, so the n/8 values are set directly. // run through all the pixels in the word. if (WordModMode) { for(int y = 0; y < 4; y++) { for(int x = 0; x < 4; x++) { i32ModulationValues[y+offsetY][x+offsetX] = ModulationBits & 3; //if (i32ModulationValues==0) {}; don't need to check 0, 0 = 0/8. if (i32ModulationValues[y+offsetY][x+offsetX]==1) { i32ModulationValues[y+offsetY][x+offsetX]=4;} else if (i32ModulationValues[y+offsetY][x+offsetX]==2) { i32ModulationValues[y+offsetY][x+offsetX]=14;} //+10 tells the decompressor to punch through alpha. else if (i32ModulationValues[y+offsetY][x+offsetX]==3) { i32ModulationValues[y+offsetY][x+offsetX]=8;} ModulationBits >>= 2; } // end for x } // end for y } else { for(int y = 0; y < 4; y++) { for(int x = 0; x < 4; x++) { i32ModulationValues[y+offsetY][x+offsetX] = ModulationBits & 3; i32ModulationValues[y+offsetY][x+offsetX]*=3; if (i32ModulationValues[y+offsetY][x+offsetX]>3) i32ModulationValues[y+offsetY][x+offsetX]-=1; ModulationBits >>= 2; } // end for x } // end for y } } } /*!*********************************************************************** @Function getModulationValues @Input i32ModulationValues The array of modulation values. @Input i32ModulationModes The array of modulation modes. @Input xPos The x Position within the current word. @Input yPos The y Position within the current word. @Input ui8Bpp Number of bpp. @Return Returns the modulation value. @Description Gets the effective modulation values for a given pixel. *************************************************************************/ static PVRTint32 getModulationValues(PVRTint32 i32ModulationValues[16][8],PVRTint32 i32ModulationModes[16][8],PVRTuint32 xPos,PVRTuint32 yPos,PVRTuint8 ui8Bpp) { if (ui8Bpp==2) { const int RepVals0[4] = {0, 3, 5, 8}; // extract the modulation value. If a simple encoding if(i32ModulationModes[xPos][yPos]==0) { return RepVals0[i32ModulationValues[xPos][yPos]]; } else { // if this is a stored value if(((xPos^yPos)&1)==0) { return RepVals0[i32ModulationValues[xPos][yPos]]; } // else average from the neighbours // if H&V interpolation... else if(i32ModulationModes[xPos][yPos] == 1) { return (RepVals0[i32ModulationValues[xPos][yPos-1]] + RepVals0[i32ModulationValues[xPos][yPos+1]] + RepVals0[i32ModulationValues[xPos-1][yPos]] + RepVals0[i32ModulationValues[xPos+1][yPos]] + 2) / 4; } // else if H-Only else if(i32ModulationModes[xPos][yPos] == 2) { return (RepVals0[i32ModulationValues[xPos-1][yPos]] + RepVals0[i32ModulationValues[xPos+1][yPos]] + 1) / 2; } // else it's V-Only else { return (RepVals0[i32ModulationValues[xPos][yPos-1]] + RepVals0[i32ModulationValues[xPos][yPos+1]] + 1) / 2; } } } else if (ui8Bpp==4) return i32ModulationValues[xPos][yPos]; return 0; } /*!*********************************************************************** @Function pvrtcGetDecompressedPixels @Input P,Q,R,S PVRTWords in current decompression area. @Modified pColourData Output pixels. @Input ui8Bpp Number of bpp. @Description Gets decompressed pixels for a given decompression area. *************************************************************************/ static void pvrtcGetDecompressedPixels(const PVRTCWord& P, const PVRTCWord& Q, const PVRTCWord& R, const PVRTCWord& S, Pixel32 *pColourData, PVRTuint8 ui8Bpp) { //4bpp only needs 8*8 values, but 2bpp needs 16*8, so rather than wasting processor time we just statically allocate 16*8. PVRTint32 i32ModulationValues[16][8]; //Only 2bpp needs this. PVRTint32 i32ModulationModes[16][8]; //4bpp only needs 16 values, but 2bpp needs 32, so rather than wasting processor time we just statically allocate 32. Pixel128S upscaledColourA[32]; Pixel128S upscaledColourB[32]; PVRTuint32 ui32WordWidth=4; PVRTuint32 ui32WordHeight=4; if (ui8Bpp==2) ui32WordWidth=8; //Get the modulations from each word. unpackModulations(P, 0, 0, i32ModulationValues, i32ModulationModes, ui8Bpp); unpackModulations(Q, ui32WordWidth, 0, i32ModulationValues, i32ModulationModes, ui8Bpp); unpackModulations(R, 0, ui32WordHeight, i32ModulationValues, i32ModulationModes, ui8Bpp); unpackModulations(S, ui32WordWidth, ui32WordHeight, i32ModulationValues, i32ModulationModes, ui8Bpp); // Bilinear upscale image data from 2x2 -> 4x4 interpolateColours(getColourA(P.u32ColourData), getColourA(Q.u32ColourData), getColourA(R.u32ColourData), getColourA(S.u32ColourData), upscaledColourA, ui8Bpp); interpolateColours(getColourB(P.u32ColourData), getColourB(Q.u32ColourData), getColourB(R.u32ColourData), getColourB(S.u32ColourData), upscaledColourB, ui8Bpp); for (unsigned int y=0; y < ui32WordHeight; y++) { for (unsigned int x=0; x < ui32WordWidth; x++) { PVRTint32 mod = getModulationValues(i32ModulationValues,i32ModulationModes,x+ui32WordWidth/2,y+ui32WordHeight/2,ui8Bpp); bool punchthroughAlpha=false; if (mod>10) {punchthroughAlpha=true; mod-=10;} Pixel128S result; result.red = (upscaledColourA[y*ui32WordWidth+x].red * (8-mod) + upscaledColourB[y*ui32WordWidth+x].red * mod) / 8; result.green = (upscaledColourA[y*ui32WordWidth+x].green * (8-mod) + upscaledColourB[y*ui32WordWidth+x].green * mod) / 8; result.blue = (upscaledColourA[y*ui32WordWidth+x].blue * (8-mod) + upscaledColourB[y*ui32WordWidth+x].blue * mod) / 8; if (punchthroughAlpha) result.alpha = 0; else result.alpha = (upscaledColourA[y*ui32WordWidth+x].alpha * (8-mod) + upscaledColourB[y*ui32WordWidth+x].alpha * mod) / 8; //Convert the 32bit precision result to 8 bit per channel colour. if (ui8Bpp==2) { pColourData[y*ui32WordWidth+x].red = (PVRTuint8)result.red; pColourData[y*ui32WordWidth+x].green = (PVRTuint8)result.green; pColourData[y*ui32WordWidth+x].blue = (PVRTuint8)result.blue; pColourData[y*ui32WordWidth+x].alpha = (PVRTuint8)result.alpha; } else if (ui8Bpp==4) { pColourData[y+x*ui32WordHeight].red = (PVRTuint8)result.red; pColourData[y+x*ui32WordHeight].green = (PVRTuint8)result.green; pColourData[y+x*ui32WordHeight].blue = (PVRTuint8)result.blue; pColourData[y+x*ui32WordHeight].alpha = (PVRTuint8)result.alpha; } } } } /*!*********************************************************************** @Function wrapWordIndex @Input numWords Total number of PVRTCWords in the current surface. @Input word Original index for a PVRTCWord. @Return unsigned int Wrapped PVRTCWord index. @Description Maps decompressed data to the correct location in the output buffer. *************************************************************************/ static unsigned int wrapWordIndex(unsigned int numWords, int word) { return ((word + numWords) % numWords); } #if defined(_DEBUG) /*!*********************************************************************** @Function isPowerOf2 @Input input Value to be checked @Returns true if the number is an integer power of two, else false. @Description Check that a number is an integer power of two, i.e. 1, 2, 4, 8, ... etc. Returns false for zero. *************************************************************************/ static bool isPowerOf2( unsigned int input ) { unsigned int minus1; if( !input ) return 0; minus1 = input - 1; return ( (input | minus1) == (input ^ minus1) ); } #endif /*!*********************************************************************** @Function TwiddleUV @Input YSize Y dimension of the texture in pixels @Input XSize X dimension of the texture in pixels @Input YPos Pixel Y position @Input XPos Pixel X position @Returns The twiddled offset of the pixel @Description Given the Word (or pixel) coordinates and the dimension of the texture in words (or pixels) this returns the twiddled offset of the word (or pixel) from the start of the map. NOTE: the dimensions of the texture must be a power of 2 *************************************************************************/ static PVRTuint32 TwiddleUV(PVRTuint32 XSize, PVRTuint32 YSize, PVRTuint32 XPos, PVRTuint32 YPos) { //Initially assume X is the larger size. PVRTuint32 MinDimension=XSize; PVRTuint32 MaxValue=YPos; PVRTuint32 Twiddled=0; PVRTuint32 SrcBitPos=1; PVRTuint32 DstBitPos=1; int ShiftCount=0; //Check the sizes are valid. _ASSERT(YPos < YSize); _ASSERT(XPos < XSize); _ASSERT(isPowerOf2(YSize)); _ASSERT(isPowerOf2(XSize)); //If Y is the larger dimension - switch the min/max values. if(YSize < XSize) { MinDimension = YSize; MaxValue = XPos; } // Step through all the bits in the "minimum" dimension while(SrcBitPos < MinDimension) { if(YPos & SrcBitPos) { Twiddled |= DstBitPos; } if(XPos & SrcBitPos) { Twiddled |= (DstBitPos << 1); } SrcBitPos <<= 1; DstBitPos <<= 2; ShiftCount += 1; } // Prepend any unused bits MaxValue >>= ShiftCount; Twiddled |= (MaxValue << (2*ShiftCount)); return Twiddled; } /*!*********************************************************************** @Function mapDecompressedData @Modified pOutput The PVRTC texture data to decompress @Input width Width of the texture surface. @Input pWord A pointer to the decompressed PVRTCWord in pixel form. @Input &words Indices for the PVRTCword. @Input ui8Bpp number of bits per pixel @Description Maps decompressed data to the correct location in the output buffer. *************************************************************************/ static void mapDecompressedData(Pixel32* pOutput, int width, const Pixel32 *pWord, const PVRTCWordIndices &words, const PVRTuint8 ui8Bpp) { PVRTuint32 ui32WordWidth=4; PVRTuint32 ui32WordHeight=4; if (ui8Bpp==2) ui32WordWidth=8; for (unsigned int y=0; y < ui32WordHeight/2; y++) { for (unsigned int x=0; x < ui32WordWidth/2; x++) { pOutput[(((words.P[1] * ui32WordHeight) + y + ui32WordHeight/2) * width + words.P[0] *ui32WordWidth + x + ui32WordWidth/2)] = pWord[y*ui32WordWidth+x]; // map P pOutput[(((words.Q[1] * ui32WordHeight) + y + ui32WordHeight/2) * width + words.Q[0] *ui32WordWidth + x)] = pWord[y*ui32WordWidth+x+ui32WordWidth/2]; // map Q pOutput[(((words.R[1] * ui32WordHeight) + y) * width + words.R[0] *ui32WordWidth + x + ui32WordWidth/2)] = pWord[(y+ui32WordHeight/2)*ui32WordWidth+x]; // map R pOutput[(((words.S[1] * ui32WordHeight) + y) * width + words.S[0] *ui32WordWidth + x)] = pWord[(y+ui32WordHeight/2)*ui32WordWidth+x+ui32WordWidth/2]; // map S } } } /*!*********************************************************************** @Function pvrtcDecompress @Input pCompressedData The PVRTC texture data to decompress @Modified pDecompressedData The output buffer to decompress into. @Input ui32Width X dimension of the texture @Input ui32Height Y dimension of the texture @Input ui8Bpp number of bits per pixel @Description Internally decompresses PVRTC to RGBA 8888 *************************************************************************/ static int pvrtcDecompress( PVRTuint8 *pCompressedData, Pixel32 *pDecompressedData, PVRTuint32 ui32Width, PVRTuint32 ui32Height, PVRTuint8 ui8Bpp) { PVRTuint32 ui32WordWidth=4; PVRTuint32 ui32WordHeight=4; if (ui8Bpp==2) ui32WordWidth=8; PVRTuint32 *pWordMembers = (PVRTuint32 *)pCompressedData; Pixel32 *pOutData = pDecompressedData; // Calculate number of words int i32NumXWords = (int)(ui32Width / ui32WordWidth); int i32NumYWords = (int)(ui32Height / ui32WordHeight); // Structs used for decompression PVRTCWordIndices indices; Pixel32 *pPixels; pPixels = (Pixel32*)malloc(ui32WordWidth*ui32WordHeight*sizeof(Pixel32)); // For each row of words for(int wordY=-1; wordY < i32NumYWords-1; wordY++) { // for each column of words for(int wordX=-1; wordX < i32NumXWords-1; wordX++) { indices.P[0] = wrapWordIndex(i32NumXWords, wordX); indices.P[1] = wrapWordIndex(i32NumYWords, wordY); indices.Q[0] = wrapWordIndex(i32NumXWords, wordX + 1); indices.Q[1] = wrapWordIndex(i32NumYWords, wordY); indices.R[0] = wrapWordIndex(i32NumXWords, wordX); indices.R[1] = wrapWordIndex(i32NumYWords, wordY + 1); indices.S[0] = wrapWordIndex(i32NumXWords, wordX + 1); indices.S[1] = wrapWordIndex(i32NumYWords, wordY + 1); //Work out the offsets into the twiddle structs, multiply by two as there are two members per word. PVRTuint32 WordOffsets[4] = { TwiddleUV(i32NumXWords,i32NumYWords,indices.P[0], indices.P[1])*2, TwiddleUV(i32NumXWords,i32NumYWords,indices.Q[0], indices.Q[1])*2, TwiddleUV(i32NumXWords,i32NumYWords,indices.R[0], indices.R[1])*2, TwiddleUV(i32NumXWords,i32NumYWords,indices.S[0], indices.S[1])*2, }; //Access individual elements to fill out PVRTCWord PVRTCWord P,Q,R,S; P.u32ColourData = pWordMembers[WordOffsets[0]+1]; P.u32ModulationData = pWordMembers[WordOffsets[0]]; Q.u32ColourData = pWordMembers[WordOffsets[1]+1]; Q.u32ModulationData = pWordMembers[WordOffsets[1]]; R.u32ColourData = pWordMembers[WordOffsets[2]+1]; R.u32ModulationData = pWordMembers[WordOffsets[2]]; S.u32ColourData = pWordMembers[WordOffsets[3]+1]; S.u32ModulationData = pWordMembers[WordOffsets[3]]; // assemble 4 words into struct to get decompressed pixels from pvrtcGetDecompressedPixels(P,Q,R,S,pPixels,ui8Bpp); mapDecompressedData(pOutData, ui32Width, pPixels, indices, ui8Bpp); } // for each word } // for each row of words free(pPixels); //Return the data size return ui32Width * ui32Height / (PVRTuint32)(ui32WordWidth/2); } /*!*********************************************************************** @Function PVRTDecompressPVRTC @Input pCompressedData The PVRTC texture data to decompress @Input Do2bitMode Signifies whether the data is PVRTC2 or PVRTC4 @Input XDim X dimension of the texture @Input YDim Y dimension of the texture @Modified pResultImage The decompressed texture data @Return Returns the amount of data that was decompressed. @Description Decompresses PVRTC to RGBA 8888 *************************************************************************/ int PVRTDecompressPVRTC(const void *pCompressedData, const int Do2bitMode, const int XDim, const int YDim, unsigned char* pResultImage) { //Cast the output buffer to a Pixel32 pointer. Pixel32* pDecompressedData = (Pixel32*)pResultImage; //Check the X and Y values are at least the minimum size. int XTrueDim = PVRT_MAX(XDim,((Do2bitMode==1)?16:8)); int YTrueDim = PVRT_MAX(YDim,8); //If the dimensions aren't correct, we need to create a new buffer instead of just using the provided one, as the buffer will overrun otherwise. if(XTrueDim!=XDim || YTrueDim!=YDim) { pDecompressedData=(Pixel32*)malloc(XTrueDim*YTrueDim*sizeof(Pixel32)); } //Decompress the surface. int retval = pvrtcDecompress((PVRTuint8*)pCompressedData,pDecompressedData,XTrueDim,YTrueDim,(Do2bitMode==1?2:4)); //If the dimensions were too small, then copy the new buffer back into the output buffer. if(XTrueDim!=XDim || YTrueDim!=YDim) { //Loop through all the required pixels. for (int x=0; x<XDim; ++x) { for (int y=0; y<YDim; ++y) { ((Pixel32*)pResultImage)[x+y*XDim]=pDecompressedData[x+y*XTrueDim]; } } //Free the temporary buffer. free(pDecompressedData); } return retval; } /**************************** ** ETC Compression ****************************/ /***************************************************************************** Macros *****************************************************************************/ #define _CLAMP_(X,Xmin,Xmax) ( (X)<(Xmax) ? ( (X)<(Xmin)?(Xmin):(X) ) : (Xmax) ) /***************************************************************************** Constants ******************************************************************************/ unsigned int ETC_FLIP = 0x01000000; unsigned int ETC_DIFF = 0x02000000; const int mod[8][4]={{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}}; /*!*********************************************************************** @Function modifyPixel @Input red Red value of pixel @Input green Green value of pixel @Input blue Blue value of pixel @Input x Pixel x position in block @Input y Pixel y position in block @Input modBlock Values for the current block @Input modTable Modulation values @Returns Returns actual pixel colour @Description Used by ETCTextureDecompress *************************************************************************/ static unsigned int modifyPixel(int red, int green, int blue, int x, int y, unsigned int modBlock, int modTable) { int index = x*4+y, pixelMod; unsigned int mostSig = modBlock<<1; if (index<8) pixelMod = mod[modTable][((modBlock>>(index+24))&0x1)+((mostSig>>(index+8))&0x2)]; else pixelMod = mod[modTable][((modBlock>>(index+8))&0x1)+((mostSig>>(index-8))&0x2)]; red = _CLAMP_(red+pixelMod,0,255); green = _CLAMP_(green+pixelMod,0,255); blue = _CLAMP_(blue+pixelMod,0,255); return ((red<<16) + (green<<8) + blue)|0xff000000; } /*!*********************************************************************** @Function ETCTextureDecompress @Input pSrcData The ETC texture data to decompress @Input x X dimension of the texture @Input y Y dimension of the texture @Modified pDestData The decompressed texture data @Input nMode The format of the data @Returns The number of bytes of ETC data decompressed @Description Decompresses ETC to RGBA 8888 *************************************************************************/ static int ETCTextureDecompress(const void * const pSrcData, const int &x, const int &y, const void *pDestData,const int &/*nMode*/) { unsigned int blockTop, blockBot, *input = (unsigned int*)pSrcData, *output; unsigned char red1, green1, blue1, red2, green2, blue2; bool bFlip, bDiff; int modtable1,modtable2; for(int i=0;i<y;i+=4) { for(int m=0;m<x;m+=4) { blockTop = *(input++); blockBot = *(input++); output = (unsigned int*)pDestData + i*x +m; // check flipbit bFlip = (blockTop & ETC_FLIP) != 0; bDiff = (blockTop & ETC_DIFF) != 0; if(bDiff) { // differential mode 5 colour bits + 3 difference bits // get base colour for subblock 1 blue1 = (unsigned char)((blockTop&0xf80000)>>16); green1 = (unsigned char)((blockTop&0xf800)>>8); red1 = (unsigned char)(blockTop&0xf8); // get differential colour for subblock 2 signed char blues = (signed char)(blue1>>3) + ((signed char) ((blockTop & 0x70000) >> 11)>>5); signed char greens = (signed char)(green1>>3) + ((signed char)((blockTop & 0x700) >>3)>>5); signed char reds = (signed char)(red1>>3) + ((signed char)((blockTop & 0x7)<<5)>>5); blue2 = (unsigned char)blues; green2 = (unsigned char)greens; red2 = (unsigned char)reds; red1 = red1 +(red1>>5); // copy bits to lower sig green1 = green1 + (green1>>5); // copy bits to lower sig blue1 = blue1 + (blue1>>5); // copy bits to lower sig red2 = (red2<<3) +(red2>>2); // copy bits to lower sig green2 = (green2<<3) + (green2>>2); // copy bits to lower sig blue2 = (blue2<<3) + (blue2>>2); // copy bits to lower sig } else { // individual mode 4 + 4 colour bits // get base colour for subblock 1 blue1 = (unsigned char)((blockTop&0xf00000)>>16); blue1 = blue1 +(blue1>>4); // copy bits to lower sig green1 = (unsigned char)((blockTop&0xf000)>>8); green1 = green1 + (green1>>4); // copy bits to lower sig red1 = (unsigned char)(blockTop&0xf0); red1 = red1 + (red1>>4); // copy bits to lower sig // get base colour for subblock 2 blue2 = (unsigned char)((blockTop&0xf0000)>>12); blue2 = blue2 +(blue2>>4); // copy bits to lower sig green2 = (unsigned char)((blockTop&0xf00)>>4); green2 = green2 + (green2>>4); // copy bits to lower sig red2 = (unsigned char)((blockTop&0xf)<<4); red2 = red2 + (red2>>4); // copy bits to lower sig } // get the modtables for each subblock modtable1 = (blockTop>>29)&0x7; modtable2 = (blockTop>>26)&0x7; if(!bFlip) { // 2 2x4 blocks side by side for(int j=0;j<4;j++) // vertical { for(int k=0;k<2;k++) // horizontal { *(output+j*x+k) = modifyPixel(red1,green1,blue1,k,j,blockBot,modtable1); *(output+j*x+k+2) = modifyPixel(red2,green2,blue2,k+2,j,blockBot,modtable2); } } } else { // 2 4x2 blocks on top of each other for(int j=0;j<2;j++) { for(int k=0;k<4;k++) { *(output+j*x+k) = modifyPixel(red1,green1,blue1,k,j,blockBot,modtable1); *(output+(j+2)*x+k) = modifyPixel(red2,green2,blue2,k,j+2,blockBot,modtable2); } } } } } return x*y/2; } /*!*********************************************************************** @Function PVRTDecompressETC @Input pSrcData The ETC texture data to decompress @Input x X dimension of the texture @Input y Y dimension of the texture @Modified pDestData The decompressed texture data @Input nMode The format of the data @Returns The number of bytes of ETC data decompressed @Description Decompresses ETC to RGBA 8888 *************************************************************************/ int PVRTDecompressETC(const void * const pSrcData, const unsigned int &x, const unsigned int &y, void *pDestData, const int &nMode) { int i32read; if(x<ETC_MIN_TEXWIDTH || y<ETC_MIN_TEXHEIGHT) { // decompress into a buffer big enough to take the minimum size char* pTempBuffer = (char*)malloc(PVRT_MAX(x,ETC_MIN_TEXWIDTH)*PVRT_MAX(y,ETC_MIN_TEXHEIGHT)*4); i32read = ETCTextureDecompress(pSrcData,PVRT_MAX(x,ETC_MIN_TEXWIDTH),PVRT_MAX(y,ETC_MIN_TEXHEIGHT),pTempBuffer,nMode); for(unsigned int i=0;i<y;i++) { // copy from larger temp buffer to output data memcpy((char*)(pDestData)+i*x*4,pTempBuffer+PVRT_MAX(x,ETC_MIN_TEXWIDTH)*4*i,x*4); } if(pTempBuffer) free(pTempBuffer); } else // decompress larger MIP levels straight into the output data i32read = ETCTextureDecompress(pSrcData,x,y,pDestData,nMode); // swap r and b channels unsigned char* pSwap = (unsigned char*)pDestData, swap; for(unsigned int i=0;i<y;i++) for(unsigned int j=0;j<x;j++) { swap = pSwap[0]; pSwap[0] = pSwap[2]; pSwap[2] = swap; pSwap+=4; } return i32read; } /***************************************************************************** End of file (PVRTDecompress.cpp) *****************************************************************************/