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