From 1115c2f9e4f7073f8b8ac77ae3b0b961c0c7a74a Mon Sep 17 00:00:00 2001 From: Pavel Krajcevski Date: Thu, 12 Sep 2013 14:30:08 -0400 Subject: [PATCH] Add 2BPP helper functions for our blocks. Namely, there are two things that we need to do: 1. Figure out the sub-mode based on the mode bit and the structure of the modulation data. The comments in Block.h describe how we do this. 2. For a given texel index, return 2BPP texel modulation bits. --- PVRTCEncoder/src/Block.cpp | 36 +++++++++++++++++++++++ PVRTCEncoder/src/Block.h | 32 +++++++++++++++++++++ PVRTCEncoder/test/BlockTest.cpp | 51 +++++++++++++++++++++++++++++++++ 3 files changed, 119 insertions(+) diff --git a/PVRTCEncoder/src/Block.cpp b/PVRTCEncoder/src/Block.cpp index 1c6fada..8197953 100644 --- a/PVRTCEncoder/src/Block.cpp +++ b/PVRTCEncoder/src/Block.cpp @@ -109,4 +109,40 @@ namespace PVRTCC { return (m_LongData >> (texelIdx * 2)) & 0x3; } + Block::E2BPPSubMode Block::Get2BPPSubMode() const { + uint8 first = GetLerpValue(0); + if(!(first & 0x1)) { + return e2BPPSubMode_All; + } + + uint8 center = GetLerpValue(10); + if(center & 0x1) { + return e2BPPSubMode_Vertical; + } + + return e2BPPSubMode_Horizontal; + } + + uint8 Block::Get2BPPLerpValue(uint32 texelIdx) const { + + if(!(GetModeBit())) { + assert(texelIdx >= 0); + assert(texelIdx < 32); + return static_cast((m_LongData >> texelIdx) & 0x1); + } + + bool firstBitOnly = false; + if(texelIdx == 0 || + (texelIdx == 10 && Get2BPPSubMode() != e2BPPSubMode_All)) { + firstBitOnly = true; + } + + uint8 ret = GetLerpValue(texelIdx); + if(firstBitOnly) { + // Change 0, 1 => 0 and 2, 3 => 3 + ret = (ret & 0x2) | ((ret >> 1) & 0x1); + } + + return ret; + } } // namespace PVRTCC diff --git a/PVRTCEncoder/src/Block.h b/PVRTCEncoder/src/Block.h index d8c1aa7..a948d8a 100644 --- a/PVRTCEncoder/src/Block.h +++ b/PVRTCEncoder/src/Block.h @@ -70,6 +70,29 @@ class Block { return static_cast((m_LongData >> 32) & 0x1); } + // 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 + // values. There are three different ways to average nearby texels: Either we + // average the neighboring horizontal or vertical pixels using (a + b) / 2, or + // we neighbor all four neighbors using (a + b + c + d + 1) / 4. + enum E2BPPSubMode { + e2BPPSubMode_All, + e2BPPSubMode_Horizontal, + e2BPPSubMode_Vertical + }; + + // For 2BPP PVRTC, this function determines the submode of the given block. The + // submode is determined by first checking the first 2bit texel index. This texel + // uses the high bit as a 1 bit modulation value (i.e. chooses colors A or B) and + // the low bit is used to determine the sub-mode. If the low bit is 0, then we + // will use e2BPPSubMode_All as defined above. If the low bit is 1, then we must + // look at the center texel (index 10) to determine the sub-mode. In this case, + // we treat the center texel as 1 bit modulation as well, and we use the low bit to + // determine the sub-mode where 0 is e2BPPSubMode_Horizontal and 1 is + // e2BPPSubMode_Vertical + E2BPPSubMode Get2BPPSubMode() const; + // Returns the modulation value for the texel in the 4x4 block. The texels are // numbered as follows: // 0 1 2 3 @@ -78,6 +101,15 @@ class Block { // 12 13 14 15 uint8 GetLerpValue(uint32 texelIdx) const; + // 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, + // then this function expects a number from 0-15 and returns the corresponding + // modulation bits given the sub-mode. Note, this function does not do the + // averaging described for E2BPPSubMode because this averaging relies on + // global information. + uint8 Get2BPPLerpValue(uint32 texelIdx) const; + private: union { uint8 m_ByteData[8]; diff --git a/PVRTCEncoder/test/BlockTest.cpp b/PVRTCEncoder/test/BlockTest.cpp index 6db8da9..db04a18 100644 --- a/PVRTCEncoder/test/BlockTest.cpp +++ b/PVRTCEncoder/test/BlockTest.cpp @@ -185,3 +185,54 @@ TEST(Block, GetLerpValue) { EXPECT_EQ(b.GetLerpValue(14), 1); EXPECT_EQ(b.GetLerpValue(15), 0); } + +TEST(Block, Get2BPPLerpValue) { + uint8 noModData[8] = { 0xDA, 0x27, 0xE4, 0x1B, 0x0, 0x0, 0x0, 0x0 }; + PVRTCC::Block b(noModData); + + uint32 dataInt = *(reinterpret_cast(noModData)); + for(uint32 i = 0; i < 32; i++) { + EXPECT_EQ(b.Get2BPPLerpValue(i), (dataInt >> i) & 0x1); + } + + uint8 modData[8]; + memcpy(modData, noModData, sizeof(modData)); + modData[4] = 0x1; + + b = PVRTCC::Block(modData); + + EXPECT_EQ(b.Get2BPPLerpValue(0), 3); + EXPECT_EQ(b.Get2BPPLerpValue(1), 2); + EXPECT_EQ(b.Get2BPPLerpValue(2), 1); + EXPECT_EQ(b.Get2BPPLerpValue(3), 3); + + EXPECT_EQ(b.Get2BPPLerpValue(4), 3); + EXPECT_EQ(b.Get2BPPLerpValue(5), 1); + EXPECT_EQ(b.Get2BPPLerpValue(6), 2); + EXPECT_EQ(b.Get2BPPLerpValue(7), 0); + + EXPECT_EQ(b.Get2BPPLerpValue(8), 0); + EXPECT_EQ(b.Get2BPPLerpValue(9), 1); + EXPECT_EQ(b.Get2BPPLerpValue(10), 2); + EXPECT_EQ(b.Get2BPPLerpValue(11), 3); + + EXPECT_EQ(b.Get2BPPLerpValue(12), 3); + EXPECT_EQ(b.Get2BPPLerpValue(13), 2); + EXPECT_EQ(b.Get2BPPLerpValue(14), 1); + EXPECT_EQ(b.Get2BPPLerpValue(15), 0); +} + +TEST(Block, Get2BPPSubMode) { + uint8 data[8] = { 0xDA, 0x27, 0xE4, 0x1B, 0x1, 0x0, 0x0, 0x0 }; + PVRTCC::Block b(data); + + EXPECT_EQ(b.Get2BPPSubMode(), PVRTCC::Block::e2BPPSubMode_All); + + data[0] = 0xDB; + b = PVRTCC::Block(data); + EXPECT_EQ(b.Get2BPPSubMode(), PVRTCC::Block::e2BPPSubMode_Horizontal); + + data[2] = 0xF4; + b = PVRTCC::Block(data); + EXPECT_EQ(b.Get2BPPSubMode(), PVRTCC::Block::e2BPPSubMode_Vertical); +}