Skip to content

Commit 1d2d19c

Browse files
committed
Support Bc images with non-standard sizes
* Resolves #39
1 parent f3ba845 commit 1d2d19c

File tree

9 files changed

+281
-88
lines changed

9 files changed

+281
-88
lines changed

AssetRipper.TextureDecoder.Tests/BcTests.cs

Lines changed: 80 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public void InitializeOriginalData()
2121
}
2222

2323
[Test]
24-
public void DecompressDXT1Test()
24+
public void Decompress_BC1()
2525
{
2626
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.DxtTestFiles + "test.dxt1");
2727
int totalBytesRead = 0;
@@ -36,7 +36,7 @@ public void DecompressDXT1Test()
3636
}
3737

3838
[Test]
39-
public void DecompressDXT3Test()
39+
public void Decompress_BC2()
4040
{
4141
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.DxtTestFiles + "test.dxt3");
4242
int totalBytesRead = 0;
@@ -51,7 +51,7 @@ public void DecompressDXT3Test()
5151
}
5252

5353
[Test]
54-
public void DecompressDXT5Test()
54+
public void Decompress_BC3()
5555
{
5656
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.DxtTestFiles + "test.dxt5");
5757
int totalBytesRead = 0;
@@ -66,15 +66,15 @@ public void DecompressDXT5Test()
6666
}
6767

6868
[Test]
69-
public void DecompressBC4Test()
69+
public void Decompress_BC4()
7070
{
7171
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.BcTestFiles + "test.bc4");
7272
int bytesRead = Bc4.Decompress(data, 512, 512, out _);
7373
Assert.That(bytesRead, Is.EqualTo(data.Length));
7474
}
7575

7676
[Test]
77-
public void DecompressBC5Test()
77+
public void Decompress_BC5()
7878
{
7979
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.BcTestFiles + "test.bc5");
8080
int bytesRead = Bc5.Decompress(data, 512, 512, out _);
@@ -84,22 +84,93 @@ public void DecompressBC5Test()
8484
[TestCase("test.bc6h_fast")]
8585
[TestCase("test.bc6h_normal")]
8686
[TestCase("test.bc6h_best")]
87-
public void DecompressBC6HTest(string fileName)
87+
public void Decompress_BC6H(string fileName)
8888
{
8989
AssertCorrectBC6HDecompression(TestFileFolders.BcTestFiles + fileName, 512, 512, false);
9090
}
9191

9292
[TestCase("test.bc7_fast")]
9393
[TestCase("test.bc7_normal")]
9494
[TestCase("test.bc7_best")]
95-
public void DecompressBC7Test(string fileName)
95+
public void Decompress_BC7(string fileName)
9696
{
9797
AssertCorrectBC7Decompression(TestFileFolders.BcTestFiles + fileName, 512, 512);
9898
}
9999

