/// <summary> /// Given a block, extract the colour information and convert to 5554 formats /// </summary> private unsafe static void Unpack5554Colour(AmtcBlock *block, int *abColors) { uint *rawBits = stackalloc uint[2]; // extract A and B // 15 bits (shifted up by one) rawBits[0] = block->PackedData1 & (0xFFFE); // 16 bits rawBits[1] = block->PackedData1 >> 16; // step through both colours for (int i = 0; i < 2; i++) { // if completely opaque if ((rawBits[i] & (1 << 15)) != 0) { // extract R and G (both 5 bit) abColors[i * 4 + 0] = (int)((rawBits[i] >> 10) & 0x1F); abColors[i * 4 + 1] = (int)((rawBits[i] >> 5) & 0x1F); // The precision of Blue depends on A or B. If A then we need to replicate the top bit to get 5 bits in total abColors[i * 4 + 2] = (int)(rawBits[i] & 0x1F); if (i == 0) { abColors[0 * 4 + 2] |= abColors[0 * 4 + 2] >> 4; } // set 4bit alpha fully on... abColors[i * 4 + 3] = 0xF; } // else if colour has variable translucency else { // extract R and G (both 4 bit). Leave a space on the end for the replication of bits abColors[i * 4 + 0] = (int)((rawBits[i] >> (8 - 1)) & 0x1E); abColors[i * 4 + 1] = (int)((rawBits[i] >> (4 - 1)) & 0x1E); // replicate bits to truly expand to 5 bits abColors[i * 4 + 0] |= abColors[i * 4 + 0] >> 4; abColors[i * 4 + 1] |= abColors[i * 4 + 1] >> 4; // grab the 3(+padding) or 4 bits of blue and add an extra padding bit abColors[i * 4 + 2] = (int)((rawBits[i] & 0xF) << 1); // expand from 3 to 5 bits if this is from colour A, or 4 to 5 bits if from colour B if (i == 0) { abColors[0 * 4 + 2] |= abColors[0 * 4 + 2] >> 3; } else { abColors[0 * 4 + 2] |= abColors[0 * 4 + 2] >> 4; } // set the alpha bits to be 3 + a zero on the end abColors[i * 4 + 3] = (int)((rawBits[i] >> 11) & 0xE); } } }
private unsafe static void PVRDecompress(byte *compressedData, bool do2bitMode, int xDim, int yDim, byte[] output) { int xBlockSize = do2bitMode ? BlockX2bpp : BlockX4bpp; // for MBX don't allow the sizes to get too small int blockXDim = Math.Max(2, xDim / xBlockSize); int blockYDim = Math.Max(2, yDim / BlockYSize); uint pblocki00 = uint.MaxValue; uint pblocki01 = uint.MaxValue; uint pblocki10 = uint.MaxValue; uint pblocki11 = uint.MaxValue; Colours5554 *m_colors = stackalloc Colours5554[2 * 2]; // interpolated A colors for the pixel int *aSig = stackalloc int[4]; // interpolated B colors for the pixel int *bSig = stackalloc int[4]; int *modulationVals = stackalloc int[8 * 16]; int *modulationModes = stackalloc int[8 * 16]; // step through the pixels of the image decompressing each one in turn. // Note that this is a hideously inefficient way to do this! for (int y = 0; y < yDim; y++) { for (int x = 0; x < xDim; x++) { // map this pixel to the top left neighbourhood of blocks int blockX = (x - xBlockSize / 2); int blockY = (y - BlockYSize / 2); blockX = LimitCoord(blockX, xDim) / xBlockSize; blockY = LimitCoord(blockY, yDim) / BlockYSize; // compute the positions of the other 3 blocks int blockXp1 = LimitCoord(blockX + 1, blockXDim); int blockYp1 = LimitCoord(blockY + 1, blockYDim); // map to block memory locations uint blocki00 = TwiddleUV((uint)blockYDim, (uint)blockXDim, (uint)blockY, (uint)blockX); uint blocki01 = TwiddleUV((uint)blockYDim, (uint)blockXDim, (uint)blockY, (uint)blockXp1); uint blocki10 = TwiddleUV((uint)blockYDim, (uint)blockXDim, (uint)blockYp1, (uint)blockX); uint blocki11 = TwiddleUV((uint)blockYDim, (uint)blockXDim, (uint)blockYp1, (uint)blockXp1); // extract the colours and the modulation information IF the previous values have changed. bool changed = blocki00 != pblocki00 || blocki01 != pblocki01 || blocki10 != pblocki10 || blocki11 != pblocki11; if (changed) { AmtcBlock *block00 = &((AmtcBlock *)compressedData)[blocki00]; Unpack5554Colour(block00, m_colors[0].Reps); UnpackModulations(block00, do2bitMode, modulationVals, modulationModes, 0, 0); AmtcBlock *block01 = &((AmtcBlock *)compressedData)[blocki01]; Unpack5554Colour(block01, m_colors[1].Reps); UnpackModulations(block01, do2bitMode, modulationVals, modulationModes, xBlockSize, 0); AmtcBlock *block10 = &((AmtcBlock *)compressedData)[blocki10]; Unpack5554Colour(block10, m_colors[2].Reps); UnpackModulations(block10, do2bitMode, modulationVals, modulationModes, 0, BlockYSize); AmtcBlock *block11 = &((AmtcBlock *)compressedData)[blocki11]; Unpack5554Colour(block11, m_colors[3].Reps); UnpackModulations(block11, do2bitMode, modulationVals, modulationModes, xBlockSize, BlockYSize); pblocki00 = blocki00; pblocki01 = blocki01; pblocki10 = blocki10; pblocki11 = blocki11; } // decompress the pixel. First compute the interpolated A and B signals InterpolateColours(m_colors[0].Reps, m_colors[1].Reps, m_colors[2].Reps, m_colors[3].Reps, 0, do2bitMode, x, y, aSig); InterpolateColours(m_colors[0].Reps, m_colors[1].Reps, m_colors[2].Reps, m_colors[3].Reps, 1, do2bitMode, x, y, bSig); GetModulationValue(x, y, do2bitMode, modulationVals, modulationModes, out int mod, out bool doPT); // compute the modulated color. Swap red and blue channel int position = (x + y * xDim) << 2; output[position + 0] = (byte)((aSig[2] * 8 + mod * (bSig[2] - aSig[2])) >> 3); output[position + 1] = (byte)((aSig[1] * 8 + mod * (bSig[1] - aSig[1])) >> 3); output[position + 2] = (byte)((aSig[0] * 8 + mod * (bSig[0] - aSig[0])) >> 3); output[position + 3] = doPT ? (byte)0 : (byte)((aSig[3] * 8 + mod * (bSig[3] - aSig[3])) >> 3); } } }
/// <summary> /// Given the block and the texture type and it's relative position in the 2x2 group of blocks, extract the bit patterns for the fully defined pixels. /// </summary> private unsafe static void UnpackModulations(AmtcBlock *block, bool do2bitMode, int *modulationVals, int *modulationModes, int startX, int startY) { int blockModMode = (int)(block->PackedData1 & 1); uint modulationBits = block->PackedData0; // if it's in an interpolated mode if (do2bitMode && (blockModMode != 0)) { // 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 < BlockYSize; y++) { for (int x = 0; x < BlockX2bpp; x++) { modulationModes[(y + startY) * 16 + x + startX] = blockModMode; // if this is a stored value... if (((x ^ y) & 1) == 0) { modulationVals[(y + startY) * 16 + x + startX] = (int)(modulationBits & 3); modulationBits >>= 2; } } } } // else if direct encoded 2bit mode - i.e. 1 mode bit per pixel else if (do2bitMode) { for (int y = 0; y < BlockYSize; y++) { for (int x = 0; x < BlockX2bpp; x++) { modulationModes[(y + startY) * 16 + x + startX] = blockModMode; // double the bits so 0=> 00, and 1=>11 if ((modulationBits & 1) != 0) { modulationVals[(y + startY) * 16 + x + startX] = 0x3; } else { modulationVals[(y + startY) * 16 + x + startX] = 0x0; } modulationBits >>= 1; } } } // else its the 4bpp mode so each value has 2 bits else { for (int y = 0; y < BlockYSize; y++) { for (int x = 0; x < BlockX4bpp; x++) { modulationModes[(y + startY) * 16 + x + startX] = blockModMode; modulationVals[(y + startY) * 16 + x + startX] = (int)(modulationBits & 3); modulationBits >>= 2; } } } // make sure nothing is left over if (modulationBits != 0) { throw new Exception("Something is left over"); } }