BC7Decoder.cs
1 using Ryujinx.Graphics.Texture.Utils; 2 using System; 3 using System.Diagnostics; 4 using System.Numerics; 5 using System.Runtime.InteropServices; 6 7 namespace Ryujinx.Graphics.Texture 8 { 9 static class BC7Decoder 10 { 11 public static void Decode(Span<byte> output, ReadOnlySpan<byte> data, int width, int height) 12 { 13 ReadOnlySpan<Block> blocks = MemoryMarshal.Cast<byte, Block>(data); 14 15 Span<uint> output32 = MemoryMarshal.Cast<byte, uint>(output); 16 17 int wInBlocks = (width + 3) / 4; 18 int hInBlocks = (height + 3) / 4; 19 20 for (int y = 0; y < hInBlocks; y++) 21 { 22 int y2 = y * 4; 23 int bh = Math.Min(4, height - y2); 24 25 for (int x = 0; x < wInBlocks; x++) 26 { 27 int x2 = x * 4; 28 int bw = Math.Min(4, width - x2); 29 30 DecodeBlock(blocks[y * wInBlocks + x], output32[(y2 * width + x2)..], bw, bh, width); 31 } 32 } 33 } 34 35 private static void DecodeBlock(Block block, Span<uint> output, int w, int h, int width) 36 { 37 int mode = BitOperations.TrailingZeroCount((byte)block.Low | 0x100); 38 if (mode == 8) 39 { 40 // Mode is invalid, the spec mandates that hardware fills the block with 41 // a transparent black color. 42 for (int ty = 0; ty < h; ty++) 43 { 44 int baseOffs = ty * width; 45 46 for (int tx = 0; tx < w; tx++) 47 { 48 int offs = baseOffs + tx; 49 50 output[offs] = 0; 51 } 52 } 53 54 return; 55 } 56 57 BC7ModeInfo modeInfo = BC67Tables.BC7ModeInfos[mode]; 58 59 int offset = mode + 1; 60 int partition = (int)block.Decode(ref offset, modeInfo.PartitionBitCount); 61 int rotation = (int)block.Decode(ref offset, modeInfo.RotationBitCount); 62 int indexMode = (int)block.Decode(ref offset, modeInfo.IndexModeBitCount); 63 64 Debug.Assert(partition < 64); 65 Debug.Assert(rotation < 4); 66 Debug.Assert(indexMode < 2); 67 68 int endPointCount = modeInfo.SubsetCount * 2; 69 70 Span<RgbaColor32> endPoints = stackalloc RgbaColor32[endPointCount]; 71 Span<byte> pValues = stackalloc byte[modeInfo.PBits]; 72 73 endPoints.Fill(new RgbaColor32(0, 0, 0, 255)); 74 75 for (int i = 0; i < endPointCount; i++) 76 { 77 endPoints[i].R = (int)block.Decode(ref offset, modeInfo.ColorDepth); 78 } 79 80 for (int i = 0; i < endPointCount; i++) 81 { 82 endPoints[i].G = (int)block.Decode(ref offset, modeInfo.ColorDepth); 83 } 84 85 for (int i = 0; i < endPointCount; i++) 86 { 87 endPoints[i].B = (int)block.Decode(ref offset, modeInfo.ColorDepth); 88 } 89 90 if (modeInfo.AlphaDepth != 0) 91 { 92 for (int i = 0; i < endPointCount; i++) 93 { 94 endPoints[i].A = (int)block.Decode(ref offset, modeInfo.AlphaDepth); 95 } 96 } 97 98 for (int i = 0; i < modeInfo.PBits; i++) 99 { 100 pValues[i] = (byte)block.Decode(ref offset, 1); 101 } 102 103 for (int i = 0; i < endPointCount; i++) 104 { 105 int pBit = -1; 106 107 if (modeInfo.PBits != 0) 108 { 109 int pIndex = (i * modeInfo.PBits) / endPointCount; 110 pBit = pValues[pIndex]; 111 } 112 113 Unquantize(ref endPoints[i], modeInfo.ColorDepth, modeInfo.AlphaDepth, pBit); 114 } 115 116 byte[] partitionTable = BC67Tables.PartitionTable[modeInfo.SubsetCount - 1][partition]; 117 byte[] fixUpTable = BC67Tables.FixUpIndices[modeInfo.SubsetCount - 1][partition]; 118 119 Span<byte> colorIndices = stackalloc byte[16]; 120 121 for (int i = 0; i < 16; i++) 122 { 123 byte subset = partitionTable[i]; 124 int bitCount = i == fixUpTable[subset] ? modeInfo.ColorIndexBitCount - 1 : modeInfo.ColorIndexBitCount; 125 126 colorIndices[i] = (byte)block.Decode(ref offset, bitCount); 127 Debug.Assert(colorIndices[i] < 16); 128 } 129 130 Span<byte> alphaIndices = stackalloc byte[16]; 131 132 if (modeInfo.AlphaIndexBitCount != 0) 133 { 134 for (int i = 0; i < 16; i++) 135 { 136 int bitCount = i != 0 ? modeInfo.AlphaIndexBitCount : modeInfo.AlphaIndexBitCount - 1; 137 138 alphaIndices[i] = (byte)block.Decode(ref offset, bitCount); 139 Debug.Assert(alphaIndices[i] < 16); 140 } 141 } 142 143 for (int ty = 0; ty < h; ty++) 144 { 145 int baseOffs = ty * width; 146 147 for (int tx = 0; tx < w; tx++) 148 { 149 int i = ty * 4 + tx; 150 151 RgbaColor32 color; 152 153 byte subset = partitionTable[i]; 154 155 RgbaColor32 color1 = endPoints[subset * 2]; 156 RgbaColor32 color2 = endPoints[subset * 2 + 1]; 157 158 if (modeInfo.AlphaIndexBitCount != 0) 159 { 160 if (indexMode == 0) 161 { 162 color = BC67Utils.Interpolate(color1, color2, colorIndices[i], alphaIndices[i], modeInfo.ColorIndexBitCount, modeInfo.AlphaIndexBitCount); 163 } 164 else 165 { 166 color = BC67Utils.Interpolate(color1, color2, alphaIndices[i], colorIndices[i], modeInfo.AlphaIndexBitCount, modeInfo.ColorIndexBitCount); 167 } 168 } 169 else 170 { 171 color = BC67Utils.Interpolate(color1, color2, colorIndices[i], colorIndices[i], modeInfo.ColorIndexBitCount, modeInfo.ColorIndexBitCount); 172 } 173 174 if (rotation != 0) 175 { 176 int a = color.A; 177 178 switch (rotation) 179 { 180 case 1: 181 color.A = color.R; 182 color.R = a; 183 break; 184 case 2: 185 color.A = color.G; 186 color.G = a; 187 break; 188 case 3: 189 color.A = color.B; 190 color.B = a; 191 break; 192 } 193 } 194 195 RgbaColor8 color8 = color.GetColor8(); 196 197 output[baseOffs + tx] = color8.ToUInt32(); 198 } 199 } 200 } 201 202 private static void Unquantize(ref RgbaColor32 color, int colorDepth, int alphaDepth, int pBit) 203 { 204 color.R = UnquantizeComponent(color.R, colorDepth, pBit); 205 color.G = UnquantizeComponent(color.G, colorDepth, pBit); 206 color.B = UnquantizeComponent(color.B, colorDepth, pBit); 207 color.A = alphaDepth != 0 ? UnquantizeComponent(color.A, alphaDepth, pBit) : 255; 208 } 209 210 private static int UnquantizeComponent(int component, int bits, int pBit) 211 { 212 int shift = 8 - bits; 213 int value = component << shift; 214 215 if (pBit >= 0) 216 { 217 Debug.Assert(pBit <= 1); 218 value |= value >> (bits + 1); 219 value |= pBit << (shift - 1); 220 } 221 else 222 { 223 value |= value >> bits; 224 } 225 226 return value; 227 } 228 } 229 }