100100
[Test]
101-
[Ignore("Not implemented")]
102-
public void BC7PartialBlockTest([Range(1, 4)] int width, [Range(1, 4)] int height)
101+
public void PartialBlock_BC1([Range(1, 4)] int width, [Range(1, 4)] int height)
102+
{
103+
Assert.Multiple(() =>
104+
{
105+
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.DxtTestFiles + "test.dxt1").AsSpan()[..Bc1.BlockSize];
106+
int bytesRead = Bc1.Decompress(data, width, height, out byte[] decodedData);
107+
Assert.That(bytesRead, Is.EqualTo(data.Length));
108+
Assert.That(decodedData, Has.Length.EqualTo(width * height * Unsafe.SizeOf<ColorBGRA32>()));
109+
});
110+
}
111+
112+
[Test]
113+
public void PartialBlock_BC2([Range(1, 4)] int width, [Range(1, 4)] int height)
114+
{
115+
Assert.Multiple(() =>
116+
{
117+
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.DxtTestFiles + "test.dxt3").AsSpan()[..Bc2.BlockSize];
118+
int bytesRead = Bc2.Decompress(data, width, height, out byte[] decodedData);
119+
Assert.That(bytesRead, Is.EqualTo(data.Length));
120+
Assert.That(decodedData, Has.Length.EqualTo(width * height * Unsafe.SizeOf<ColorBGRA32>()));
121+
});
122+
}
123+
124+
[Test]
125+
public void PartialBlock_BC3([Range(1, 4)] int width, [Range(1, 4)] int height)
126+
{
127+
Assert.Multiple(() =>
128+
{
129+
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.DxtTestFiles + "test.dxt5").AsSpan()[..Bc3.BlockSize];
130+
int bytesRead = Bc3.Decompress(data, width, height, out byte[] decodedData);
131+
Assert.That(bytesRead, Is.EqualTo(data.Length));
132+
Assert.That(decodedData, Has.Length.EqualTo(width * height * Unsafe.SizeOf<ColorBGRA32>()));
133+
});
134+
}
135+
136+
[Test]
137+
public void PartialBlock_BC4([Range(1, 4)] int width, [Range(1, 4)] int height)
138+
{
139+
Assert.Multiple(() =>
140+
{
141+
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.BcTestFiles + "test.bc4").AsSpan()[..Bc4.BlockSize];
142+
int bytesRead = Bc4.Decompress(data, width, height, out byte[] decodedData);
143+
Assert.That(bytesRead, Is.EqualTo(data.Length));
144+
Assert.That(decodedData, Has.Length.EqualTo(width * height * Unsafe.SizeOf<ColorBGRA32>()));
145+
});
146+
}
147+
148+
[Test]
149+
public void PartialBlock_BC5([Range(1, 4)] int width, [Range(1, 4)] int height)
150+
{
151+
Assert.Multiple(() =>
152+
{
153+
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.BcTestFiles + "test.bc5").AsSpan()[..Bc5.BlockSize];
154+
int bytesRead = Bc5.Decompress(data, width, height, out byte[] decodedData);
155+
Assert.That(bytesRead, Is.EqualTo(data.Length));
156+
Assert.That(decodedData, Has.Length.EqualTo(width * height * Unsafe.SizeOf<ColorBGRA32>()));
157+
});
158+
}
159+
160+
[Test]
161+
public void PartialBlock_BC6H([Range(1, 4)] int width, [Range(1, 4)] int height)
162+
{
163+
Assert.Multiple(() =>
164+
{
165+
ReadOnlySpan<byte> data = File.ReadAllBytes(TestFileFolders.BcTestFiles + "test.bc6h_best").AsSpan()[..Bc6h.BlockSize];
166+
int bytesRead = Bc6h.Decompress(data, width, height, false, out byte[] decodedData);
167+
Assert.That(bytesRead, Is.EqualTo(data.Length));
168+
Assert.That(decodedData, Has.Length.EqualTo(width * height * Unsafe.SizeOf<ColorBGRA32>()));
169+
});
170+
}
171+
172+
[Test]
173+
public void PartialBlock_BC7([Range(1, 4)] int width, [Range(1, 4)] int height)
103174
{
104175
Assert.Multiple(() =>
105176
{

AssetRipper.TextureDecoder/Bc/Bc1.cs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,22 @@ public static class Bc1
99
/// The size of an encoded block, in bytes.
1010
/// </summary>
1111
public const int BlockSize = 8;
12+
/// <summary>
13+
/// The width of a decoded block, in pixels.
14+
/// </summary>
15+
private const int BlockWidth = 4;
16+
/// <summary>
17+
/// The height of a decoded block, in pixels.
18+
/// </summary>
19+
private const int BlockHeight = 4;
20+
/// <summary>
21+
/// The size of the natural pixel type.
22+
/// </summary>
23+
private static int PixelSize => Unsafe.SizeOf<ColorRGBA<byte>>();
24+
/// <summary>
25+
/// The size of a decoded block, in bytes.
26+
/// </summary>
27+
internal static int DecodedBlockSize => BlockWidth * BlockHeight * PixelSize;
1228

1329
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
1430
{
@@ -18,13 +34,14 @@ public static int Decompress(ReadOnlySpan<byte> input, int width, int height, ou
1834

1935
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
2036
{
37+
Span<byte> buffer = stackalloc byte[DecodedBlockSize];
2138
int inputOffset = 0;
22-
for (int i = 0; i < height; i += 4)
39+
for (int i = 0; i < height; i += BlockHeight)
2340
{
24-
for (int j = 0; j < width; j += 4)
41+
for (int j = 0; j < width; j += BlockWidth)
2542
{
26-
int outputOffset = ((i * width) + j) * Unsafe.SizeOf<ColorRGBA<byte>>();
27-
BcHelpers.DecompressBc1(input.Slice(inputOffset, BlockSize), output.Slice(outputOffset), width * 4);
43+
BcHelpers.DecompressBc1(input.Slice(inputOffset, BlockSize), buffer);
44+
BcHelpers.CopyBufferToOutput(buffer, output, width, height, j, i, BlockWidth, BlockHeight, PixelSize);
2845
inputOffset += BlockSize;
2946
}
3047
}

AssetRipper.TextureDecoder/Bc/Bc2.cs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,22 @@ public static class Bc2
99
/// The size of an encoded block, in bytes.
1010
/// </summary>
1111
public const int BlockSize = 16;
12+
/// <summary>
13+
/// The width of a decoded block, in pixels.
14+
/// </summary>
15+
private const int BlockWidth = 4;
16+
/// <summary>
17+
/// The height of a decoded block, in pixels.
18+
/// </summary>
19+
private const int BlockHeight = 4;
20+
/// <summary>
21+
/// The size of the natural pixel type.
22+
/// </summary>
23+
private static int PixelSize => Unsafe.SizeOf<ColorRGBA<byte>>();
24+
/// <summary>
25+
/// The size of a decoded block, in bytes.
26+
/// </summary>
27+
internal static int DecodedBlockSize => BlockWidth * BlockHeight * PixelSize;
1228

1329
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
1430
{
@@ -18,13 +34,14 @@ public static int Decompress(ReadOnlySpan<byte> input, int width, int height, ou
1834

1935
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
2036
{
37+
Span<byte> buffer = stackalloc byte[DecodedBlockSize];
2138
int inputOffset = 0;
22-
for (int i = 0; i < height; i += 4)
39+
for (int i = 0; i < height; i += BlockHeight)
2340
{
24-
for (int j = 0; j < width; j += 4)
41+
for (int j = 0; j < width; j += BlockWidth)
2542
{
26-
int outputOffset = ((i * width) + j) * Unsafe.SizeOf<ColorRGBA<byte>>();
27-
BcHelpers.DecompressBc2(input.Slice(inputOffset), output.Slice(outputOffset), width * 4);
43+
BcHelpers.DecompressBc2(input.Slice(inputOffset), buffer);
44+
BcHelpers.CopyBufferToOutput(buffer, output, width, height, j, i, BlockWidth, BlockHeight, PixelSize);
2845
inputOffset += BlockSize;
2946
}
3047
}

AssetRipper.TextureDecoder/Bc/Bc3.cs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,22 @@ public static class Bc3
99
/// The size of an encoded block, in bytes.
1010
/// </summary>
1111
public const int BlockSize = 16;
12+
/// <summary>
13+
/// The width of a decoded block, in pixels.
14+
/// </summary>
15+
private const int BlockWidth = 4;
16+
/// <summary>
17+
/// The height of a decoded block, in pixels.
18+
/// </summary>
19+
private const int BlockHeight = 4;
20+
/// <summary>
21+
/// The size of the natural pixel type.
22+
/// </summary>
23+
private static int PixelSize => Unsafe.SizeOf<ColorRGBA<byte>>();
24+
/// <summary>
25+
/// The size of a decoded block, in bytes.
26+
/// </summary>
27+
internal static int DecodedBlockSize => BlockWidth * BlockHeight * PixelSize;
1228

1329
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
1430
{
@@ -18,13 +34,14 @@ public static int Decompress(ReadOnlySpan<byte> input, int width, int height, ou
1834

1935
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
2036
{
37+
Span<byte> buffer = stackalloc byte[DecodedBlockSize];
2138
int inputOffset = 0;
22-
for (int i = 0; i < height; i += 4)
39+
for (int i = 0; i < height; i += BlockHeight)
2340
{
24-
for (int j = 0; j < width; j += 4)
41+
for (int j = 0; j < width; j += BlockWidth)
2542
{
26-
int outputOffset = ((i * width) + j) * Unsafe.SizeOf<ColorRGBA<byte>>();
27-
BcHelpers.DecompressBc3(input.Slice(inputOffset), output.Slice(outputOffset), width * 4);
43+
BcHelpers.DecompressBc3(input.Slice(inputOffset), buffer);
44+
BcHelpers.CopyBufferToOutput(buffer, output, width, height, j, i, BlockWidth, BlockHeight, PixelSize);
2845
inputOffset += BlockSize;
2946
}
3047
}

AssetRipper.TextureDecoder/Bc/Bc4.cs

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,22 @@ public static class Bc4
1010
/// The size of an encoded block, in bytes.
1111
/// </summary>
1212
public const int BlockSize = 8;
13+
/// <summary>
14+
/// The width of a decoded block, in pixels.
15+
/// </summary>
16+
private const int BlockWidth = 4;
17+
/// <summary>
18+
/// The height of a decoded block, in pixels.
19+
/// </summary>
20+
private const int BlockHeight = 4;
21+
/// <summary>
22+
/// The size of the natural pixel type.
23+
/// </summary>
24+
private static int PixelSize => Unsafe.SizeOf<ColorR<byte>>();
25+
/// <summary>
26+
/// The size of a decoded block, in bytes.
27+
/// </summary>
28+
internal static int DecodedBlockSize => BlockWidth * BlockHeight * PixelSize;
1329

1430
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
1531
{
@@ -19,21 +35,22 @@ public static int Decompress(ReadOnlySpan<byte> input, int width, int height, ou
1935

2036
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
2137
{
22-
int bufferSize = width * height * Unsafe.SizeOf<ColorR<byte>>();
23-
byte[] bufferArray = ArrayPool<byte>.Shared.Rent(bufferSize);
24-
Span<byte> buffer = new Span<byte>(bufferArray, 0, bufferSize);
38+
int naturalSize = width * height * PixelSize;
39+
byte[] rentedArray = ArrayPool<byte>.Shared.Rent(naturalSize);
40+
Span<byte> naturalPixels = new Span<byte>(rentedArray, 0, naturalSize);
41+
Span<byte> buffer = stackalloc byte[DecodedBlockSize];
2542
int inputOffset = 0;
26-
for (int i = 0; i < height; i += 4)
43+
for (int i = 0; i < height; i += BlockHeight)
2744
{
28-
for (int j = 0; j < width; j += 4)
45+
for (int j = 0; j < width; j += BlockWidth)
2946
{
30-
int bufferOffset = ((i * width) + j) * Unsafe.SizeOf<ColorR<byte>>();
31-
BcHelpers.DecompressBc4(input.Slice(inputOffset), buffer.Slice(bufferOffset), width);
47+
BcHelpers.DecompressBc4(input.Slice(inputOffset), buffer);
48+
BcHelpers.CopyBufferToOutput(buffer, naturalPixels, width, height, j, i, BlockWidth, BlockHeight, PixelSize);
3249
inputOffset += BlockSize;
3350
}
3451
}
35-
RgbConverter.Convert<ColorR<byte>, byte, ColorBGRA32, byte>(buffer, width, height, output);
36-
ArrayPool<byte>.Shared.Return(bufferArray);
52+
RgbConverter.Convert<ColorR<byte>, byte, ColorBGRA32, byte>(naturalPixels, width, height, output);
53+
ArrayPool<byte>.Shared.Return(rentedArray);
3754
return inputOffset;
3855
}
3956

AssetRipper.TextureDecoder/Bc/Bc5.cs

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,22 @@ public static class Bc5
1010
/// The size of an encoded block, in bytes.
1111
/// </summary>
1212
public const int BlockSize = 16;
13+
/// <summary>
14+
/// The width of a decoded block, in pixels.
15+
/// </summary>
16+
private const int BlockWidth = 4;
17+
/// <summary>
18+
/// The height of a decoded block, in pixels.
19+
/// </summary>
20+
private const int BlockHeight = 4;
21+
/// <summary>
22+
/// The size of the natural pixel type.
23+
/// </summary>
24+
private static int PixelSize => Unsafe.SizeOf<ColorRG<byte>>();
25+
/// <summary>
26+
/// The size of a decoded block, in bytes.
27+
/// </summary>
28+
internal static int DecodedBlockSize => BlockWidth * BlockHeight * PixelSize;
1329

1430
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, out byte[] output)
1531
{
@@ -19,21 +35,22 @@ public static int Decompress(ReadOnlySpan<byte> input, int width, int height, ou
1935

2036
public static int Decompress(ReadOnlySpan<byte> input, int width, int height, Span<byte> output)
2137
{
22-
int bufferSize = width * height * Unsafe.SizeOf<ColorRG<byte>>();
23-
byte[] bufferArray = ArrayPool<byte>.Shared.Rent(bufferSize);
24-
Span<byte> buffer = new Span<byte>(bufferArray, 0, bufferSize);
38+
int naturalSize = width * height * PixelSize;
39+
byte[] rentedArray = ArrayPool<byte>.Shared.Rent(naturalSize);
40+
Span<byte> naturalPixels = new Span<byte>(rentedArray, 0, naturalSize);
41+
Span<byte> buffer = stackalloc byte[DecodedBlockSize];
2542
int inputOffset = 0;
26-
for (int i = 0; i < height; i += 4)
43+
for (int i = 0; i < height; i += BlockHeight)
2744
{
28-
for (int j = 0; j < width; j += 4)
45+
for (int j = 0; j < width; j += BlockWidth)
2946
{
30-
int bufferOffset = ((i * width) + j) * Unsafe.SizeOf<ColorRG<byte>>();
31-
BcHelpers.DecompressBc5(input.Slice(inputOffset), buffer.Slice(bufferOffset), width * 2);
47+
BcHelpers.DecompressBc5(input.Slice(inputOffset), buffer);
48+
BcHelpers.CopyBufferToOutput(buffer, naturalPixels, width, height, j, i, BlockWidth, BlockHeight, PixelSize);
3249
inputOffset += BlockSize;
3350
}
3451
}
35-
RgbConverter.Convert<ColorRG<byte>, byte, ColorBGRA32, byte>(buffer, width, height, output);
36-
ArrayPool<byte>.Shared.Return(bufferArray);
52+
RgbConverter.Convert<ColorRG<byte>, byte, ColorBGRA32, byte>(naturalPixels, width, height, output);
53+
ArrayPool<byte>.Shared.Return(rentedArray);
3754
return inputOffset;
3855
}
3956

0 commit comments

Comments
 (0)