diff --git a/ASTCEncoder/src/Decompressor.cpp b/ASTCEncoder/src/Decompressor.cpp index c6ec9f0..8392d26 100644 --- a/ASTCEncoder/src/Decompressor.cpp +++ b/ASTCEncoder/src/Decompressor.cpp @@ -52,18 +52,49 @@ #include "ASTCCompressor.h" +#include #include #include +#include #include "Utils.h" #include "TexCompTypes.h" + +#include "Bits.h" +using FasTC::Bits; + #include "BitStream.h" using FasTC::BitStreamReadOnly; namespace ASTCC { - struct TexelWeightParams { + // According to table C.2.7 + void GetBitEncoding(uint8 &nQuints, uint8 &nTrits, uint8 &nBits, + const uint32 maxWeight) { + nQuints = nTrits = nBits = 0; + switch(maxWeight) { + case 1: nBits = 1; return; + case 2: nTrits = 1; return; + case 3: nBits = 2; return; + case 4: nQuints = 1; return; + case 5: nTrits = 1; nBits = 1; return; + case 7: nBits = 3; return; + case 9: nQuints = 1; nBits = 1; return; + case 11: nTrits = 1; nBits = 2; return; + case 15: nBits = 4; return; + case 19: nQuints = 1; nBits = 2; return; + case 23: nTrits = 1; nBits = 3; return; + case 31: nBits = 5; return; + + default: + assert(!"Invalid maximum weight"); + return; + } + } + + class TexelWeightParams { + public: uint32 m_Width; uint32 m_Height; bool m_bDualPlane; @@ -75,29 +106,26 @@ namespace ASTCC { memset(this, 0, sizeof(*this)); } - // According to table C.2.7 - void GetBitEncoding(uint8 &nQuints, uint8 &nTrits, uint8 &nBits) { - nQuints = 0; - nTrits = 0; - nBits = 0; - switch(m_MaxWeight) { - case 1: nBits = 1; return; - case 2: nTrits = 1; return; - case 3: nBits = 2; return; - case 4: nQuints = 1; return; - case 5: nTrits = 1; nBits = 1; return; - case 7: nBits = 3; return; - case 9: nQuints = 1; nBits = 1; return; - case 11: nTrits = 1; nBits = 2; return; - case 15: nBits = 4; return; - case 19: nQuints = 1; nBits = 2; return; - case 23: nTrits = 1; nBits = 3; return; - case 31: nBits = 5; return; - - default: - assert(!"Invalid maximum weight"); - return; + uint32 GetPackedBitSize() { + // How many indices do we have? + uint32 nIdxs = m_Height * m_Width; + if(m_bDualPlane) { + nIdxs *= 2; } + + // How are they encoded? + uint8 nQuints, nTrits, nBits; + GetBitEncoding(nQuints, nTrits, nBits, m_MaxWeight); + + // nQuints and nTrits are mutually exclusive values of one. + assert(nQuints != 1 || nTrits == 0); + assert(nTrits != 1 || nQuints == 0); + + // each index has at least as many bits per index as described. + uint32 totalBits = nBits * nIdxs; + totalBits += (nIdxs * 8 * nTrits + 4) / 5; + totalBits += (nIdxs * 7 * nQuints + 2) / 3; + return totalBits; } }; @@ -298,6 +326,143 @@ namespace ASTCC { } } + void DecodeTritBlock(BitStreamReadOnly &bits, + std::vector &result, + uint32 nBitsPerValue) { + // Implement the algorithm in section C.2.12 + uint32 m[5]; + uint32 t[5]; + uint32 T; + + // Read the trit encoded block according to + // table C.2.14 + m[0] = bits.ReadBits(nBitsPerValue); + T = bits.ReadBits(2); + m[1] = bits.ReadBits(nBitsPerValue); + T |= bits.ReadBits(2) << 2; + m[2] = bits.ReadBits(nBitsPerValue); + T |= bits.ReadBit() << 4; + m[3] = bits.ReadBits(nBitsPerValue); + T |= bits.ReadBits(2) << 5; + m[4] = bits.ReadBits(nBitsPerValue); + T |= bits.ReadBit() << 7; + + uint32 C = 0; + + Bits Tb(T); + if(Tb(2, 4) == 7) { + C = (Tb(5, 7) << 2) | Tb(0, 1); + t[4] = t[3] = 2; + } else { + C = Tb(0, 4); + if(Tb(5, 6) == 3) { + t[4] = 2; + t[3] = Tb[7]; + } else { + t[4] = Tb[7]; + t[3] = Tb(5, 6); + } + } + + Bits Cb(C); + if(Cb(0, 1) == 3) { + t[2] = 2; + t[1] = Cb[4]; + t[0] = (Cb[3] << 1) | (Cb[2] & ~Cb[3]); + } else if(Cb(2, 3) == 3) { + t[2] = 2; + t[1] = 2; + t[0] = Cb(0, 1); + } else { + t[2] = Cb[4]; + t[1] = Cb(2, 3); + t[0] = (Cb[1] << 1) | (Cb[0] & ~Cb[1]); + } + + for(uint32 i = 0; i < 5; i++) { + assert(t[i] < 3); + uint32 val = (t[i] << nBitsPerValue) + m[i]; + result.push_back(val); + } + } + + void DecodeQuintBlock(BitStreamReadOnly &bits, + std::vector &result, + uint32 nBitsPerValue) { + // Implement the algorithm in section C.2.12 + uint32 m[3]; + uint32 q[3]; + uint32 Q; + + // Read the trit encoded block according to + // table C.2.15 + m[0] = bits.ReadBits(nBitsPerValue); + Q = bits.ReadBits(3); + m[1] = bits.ReadBits(nBitsPerValue); + Q |= bits.ReadBits(2) << 3; + m[2] = bits.ReadBits(nBitsPerValue); + Q |= bits.ReadBits(2) << 5; + + Bits Qb(Q); + if(Qb(1, 2) == 3 && Qb(5, 6) == 0) { + q[0] = q[1] = 4; + q[2] = (Qb[0] << 2) | ((Qb[4] & ~Qb[0]) << 1) | (Qb[3] & ~Qb[0]); + } else { + uint32 C = 0; + if(Qb(1, 2) == 3) { + q[2] = 4; + C = (Qb(3, 4) << 3) | ((~Qb(5, 6) & 3) << 1) | Qb[0]; + } else { + q[2] = Qb(5, 6); + C = Qb(0, 4); + } + + Bits Cb(C); + if(Cb(0, 2) == 5) { + q[1] = 4; + q[0] = Cb(3, 4); + } else { + q[1] = Cb(3, 4); + q[0] = Cb(0, 2); + } + } + + for(uint32 i = 0; i < 3; i++) { + assert(q[i] < 5); + uint32 val = (q[i] << nBitsPerValue) + m[i]; + result.push_back(val); + } + } + + void DecodeIntegerSequence(BitStreamReadOnly &bits, + std::vector &result, + uint32 maxRange, + uint32 nValues) { + // Clean our result vector + result.clear(); + result.reserve(nValues); + + // Determine encoding parameters + uint8 nQuints, nTrits, nBits; + GetBitEncoding(nQuints, nTrits, nBits, maxRange); + + // Start decoding + uint32 nValsDecoded = 0; + while(nValsDecoded < nValues) { + if(nQuints) { + DecodeQuintBlock(bits, result, nBits); + nValsDecoded += 3; + } else if(nTrits) { + DecodeTritBlock(bits, result, nBits); + nValsDecoded += 5; + } else { + // Decode bit by bit + result.push_back(bits.ReadBits(nBits)); + nValsDecoded++; + } + } + } + void DecompressBlock(const uint8 inBuf[16], const uint32 blockWidth, const uint32 blockHeight, uint8 *outBuf) { @@ -335,29 +500,103 @@ namespace ASTCC { // Based on the number of partitions, read the color endpoint mode for // each partition. + + // Determine partitions, partition index, and color endpoint modes + int32 planeIdx = -1; uint32 partitionIndex = nPartitions; uint32 colorEndpointMode[4] = {0, 0, 0, 0}; + + // Define color data. + uint8 colorEndpointData[16]; + memset(colorEndpointData, 0, sizeof(colorEndpointData)); + FasTC::BitStream colorEndpointStream (colorEndpointData, 16*8, 0); + + // Read extra config data... + uint32 baseCEM = 0; if(nPartitions == 1) { colorEndpointMode[0] = strm.ReadBits(4); } else { uint32 restOfPartitionIndex = strm.ReadBits(10); partitionIndex |= restOfPartitionIndex << 2; + baseCEM = strm.ReadBits(6); + } + uint32 baseMode = (baseCEM & 3); - uint32 CEM = strm.ReadBits(2) - 1; + // Remaining bits are color endpoint data... + uint32 nWeightBits = weightParams.GetPackedBitSize(); + int32 remainingBits = 128 - nWeightBits - strm.GetBitsRead(); + + // Consider extra bits prior to texel data... + uint32 extraCEMbits = 0; + if(baseMode) { + switch(nPartitions) { + case 2: extraCEMbits += 2; break; + case 3: extraCEMbits += 5; break; + case 4: extraCEMbits += 8; break; + default: assert(false); break; + } + } + remainingBits -= extraCEMbits; + + // Do we have a dual plane situation? + uint32 planeSelectorBits = 0; + if(weightParams.m_bDualPlane) { + planeSelectorBits = 2; + } + remainingBits -= planeSelectorBits; + + // Read color data... + while(remainingBits > 0) { + uint32 nb = std::min(remainingBits, 8); + uint32 b = strm.ReadBits(nb); + colorEndpointStream.WriteBits(b, nb); + remainingBits -= 8; + } + + // Read the plane selection bits + planeIdx = strm.ReadBits(planeSelectorBits); + + // Read the rest of the CEM + if(baseMode) { + uint32 extraCEM = strm.ReadBits(extraCEMbits); + uint32 CEM = (extraCEM << 6) | baseCEM; + CEM >>= 2; + + bool C[4] = { 0 }; for(uint32 i = 0; i < nPartitions; i++) { - colorEndpointMode[i] = CEM + strm.ReadBit(); + C[i] = CEM & 1; + CEM >>= 1; + } + + uint8 M[4] = { 0 }; + for(uint32 i = 0; i < nPartitions; i++) { + M[i] = CEM & 3; + CEM >>= 2; + assert(M[i] <= 3); } for(uint32 i = 0; i < nPartitions; i++) { + colorEndpointMode[i] = baseMode; + if(!(C[i])) colorEndpointMode[i] -= 1; colorEndpointMode[i] <<= 2; - if(i == 0 && nPartitions == 3) { - colorEndpointMode[i] += strm.ReadBit(); - } else { - colorEndpointMode[i] += strm.ReadBits(2); - } + colorEndpointMode[i] |= M[i]; + } + } else if(nPartitions > 1) { + uint32 CEM = baseCEM >> 2; + for(uint32 i = 0; i < nPartitions; i++) { + colorEndpointMode[i] = CEM; } } +#ifndef NDEBUG + // Make sure everything up till here is sane. + for(uint32 i = 0; i < nPartitions; i++) { + assert(colorEndpointMode[i] < 16); + } + assert(strm.GetBitsRead() + weightParams.GetPackedBitSize() == 128); +#endif + + // Read the texel weight data.. }