From 8f4dcca4d794d44aa536c442ad32ba0c621dd1b3 Mon Sep 17 00:00:00 2001 From: Pavel Krajcevski Date: Tue, 24 Sep 2013 20:33:48 -0400 Subject: [PATCH] Add some utility functions for manipulating the block data, including packing colors back into the 64-bit word. --- PVRTCEncoder/src/Block.cpp | 108 ++++++++++++++++++++++++++++++++ PVRTCEncoder/src/Block.h | 30 +++++++++ PVRTCEncoder/test/BlockTest.cpp | 91 +++++++++++++++++++++++++++ 3 files changed, 229 insertions(+) diff --git a/PVRTCEncoder/src/Block.cpp b/PVRTCEncoder/src/Block.cpp index 8197953..602aab5 100644 --- a/PVRTCEncoder/src/Block.cpp +++ b/PVRTCEncoder/src/Block.cpp @@ -83,6 +83,42 @@ namespace PVRTCC { return m_ColorA; } + Pixel Block::SetColor(const Pixel &c, bool transparent, + const uint8 (&tbd)[4], const uint8 (&obd)[4]) { + uint8 cDepth[4]; + c.GetBitDepth(cDepth); + + Pixel final = c; + if(transparent) { + final.ChangeBitDepth(tbd); + + // If we went effectively transparent, then just switch over to opaque... + if(final.A() == 0x7) { + return SetColor(c, false, tbd, obd); + } + + } else { + final.A() = 255; + final.ChangeBitDepth(obd); + } + + return final; + } + + void Block::SetColorA(const Pixel &c, bool transparent) { + const uint8 transparentBitDepth[4] = { 3, 4, 4, 4 }; + const uint8 opaqueBitDepth[4] = { 0, 5, 5, 5 }; + m_ColorA = SetColor(c, transparent, transparentBitDepth, opaqueBitDepth); + m_ColorACached = true; + } + + void Block::SetColorB(const Pixel &c, bool transparent) { + const uint8 transparentBitDepth[4] = { 3, 4, 4, 3 }; + const uint8 opaqueBitDepth[4] = { 0, 5, 5, 4 }; + m_ColorB = SetColor(c, transparent, transparentBitDepth, opaqueBitDepth); + m_ColorBCached = true; + } + Pixel Block::GetColorB() { if(m_ColorBCached) { return m_ColorB; @@ -109,6 +145,17 @@ namespace PVRTCC { return (m_LongData >> (texelIdx * 2)) & 0x3; } + void Block::SetLerpValue(uint32 texelIdx, uint8 lerpVal) { + assert(texelIdx >= 0); + assert(texelIdx <= 15); + + assert(lerpVal >= 0); + assert(lerpVal < 4); + + m_LongData &= ~(static_cast(0x3) << (texelIdx * 2)); + m_LongData |= static_cast(lerpVal & 0x3) << (texelIdx * 2); + } + Block::E2BPPSubMode Block::Get2BPPSubMode() const { uint8 first = GetLerpValue(0); if(!(first & 0x1)) { @@ -145,4 +192,65 @@ namespace PVRTCC { return ret; } + + uint64 Block::Pack() { + assert(m_ColorACached); + assert(m_ColorBCached); + +#ifndef NDEBUG + uint8 bitDepthA[4]; + m_ColorA.GetBitDepth(bitDepthA); + + uint32 sumA = 0; + for(int i = 0; i < 4; i++) { + sumA += bitDepthA[i]; + } + assert(sumA == 15); +#endif + +#ifndef NDEBUG + uint8 bitDepthB[4]; + m_ColorB.GetBitDepth(bitDepthB); + + uint32 sumB = 0; + for(int i = 0; i < 4; i++) { + sumB += bitDepthB[i]; + } + assert(sumB == 14); +#endif + + uint8 aBits[2], bBits[2]; + memset(aBits, 0, sizeof(aBits)); + memset(bBits, 0, sizeof(bBits)); + + m_ColorA.ToBits(aBits, 2); + m_ColorB.ToBits(bBits, 2, 1); + + if(m_ColorA.A() == 0xFF) { + m_ByteData[7] |= 0x80; + } else { + m_ByteData[7] &= 0x7f; + } + m_ByteData[7] = aBits[1]; + m_ByteData[6] = aBits[0]; + + bool modeBit = GetModeBit(); + m_ByteData[5] = bBits[1]; + m_ByteData[4] = bBits[0]; + if(m_ColorB.A() == 0xFF) { + m_ByteData[5] |= 0x80; + } else { + m_ByteData[5] &= 0x7f; + } + + if(modeBit) { + m_ByteData[4] |= 0x1; + } else { + m_ByteData[4] &= 0xFE; + } + + // Modulation data should have already been set... + return m_LongData; + } + } // namespace PVRTCC diff --git a/PVRTCEncoder/src/Block.h b/PVRTCEncoder/src/Block.h index a948d8a..0173f09 100644 --- a/PVRTCEncoder/src/Block.h +++ b/PVRTCEncoder/src/Block.h @@ -61,15 +61,29 @@ namespace PVRTCC { class Block { public: + Block(): m_LongData(0) { } explicit Block(const uint8 *data); + // Accessors for the A and B colors of the block. Pixel GetColorA(); + void SetColorA(const Pixel &, bool transparent=false); + Pixel GetColorB(); + void SetColorB(const Pixel &, bool transparent=false); bool GetModeBit() const { return static_cast((m_LongData >> 32) & 0x1); } + void SetModeBit(bool flag) { + const uint64 bit = 0x100000000L; + if(flag) { + m_LongData |= bit; + } else { + m_LongData &= ~bit; + } + } + // For 2BPP PVRTC, if the mode bit is set, then we use the modulation data // as 2 bits for every other texel in the 8x4 block in a checkerboard pattern. // The interleaved texel data is decided by averaging nearby texel modulation @@ -101,6 +115,11 @@ class Block { // 12 13 14 15 uint8 GetLerpValue(uint32 texelIdx) const; + // Sets the values in the data for this block according to the texel and + // modulation value passed. This happens immediately (i.e. a call to Pack() + // will reflect these changes). + void SetLerpValue(uint32 texelIdx, uint8 lerpVal); + // This returns the modulation value for the texel in the block interpreted as // 2BPP. If the modulation bit is not set, then it expects a number from 0-31 // and does the same operation as GetLerpValue. If the modulation bit is set, @@ -110,6 +129,12 @@ class Block { // global information. uint8 Get2BPPLerpValue(uint32 texelIdx) const; + // Returns the 64-bit word that represents this block. This function packs the + // A and B colors based on their bit depths and preserves the corresponding mode + // bits. The color modes are determined by whether or not the alpha channel of + // each block is fully opaque or not. + uint64 Pack(); + private: union { uint8 m_ByteData[8]; @@ -121,6 +146,11 @@ class Block { bool m_ColorBCached; Pixel m_ColorB; + + // tbd -- transparent bit depth + // obd -- opaque bit depth + static Pixel SetColor(const Pixel &c, bool transparent, + const uint8 (&tbd)[4], const uint8 (&obd)[4]); }; } // namespace PVRTCC diff --git a/PVRTCEncoder/test/BlockTest.cpp b/PVRTCEncoder/test/BlockTest.cpp index db04a18..5e47315 100644 --- a/PVRTCEncoder/test/BlockTest.cpp +++ b/PVRTCEncoder/test/BlockTest.cpp @@ -236,3 +236,94 @@ TEST(Block, Get2BPPSubMode) { b = PVRTCC::Block(data); EXPECT_EQ(b.Get2BPPSubMode(), PVRTCC::Block::e2BPPSubMode_Vertical); } + +TEST(Block, SetColorAandB) { + PVRTCC::Block b; + PVRTCC::Pixel color; + color.A() = 212; + color.R() = 200; + color.G() = 100; + color.B() = -120; + b.SetColorA(color); + PVRTCC::Pixel cA = b.GetColorA(); + + uint8 bitDepth[4] = { 0, 5, 5, 5 }; + color.ChangeBitDepth(bitDepth); + + EXPECT_FALSE(memcmp(&color, &cA, sizeof(color))); + + memset(bitDepth, 8, sizeof(bitDepth)); + color.ChangeBitDepth(bitDepth); + + color.A() = 212; + color.R() = 200; + color.G() = 100; + color.B() = -120; + b.SetColorB(color, true); + PVRTCC::Pixel cB = b.GetColorB(); + + uint8 tBitDepth[4] = { 0, 5, 5, 4 }; + color.ChangeBitDepth(tBitDepth); + + EXPECT_FALSE(memcmp(&color, &cB, sizeof(color))); + + memset(bitDepth, 8, sizeof(bitDepth)); + color.ChangeBitDepth(bitDepth); + + color.A() = 100; + color.R() = 200; + color.G() = 100; + color.B() = -120; + b.SetColorB(color, true); + PVRTCC::Pixel cC = b.GetColorB(); + + uint8 uBitDepth[4] = { 3, 4, 4, 3 }; + color.ChangeBitDepth(uBitDepth); + + EXPECT_FALSE(memcmp(&color, &cC, sizeof(color))); +} + +TEST(Block, SetLerpValue) { + PVRTCC::Block b; + + for(int i = 0; i < 16; i++) { + b.SetLerpValue(i, i%4); + } + + for(int i = 0; i < 16; i++) { + EXPECT_EQ(b.GetLerpValue(i), i % 4); + } +} + +TEST(Block, PackBlock) { + PVRTCC::Block b; + + PVRTCC::Pixel cA, cB; + + cA.A() = 0xFF; + cA.R() = 0xFF; + cA.G() = 0x80; + cA.B() = 0x00; + + cB.A() = 0x80; + cB.R() = 0x7F; + cB.G() = 0x00; + cB.B() = 0xFF; + + b.SetColorA(cA); + b.SetColorB(cB, true); + + for(int i = 0; i < 16; i++) { + b.SetLerpValue(i, i%4); + } + + b.SetModeBit(false); + EXPECT_EQ(b.Pack(), 0xFE00480EE4E4E4E4UL); + + b.SetModeBit(true); + EXPECT_EQ(b.Pack(), 0xFE00480FE4E4E4E4UL); + + b.SetColorB(cB); + b.SetModeBit(false); + EXPECT_EQ(b.Pack(), 0xFE00C01EE4E4E4E4UL); +}