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.
This commit is contained in:
Pavel Krajcevski 2013-09-12 14:30:08 -04:00
parent 08cad3ba86
commit 1115c2f9e4
3 changed files with 119 additions and 0 deletions

View File

@ -109,4 +109,40 @@ namespace PVRTCC {
return (m_LongData >> (texelIdx * 2)) & 0x3; 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<uint8>((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 } // namespace PVRTCC

View File

@ -70,6 +70,29 @@ class Block {
return static_cast<bool>((m_LongData >> 32) & 0x1); return static_cast<bool>((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 // Returns the modulation value for the texel in the 4x4 block. The texels are
// numbered as follows: // numbered as follows:
// 0 1 2 3 // 0 1 2 3
@ -78,6 +101,15 @@ class Block {
// 12 13 14 15 // 12 13 14 15
uint8 GetLerpValue(uint32 texelIdx) const; 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: private:
union { union {
uint8 m_ByteData[8]; uint8 m_ByteData[8];

View File

@ -185,3 +185,54 @@ TEST(Block, GetLerpValue) {
EXPECT_EQ(b.GetLerpValue(14), 1); EXPECT_EQ(b.GetLerpValue(14), 1);
EXPECT_EQ(b.GetLerpValue(15), 0); 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<const uint32 *>(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);
}