Skip to content

Commit 128eb58

Browse files
committed
Use unsigned integers for Bc decoding
1 parent 9f7a611 commit 128eb58

File tree

2 files changed

+105
-70
lines changed

2 files changed

+105
-70
lines changed

AssetRipper.TextureDecoder/Bc/BcHelpers.cs

Lines changed: 60 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ public static void DecompressBc6h(ReadOnlySpan<byte> compressedBlock, Span<byte>
3939
low = BinaryPrimitives.ReadUInt64LittleEndian(compressedBlock),
4040
high = BinaryPrimitives.ReadUInt64LittleEndian(compressedBlock.Slice(sizeof(ulong)))
4141
};
42-
Span<int> r = stackalloc int[4]; // wxyz
43-
Span<int> g = stackalloc int[4];
44-
Span<int> b = stackalloc int[4];
42+
Span<uint> r = stackalloc uint[4]; // wxyz
43+
Span<uint> g = stackalloc uint[4];
44+
Span<uint> b = stackalloc uint[4];
4545

4646
int decompressedOffset = 0;
4747

@@ -52,7 +52,7 @@ public static void DecompressBc6h(ReadOnlySpan<byte> compressedBlock, Span<byte>
5252
int mode;
5353

5454
//modes >= 11 (10 in my code) are using 0 one, others will read it from the bitstream
55-
int partition;
55+
uint partition;
5656

5757
switch (ReadBc6hModeBits(ref bstream))
5858
{
@@ -520,7 +520,7 @@ public static void DecompressBc6h(ReadOnlySpan<byte> compressedBlock, Span<byte>
520520
}
521521
partitionSet &= 0x01;
522522

523-
int index = bstream.ReadBits(indexBits);
523+
int index = bstream.ReadBits(indexBits).ToSigned();
524524

525525
int ep_i = (partitionSet * 2);
526526

@@ -542,12 +542,12 @@ public static void DecompressBc6h(ReadOnlySpan<byte> compressedBlock, Span<byte>
542542
decompressedOffset += destinationPitch * sizeof(ushort);
543543
}
544544

545-
static int ReadBc6hModeBits(ref BitStream bstream)
545+
static uint ReadBc6hModeBits(ref BitStream bstream)
546546
{
547-
int twoBits = bstream.ReadBits(2);
547+
uint twoBits = bstream.ReadBits(2);
548548
if (twoBits > 1)
549549
{
550-
int threeBits = bstream.ReadBits(3);
550+
uint threeBits = bstream.ReadBits(3);
551551
return (threeBits << 2) | twoBits;
552552
}
553553
else
@@ -564,7 +564,7 @@ public static void DecompressBc7(ReadOnlySpan<byte> compressedBlock, Span<byte>
564564
low = BinaryPrimitives.ReadUInt64LittleEndian(compressedBlock),
565565
high = BinaryPrimitives.ReadUInt64LittleEndian(compressedBlock.Slice(sizeof(ulong)))
566566
};
567-
Span2D<int> endpoints = new(stackalloc int[6 * 4], 6, 4);
567+
Span2D<uint> endpoints = new(stackalloc uint[6 * 4], 6, 4);
568568
Span2D<int> indices = new(stackalloc int[4 * 4], 4, 4);
569569

570570
int decompressedOffset = 0;
@@ -589,15 +589,15 @@ public static void DecompressBc7(ReadOnlySpan<byte> compressedBlock, Span<byte>
589589
return;
590590
}
591591

592-
int partition = 0;
592+
uint partition = 0;
593593
int numPartitions = 1;
594-
int rotation = 0;
595-
int indexSelectionBit = 0;
594+
uint rotation = 0;
595+
uint indexSelectionBit = 0;
596596

597597
if (mode == 0 || mode == 1 || mode == 2 || mode == 3 || mode == 7)
598598
{
599-
numPartitions = (mode == 0 || mode == 2) ? 3 : 2;
600-
partition = bstream.ReadBits((mode == 0) ? 4 : 6);
599+
numPartitions = (mode is 0 or 2) ? 3 : 2;
600+
partition = bstream.ReadBits((mode is 0) ? 4 : 6);
601601
}
602602

603603
int numEndpoints = numPartitions * 2;
@@ -646,8 +646,8 @@ public static void DecompressBc7(ReadOnlySpan<byte> compressedBlock, Span<byte>
646646
// if P-bit is shared
647647
if (mode == 1)
648648
{
649-
int i = bstream.ReadBit();
650-
int j = bstream.ReadBit();
649+
uint i = bstream.ReadBit();
650+
uint j = bstream.ReadBit();
651651

652652
// rgb component-wise insert pbits
653653
for (int k = 0; k < 3; ++k)
@@ -663,7 +663,7 @@ public static void DecompressBc7(ReadOnlySpan<byte> compressedBlock, Span<byte>
663663
// unique P-bit per endpoint
664664
for (int i = 0; i < numEndpoints; ++i)
665665
{
666-
int j = bstream.ReadBit();
666+
uint j = bstream.ReadBit();
667667
for (int k = 0; k < 4; ++k)
668668
{
669669
endpoints[i, k] |= j;
@@ -726,7 +726,7 @@ public static void DecompressBc7(ReadOnlySpan<byte> compressedBlock, Span<byte>
726726
indexBits--;
727727
}
728728

729-
indices[i, j] = bstream.ReadBits(indexBits);
729+
indices[i, j] = bstream.ReadBits(indexBits).ToSigned();
730730
}
731731
}
732732

@@ -742,10 +742,10 @@ public static void DecompressBc7(ReadOnlySpan<byte> compressedBlock, Span<byte>
742742

743743
int index = indices[i, j];
744744

745-
int r;
746-
int g;
747-
int b;
748-
int a;
745+
uint r;
746+
uint g;
747+
uint b;
748+
uint a;
749749

750750
if (indexBits2 == 0)
751751
{
@@ -756,7 +756,7 @@ public static void DecompressBc7(ReadOnlySpan<byte> compressedBlock, Span<byte>
756756
}
757757
else
758758
{
759-
int index2 = bstream.ReadBits((i | j) != 0 ? indexBits2 : (indexBits2 - 1));
759+
int index2 = bstream.ReadBits((i | j) != 0 ? indexBits2 : (indexBits2 - 1)).ToSigned();
760760
// The index value for interpolating color comes from the secondary index bits for the texel
761761
// if the mode has an index selection bit and its value is one, and from the primary index bits otherwise.
762762
// The alpha index comes from the secondary index bits if the block has a secondary index
@@ -949,6 +949,11 @@ public static int ExtendSign(int val, int bits)
949949
return (val << (32 - bits)) >> (32 - bits);
950950
}
951951

952+
private static uint ExtendSign(uint val, int bits)
953+
{
954+
return ExtendSign(val.ToSigned(), bits).ToUnsigned();
955+
}
956+
952957
public static int TransformInverse(int val, int a0, int bits, bool isSigned)
953958
{
954959
// If the precision of A0 is "p" bits, then the transform algorithm is:
@@ -961,6 +966,11 @@ public static int TransformInverse(int val, int a0, int bits, bool isSigned)
961966
return val;
962967
}
963968

969+
private static uint TransformInverse(uint val, uint a0, int bits, bool isSigned)
970+
{
971+
return TransformInverse(val.ToSigned(), a0.ToSigned(), bits, isSigned).ToUnsigned();
972+
}
973+
964974
/// <summary>
965975
/// Essentially copy-paste from documentation
966976
/// </summary>
@@ -1028,11 +1038,21 @@ public static int Unquantize(int val, int bits, bool isSigned)
10281038
return unq;
10291039
}
10301040

1041+
private static uint Unquantize(uint val, int bits, bool isSigned)
1042+
{
1043+
return Unquantize(val.ToSigned(), bits, isSigned).ToUnsigned();
1044+
}
1045+
10311046
public static int Interpolate(int a, int b, ReadOnlySpan<int> weights, int index)
10321047
{
10331048
return ((a * (64 - weights[index])) + (b * weights[index]) + 32) >> 6;
10341049
}
10351050

1051+
private static uint Interpolate(uint a, uint b, ReadOnlySpan<int> weights, int index)
1052+
{
1053+
return Interpolate(a.ToSigned(), b.ToSigned(), weights, index).ToUnsigned();
1054+
}
1055+
10361056
public static ushort FinishUnquantize(int val, bool isSigned)
10371057
{
10381058
if (!isSigned)
@@ -1041,7 +1061,7 @@ public static ushort FinishUnquantize(int val, bool isSigned)
10411061
}
10421062
else
10431063
{
1044-
val = (val < 0) ? -(((-val) * 31) >> 5) : (val * 31) >> 5; // scale the magnitude by 31 / 32
1064+
val = (val < 0) ? (((-val) * 31) >> 5) : (val * 31) >> 5; // scale the magnitude by 31 / 32
10451065
int s = 0;
10461066
if (val < 0)
10471067
{
@@ -1052,8 +1072,23 @@ public static ushort FinishUnquantize(int val, bool isSigned)
10521072
}
10531073
}
10541074

1055-
public static void SwapValues(ref int a, ref int b)
1075+
private static ushort FinishUnquantize(uint val, bool isSigned)
1076+
{
1077+
return FinishUnquantize(val.ToSigned(), isSigned);
1078+
}
1079+
1080+
public static void SwapValues(ref uint a, ref uint b)
10561081
{
10571082
(a, b) = (b, a);
10581083
}
1084+
1085+
private static uint ToUnsigned(this int value)
1086+
{
1087+
return unchecked((uint)value);
1088+
}
1089+
1090+
private static int ToSigned(this uint value)
1091+
{
1092+
return unchecked((int)value);
1093+
}
10591094
}
Lines changed: 45 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,45 @@
1-
namespace AssetRipper.TextureDecoder.Bc;
2-
3-
internal struct BitStream
4-
{
5-
public ulong low;
6-
public ulong high;
7-
8-
public int ReadBits(int numBits)
9-
{
10-
uint mask = (uint)((1 << numBits) - 1);
11-
// Read the low N bits
12-
uint bits = (uint)(this.low & mask);
13-
14-
this.low >>= numBits;
15-
// Put the low N bits of "high" into the high 64-N bits of "low".
16-
this.low |= (this.high & mask) << ((sizeof(ulong) * 8) - numBits);
17-
this.high >>= numBits;
18-
19-
return (int)bits;
20-
}
21-
22-
public int ReadBit()
23-
{
24-
return this.ReadBits(1);
25-
}
26-
27-
/// <summary>
28-
/// Reversed bits pulling, used in BC6H decoding
29-
/// </summary>
30-
/// <param name="numBits"></param>
31-
/// <returns></returns>
32-
public int ReadBitsReversed(int numBits)
33-
{
34-
int bits = this.ReadBits(numBits);
35-
// Reverse the bits.
36-
int result = 0;
37-
while (numBits-- != 0)
38-
{
39-
result <<= 1;
40-
result |= bits & 1;
41-
bits >>= 1;
42-
}
43-
return result;
44-
}
45-
}
1+
namespace AssetRipper.TextureDecoder.Bc;
2+
3+
internal struct BitStream
4+
{
5+
public ulong low;
6+
public ulong high;
7+
8+
public uint ReadBits(int numBits)
9+
{
10+
uint mask = (1u << numBits) - 1u;
11+
// Read the low N bits
12+
uint bits = unchecked((uint)(this.low & mask));
13+
14+
this.low >>= numBits;
15+
// Put the low N bits of "high" into the high 64-N bits of "low".
16+
this.low |= (this.high & mask) << ((sizeof(ulong) * 8) - numBits);
17+
this.high >>= numBits;
18+
19+
return bits;
20+
}
21+
22+
public uint ReadBit()
23+
{
24+
return this.ReadBits(1);
25+
}
26+
27+
/// <summary>
28+
/// Reversed bits pulling, used in BC6H decoding
29+
/// </summary>
30+
/// <param name="numBits"></param>
31+
/// <returns></returns>
32+
public uint ReadBitsReversed(int numBits)
33+
{
34+
uint bits = this.ReadBits(numBits);
35+
// Reverse the bits.
36+
uint result = 0;
37+
while (numBits-- != 0)
38+
{
39+
result <<= 1;
40+
result |= bits & 1;
41+
bits >>= 1;
42+
}
43+
return result;
44+
}
45+
}

0 commit comments

Comments
 (0)