/ src / Ryujinx.Graphics.Texture / BC7Decoder.cs
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  }