Skip to content

Commit 40ef1f6

Browse files
committed
Generic color support for PVRTC
1 parent eeffa58 commit 40ef1f6

1 file changed

Lines changed: 81 additions & 48 deletions

File tree

AssetRipper.TextureDecoder/Pvrtc/PvrtcDecoder.cs

Lines changed: 81 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,94 @@
11
//#define DISABLE_TWINDDLING_ROUTINE
22
#define ASSUME_IMAGE_TILING
33

4+
using AssetRipper.TextureDecoder.Rgb;
5+
using AssetRipper.TextureDecoder.Rgb.Formats;
6+
using System.Diagnostics;
7+
48
namespace AssetRipper.TextureDecoder.Pvrtc
59
{
610
public static partial class PvrtcDecoder
711
{
812
/// <summary>
9-
/// Decompresses PVRTC to RGBA 8888
13+
/// Decompresses PVRTC to BGRA 8888
1014
/// </summary>
1115
/// <param name="input">The PVRTC texture data to decompress</param>
1216
/// <param name="xDim">X dimension (width) of the texture</param>
1317
/// <param name="yDim">Y dimension (height) of the texture</param>
1418
/// <param name="output">The decompressed texture data</param>
1519
/// <param name="do2bitMode">Signifies whether the data is PVRTC2 or PVRTC4</param>
1620
/// <returns>The number of bytes read from <paramref name="input"/></returns>
17-
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
1821
public static int DecompressPVRTC(ReadOnlySpan<byte> input, int xDim, int yDim, bool do2bitMode, out byte[] output)
1922
{
20-
output = new byte[xDim * yDim * sizeof(uint)];
21-
return DecompressPVRTC(input, xDim, yDim, do2bitMode, output);
23+
return DecompressPVRTC<ColorBGRA32, byte>(input, xDim, yDim, do2bitMode, out output);
2224
}
2325

2426
/// <summary>
25-
/// Decompresses PVRTC to RGBA 8888
27+
/// Decompresses PVRTC to BGRA 8888
2628
/// </summary>
2729
/// <param name="input">The PVRTC texture data to decompress</param>
2830
/// <param name="xDim">X dimension (width) of the texture</param>
2931
/// <param name="yDim">Y dimension (height) of the texture</param>
3032
/// <param name="output">The decompressed texture data</param>
3133
/// <param name="do2bitMode">Signifies whether the data is PVRTC2 or PVRTC4</param>
3234
/// <returns>The number of bytes read from <paramref name="input"/></returns>
33-
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
3435
public static int DecompressPVRTC(ReadOnlySpan<byte> input, int xDim, int yDim, bool do2bitMode, Span<byte> output)
36+
{
37+
return DecompressPVRTC<ColorBGRA32, byte>(input, xDim, yDim, do2bitMode, output);
38+
}
39+
40+
/// <summary>
41+
/// Decompresses PVRTC
42+
/// </summary>
43+
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
44+
/// <typeparam name="TOutputChannelValue">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
45+
/// <param name="input">The PVRTC texture data to decompress</param>
46+
/// <param name="xDim">X dimension (width) of the texture</param>
47+
/// <param name="yDim">Y dimension (height) of the texture</param>
48+
/// <param name="output">The decompressed texture data</param>
49+
/// <param name="do2bitMode">Signifies whether the data is PVRTC2 or PVRTC4</param>
50+
/// <returns>The number of bytes read from <paramref name="input"/></returns>
51+
public static int DecompressPVRTC<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int xDim, int yDim, bool do2bitMode, out byte[] output)
52+
where TOutputChannelValue : unmanaged
53+
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
54+
{
55+
output = new byte[xDim * yDim * Unsafe.SizeOf<TOutputColor>()];
56+
return DecompressPVRTC<TOutputColor, TOutputChannelValue>(input, xDim, yDim, do2bitMode, output);
57+
}
58+
59+
/// <summary>
60+
/// Decompresses PVRTC
61+
/// </summary>
62+
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
63+
/// <typeparam name="TOutputChannelValue">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
64+
/// <param name="input">The PVRTC texture data to decompress</param>
65+
/// <param name="xDim">X dimension (width) of the texture</param>
66+
/// <param name="yDim">Y dimension (height) of the texture</param>
67+
/// <param name="output">The decompressed texture data</param>
68+
/// <param name="do2bitMode">Signifies whether the data is PVRTC2 or PVRTC4</param>
69+
/// <returns>The number of bytes read from <paramref name="input"/></returns>
70+
public static int DecompressPVRTC<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int xDim, int yDim, bool do2bitMode, Span<byte> output)
71+
where TOutputChannelValue : unmanaged
72+
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
73+
{
74+
ThrowHelper.ThrowIfNotLittleEndian();
75+
return DecompressPVRTC<TOutputColor, TOutputChannelValue>(input, xDim, yDim, do2bitMode, MemoryMarshal.Cast<byte, TOutputColor>(output));
76+
}
77+
78+
/// <summary>
79+
/// Decompresses PVRTC
80+
/// </summary>
81+
/// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
82+
/// <typeparam name="TOutputChannelValue">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
83+
/// <param name="input">The PVRTC texture data to decompress</param>
84+
/// <param name="xDim">X dimension (width) of the texture</param>
85+
/// <param name="yDim">Y dimension (height) of the texture</param>
86+
/// <param name="output">The decompressed texture data</param>
87+
/// <param name="do2bitMode">Signifies whether the data is PVRTC2 or PVRTC4</param>
88+
/// <returns>The number of bytes read from <paramref name="input"/></returns>
89+
public static int DecompressPVRTC<TOutputColor, TOutputChannelValue>(ReadOnlySpan<byte> input, int xDim, int yDim, bool do2bitMode, Span<TOutputColor> output)
90+
where TOutputChannelValue : unmanaged
91+
where TOutputColor : unmanaged, IColor<TOutputChannelValue>
3592
{
3693
ThrowHelper.ThrowIfNotLittleEndian();
3794
int xBlockSize = do2bitMode ? BlockX2bpp : BlockX4bpp;
@@ -109,12 +166,12 @@ public static int DecompressPVRTC(ReadOnlySpan<byte> input, int xDim, int yDim,
109166
InterpolateColours(m_colors[0], m_colors[1], m_colors[2], m_colors[3], 1, do2bitMode, x, y, bSig);
110167
GetModulationValue(x, y, do2bitMode, modulationVals, modulationModes, out int mod, out bool doPT);
111168

112-
// compute the modulated color. Swap red and blue channel
113-
int position = (x + y * xDim) << 2;
114-
output[position + 0] = (byte)((aSig[2] * 8 + mod * (bSig[2] - aSig[2])) >> 3);
115-
output[position + 1] = (byte)((aSig[1] * 8 + mod * (bSig[1] - aSig[1])) >> 3);
116-
output[position + 2] = (byte)((aSig[0] * 8 + mod * (bSig[0] - aSig[0])) >> 3);
117-
output[position + 3] = doPT ? (byte)0 : (byte)((aSig[3] * 8 + mod * (bSig[3] - aSig[3])) >> 3);
169+
// compute the modulated color.
170+
byte red = (byte)((aSig[0] * 8 + mod * (bSig[0] - aSig[0])) >> 3);
171+
byte green = (byte)((aSig[1] * 8 + mod * (bSig[1] - aSig[1])) >> 3);
172+
byte blue = (byte)((aSig[2] * 8 + mod * (bSig[2] - aSig[2])) >> 3);
173+
byte alpha = doPT ? (byte)0 : (byte)((aSig[3] * 8 + mod * (bSig[3] - aSig[3])) >> 3);
174+
output[x + y * xDim].SetConvertedChannels<TOutputColor, TOutputChannelValue, byte>(red, green, blue, alpha);
118175
}
119176
}
120177

