| /****************************************************************************** |
| |
| @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) |
| *****************************************************************************/ |
| |