@@ -133,24 +190,10 @@ public static int DecompressPVRTC(ReadOnlySpan<byte> input, int xDim, int yDim,
133190
/// <returns>The twiddled offset of the pixel</returns>
134191
private static uint TwiddleUV(uint ySize, uint xSize, uint yPos, uint xPos)
135192
{
136-
#if DEBUG
137-
if (yPos >= ySize)
138-
{
139-
throw new Exception();
140-
}
141-
if (xPos >= xSize)
142-
{
143-
throw new Exception();
144-
}
145-
if (!BitOperations.IsPow2(ySize))
146-
{
147-
throw new Exception();
148-
}
149-
if (!BitOperations.IsPow2(xSize))
150-
{
151-
throw new Exception();
152-
}
153-
#endif
193+
Debug.Assert(yPos < ySize);
194+
Debug.Assert(xPos < xSize);
195+
Debug.Assert(BitOperations.IsPow2(ySize));
196+
Debug.Assert(BitOperations.IsPow2(xSize));
154197

155198
uint minDimension;
156199
uint maxValue;
@@ -291,16 +334,11 @@ private static void InterpolateColours(ReadOnlySpan<int> colorP, ReadOnlySpan<in
291334
}
292335
}
293336

294-
#if DEBUG
295337
// sanity check
296-
for (int i = 0; i < 4; i++)
297-
{
298-
if (result[i] >= 256)
299-
{
300-
throw new Exception("Sanity failed");
301-
}
302-
}
303-
#endif
338+
Debug.Assert(result[0] < 256);
339+
Debug.Assert(result[1] < 256);
340+
Debug.Assert(result[2] < 256);
341+
Debug.Assert(result[3] < 256);
304342

305343
// convert from 5554 to 8888, do RGB 5.3 => 8
306344
for (int i = 0; i < 3; i++)
@@ -309,16 +347,11 @@ private static void InterpolateColours(ReadOnlySpan<int> colorP, ReadOnlySpan<in
309347
}
310348
result[3] += result[3] >> 4;
311349

312-
#if DEBUG
313350
// 2nd sanity check
314-
for (int i = 0; i < 4; i++)
315-
{
316-
if (result[i] >= 256)
317-
{
318-
throw new Exception("2nd sanity failed");
319-
}
320-
}
321-
#endif
351+
Debug.Assert(result[0] < 256);
352+
Debug.Assert(result[1] < 256);
353+
Debug.Assert(result[2] < 256);
354+
Debug.Assert(result[3] < 256);
322355
}
323356

324357
/// <summary>

0 commit comments

Comments
 (0)