/ src / Ryujinx.Graphics.Texture / Astc / AstcDecoder.cs
AstcDecoder.cs
   1  using Ryujinx.Common.Memory;
   2  using Ryujinx.Common.Utilities;
   3  using System;
   4  using System.Diagnostics;
   5  using System.Linq;
   6  using System.Runtime.CompilerServices;
   7  using System.Runtime.InteropServices;
   8  
   9  namespace Ryujinx.Graphics.Texture.Astc
  10  {
  11      // https://github.com/GammaUNC/FasTC/blob/master/ASTCEncoder/src/Decompressor.cpp
  12      public class AstcDecoder
  13      {
  14          private ReadOnlyMemory<byte> InputBuffer { get; }
  15          private Memory<byte> OutputBuffer { get; }
  16  
  17          private int BlockSizeX { get; }
  18          private int BlockSizeY { get; }
  19  
  20          private AstcLevel[] Levels { get; }
  21  
  22          private bool Success { get; set; }
  23  
  24          public int TotalBlockCount { get; }
  25  
  26          public AstcDecoder(
  27              ReadOnlyMemory<byte> inputBuffer,
  28              Memory<byte> outputBuffer,
  29              int blockWidth,
  30              int blockHeight,
  31              int width,
  32              int height,
  33              int depth,
  34              int levels,
  35              int layers)
  36          {
  37              if ((uint)blockWidth > 12)
  38              {
  39                  throw new ArgumentOutOfRangeException(nameof(blockWidth));
  40              }
  41  
  42              if ((uint)blockHeight > 12)
  43              {
  44                  throw new ArgumentOutOfRangeException(nameof(blockHeight));
  45              }
  46  
  47              InputBuffer = inputBuffer;
  48              OutputBuffer = outputBuffer;
  49  
  50              BlockSizeX = blockWidth;
  51              BlockSizeY = blockHeight;
  52  
  53              Levels = new AstcLevel[levels * layers];
  54  
  55              Success = true;
  56  
  57              TotalBlockCount = 0;
  58  
  59              int currentInputBlock = 0;
  60              int currentOutputOffset = 0;
  61  
  62              for (int i = 0; i < levels; i++)
  63              {
  64                  for (int j = 0; j < layers; j++)
  65                  {
  66                      ref AstcLevel level = ref Levels[i * layers + j];
  67  
  68                      level.ImageSizeX = Math.Max(1, width >> i);
  69                      level.ImageSizeY = Math.Max(1, height >> i);
  70                      level.ImageSizeZ = Math.Max(1, depth >> i);
  71  
  72                      level.BlockCountX = (level.ImageSizeX + blockWidth - 1) / blockWidth;
  73                      level.BlockCountY = (level.ImageSizeY + blockHeight - 1) / blockHeight;
  74  
  75                      level.StartBlock = currentInputBlock;
  76                      level.OutputByteOffset = currentOutputOffset;
  77  
  78                      currentInputBlock += level.TotalBlockCount;
  79                      currentOutputOffset += level.PixelCount * 4;
  80                  }
  81              }
  82  
  83              TotalBlockCount = currentInputBlock;
  84          }
  85  
  86          private struct AstcLevel
  87          {
  88              public int ImageSizeX { get; set; }
  89              public int ImageSizeY { get; set; }
  90              public int ImageSizeZ { get; set; }
  91  
  92              public int BlockCountX { get; set; }
  93              public int BlockCountY { get; set; }
  94  
  95              public int StartBlock { get; set; }
  96              public int OutputByteOffset { get; set; }
  97  
  98              public readonly int TotalBlockCount => BlockCountX * BlockCountY * ImageSizeZ;
  99              public readonly int PixelCount => ImageSizeX * ImageSizeY * ImageSizeZ;
 100          }
 101  
 102          public static int QueryDecompressedSize(int sizeX, int sizeY, int sizeZ, int levelCount, int layerCount)
 103          {
 104              int size = 0;
 105  
 106              for (int i = 0; i < levelCount; i++)
 107              {
 108                  int levelSizeX = Math.Max(1, sizeX >> i);
 109                  int levelSizeY = Math.Max(1, sizeY >> i);
 110                  int levelSizeZ = Math.Max(1, sizeZ >> i);
 111  
 112                  size += levelSizeX * levelSizeY * levelSizeZ * layerCount;
 113              }
 114  
 115              return size * 4;
 116          }
 117  
 118          public void ProcessBlock(int index)
 119          {
 120              Buffer16 inputBlock = MemoryMarshal.Cast<byte, Buffer16>(InputBuffer.Span)[index];
 121  
 122              Span<int> decompressedData = stackalloc int[144];
 123  
 124              try
 125              {
 126                  DecompressBlock(inputBlock, decompressedData, BlockSizeX, BlockSizeY);
 127              }
 128              catch (Exception)
 129              {
 130                  Success = false;
 131              }
 132  
 133              Span<byte> decompressedBytes = MemoryMarshal.Cast<int, byte>(decompressedData);
 134  
 135              AstcLevel levelInfo = GetLevelInfo(index);
 136  
 137              WriteDecompressedBlock(decompressedBytes, OutputBuffer.Span[levelInfo.OutputByteOffset..],
 138                  index - levelInfo.StartBlock, levelInfo);
 139          }
 140  
 141          private AstcLevel GetLevelInfo(int blockIndex)
 142          {
 143              foreach (AstcLevel levelInfo in Levels)
 144              {
 145                  if (blockIndex < levelInfo.StartBlock + levelInfo.TotalBlockCount)
 146                  {
 147                      return levelInfo;
 148                  }
 149              }
 150  
 151              throw new AstcDecoderException("Invalid block index.");
 152          }
 153  
 154          private void WriteDecompressedBlock(ReadOnlySpan<byte> block, Span<byte> outputBuffer, int blockIndex, AstcLevel level)
 155          {
 156              int stride = level.ImageSizeX * 4;
 157  
 158              int blockCordX = blockIndex % level.BlockCountX;
 159              int blockCordY = blockIndex / level.BlockCountX;
 160  
 161              int pixelCordX = blockCordX * BlockSizeX;
 162              int pixelCordY = blockCordY * BlockSizeY;
 163  
 164              int outputPixelsX = Math.Min(pixelCordX + BlockSizeX, level.ImageSizeX) - pixelCordX;
 165              int outputPixelsY = Math.Min(pixelCordY + BlockSizeY, level.ImageSizeY * level.ImageSizeZ) - pixelCordY;
 166  
 167              int outputStart = pixelCordX * 4 + pixelCordY * stride;
 168              int outputOffset = outputStart;
 169  
 170              int inputOffset = 0;
 171  
 172              for (int i = 0; i < outputPixelsY; i++)
 173              {
 174                  ReadOnlySpan<byte> blockRow = block.Slice(inputOffset, outputPixelsX * 4);
 175                  Span<byte> outputRow = outputBuffer[outputOffset..];
 176                  blockRow.CopyTo(outputRow);
 177  
 178                  inputOffset += BlockSizeX * 4;
 179                  outputOffset += stride;
 180              }
 181          }
 182  
 183          struct TexelWeightParams
 184          {
 185              public int Width;
 186              public int Height;
 187              public int MaxWeight;
 188              public bool DualPlane;
 189              public bool Error;
 190              public bool VoidExtentLdr;
 191              public bool VoidExtentHdr;
 192  
 193              public readonly int GetPackedBitSize()
 194              {
 195                  // How many indices do we have?
 196                  int indices = Height * Width;
 197  
 198                  if (DualPlane)
 199                  {
 200                      indices *= 2;
 201                  }
 202  
 203                  IntegerEncoded intEncoded = IntegerEncoded.CreateEncoding(MaxWeight);
 204  
 205                  return intEncoded.GetBitLength(indices);
 206              }
 207  
 208              public readonly int GetNumWeightValues()
 209              {
 210                  int ret = Width * Height;
 211  
 212                  if (DualPlane)
 213                  {
 214                      ret *= 2;
 215                  }
 216  
 217                  return ret;
 218              }
 219          }
 220  
 221          public static bool TryDecodeToRgba8(
 222              ReadOnlyMemory<byte> data,
 223              int blockWidth,
 224              int blockHeight,
 225              int width,
 226              int height,
 227              int depth,
 228              int levels,
 229              int layers,
 230              out Span<byte> decoded)
 231          {
 232              byte[] output = new byte[QueryDecompressedSize(width, height, depth, levels, layers)];
 233  
 234              AstcDecoder decoder = new(data, output, blockWidth, blockHeight, width, height, depth, levels, layers);
 235  
 236              for (int i = 0; i < decoder.TotalBlockCount; i++)
 237              {
 238                  decoder.ProcessBlock(i);
 239              }
 240  
 241              decoded = output;
 242  
 243              return decoder.Success;
 244          }
 245  
 246          public static bool TryDecodeToRgba8(
 247              ReadOnlyMemory<byte> data,
 248              Memory<byte> outputBuffer,
 249              int blockWidth,
 250              int blockHeight,
 251              int width,
 252              int height,
 253              int depth,
 254              int levels,
 255              int layers)
 256          {
 257              AstcDecoder decoder = new(data, outputBuffer, blockWidth, blockHeight, width, height, depth, levels, layers);
 258  
 259              for (int i = 0; i < decoder.TotalBlockCount; i++)
 260              {
 261                  decoder.ProcessBlock(i);
 262              }
 263  
 264              return decoder.Success;
 265          }
 266  
 267          public static bool TryDecodeToRgba8P(
 268              ReadOnlyMemory<byte> data,
 269              Memory<byte> outputBuffer,
 270              int blockWidth,
 271              int blockHeight,
 272              int width,
 273              int height,
 274              int depth,
 275              int levels,
 276              int layers)
 277          {
 278              AstcDecoder decoder = new(data, outputBuffer, blockWidth, blockHeight, width, height, depth, levels, layers);
 279  
 280              // Lazy parallelism
 281              Enumerable.Range(0, decoder.TotalBlockCount).AsParallel().ForAll(x => decoder.ProcessBlock(x));
 282  
 283              return decoder.Success;
 284          }
 285  
 286          public static bool TryDecodeToRgba8P(
 287              ReadOnlyMemory<byte> data,
 288              int blockWidth,
 289              int blockHeight,
 290              int width,
 291              int height,
 292              int depth,
 293              int levels,
 294              int layers,
 295              out MemoryOwner<byte> decoded)
 296          {
 297              decoded = MemoryOwner<byte>.Rent(QueryDecompressedSize(width, height, depth, levels, layers));
 298  
 299              AstcDecoder decoder = new(data, decoded.Memory, blockWidth, blockHeight, width, height, depth, levels, layers);
 300  
 301              Enumerable.Range(0, decoder.TotalBlockCount).AsParallel().ForAll(x => decoder.ProcessBlock(x));
 302  
 303              return decoder.Success;
 304          }
 305  
 306          public static bool DecompressBlock(
 307              Buffer16 inputBlock,
 308              Span<int> outputBuffer,
 309              int blockWidth,
 310              int blockHeight)
 311          {
 312              BitStream128 bitStream = new(inputBlock);
 313  
 314              DecodeBlockInfo(ref bitStream, out TexelWeightParams texelParams);
 315  
 316              if (texelParams.Error)
 317              {
 318                  throw new AstcDecoderException("Invalid block mode");
 319              }
 320  
 321              if (texelParams.VoidExtentLdr)
 322              {
 323                  FillVoidExtentLdr(ref bitStream, outputBuffer, blockWidth, blockHeight);
 324  
 325                  return true;
 326              }
 327  
 328              if (texelParams.VoidExtentHdr)
 329              {
 330                  throw new AstcDecoderException("HDR void extent blocks are not supported.");
 331              }
 332  
 333              if (texelParams.Width > blockWidth)
 334              {
 335                  throw new AstcDecoderException("Texel weight grid width should be smaller than block width.");
 336              }
 337  
 338              if (texelParams.Height > blockHeight)
 339              {
 340                  throw new AstcDecoderException("Texel weight grid height should be smaller than block height.");
 341              }
 342  
 343              // Read num partitions
 344              int numberPartitions = bitStream.ReadBits(2) + 1;
 345              Debug.Assert(numberPartitions <= 4);
 346  
 347              if (numberPartitions == 4 && texelParams.DualPlane)
 348              {
 349                  throw new AstcDecoderException("Dual plane mode is incompatible with four partition blocks.");
 350              }
 351  
 352              // Based on the number of partitions, read the color endpoint mode for
 353              // each partition.
 354  
 355              // Determine partitions, partition index, and color endpoint modes
 356              int planeIndices;
 357              int partitionIndex;
 358  
 359              Span<uint> colorEndpointMode = stackalloc uint[4];
 360  
 361              BitStream128 colorEndpointStream = new();
 362  
 363              // Read extra config data...
 364              uint baseColorEndpointMode = 0;
 365  
 366              if (numberPartitions == 1)
 367              {
 368                  colorEndpointMode[0] = (uint)bitStream.ReadBits(4);
 369                  partitionIndex = 0;
 370              }
 371              else
 372              {
 373                  partitionIndex = bitStream.ReadBits(10);
 374                  baseColorEndpointMode = (uint)bitStream.ReadBits(6);
 375              }
 376  
 377              uint baseMode = (baseColorEndpointMode & 3);
 378  
 379              // Remaining bits are color endpoint data...
 380              int numberWeightBits = texelParams.GetPackedBitSize();
 381              int remainingBits = bitStream.BitsLeft - numberWeightBits;
 382  
 383              // Consider extra bits prior to texel data...
 384              uint extraColorEndpointModeBits = 0;
 385  
 386              if (baseMode != 0)
 387              {
 388                  switch (numberPartitions)
 389                  {
 390                      case 2:
 391                          extraColorEndpointModeBits += 2;
 392                          break;
 393                      case 3:
 394                          extraColorEndpointModeBits += 5;
 395                          break;
 396                      case 4:
 397                          extraColorEndpointModeBits += 8;
 398                          break;
 399                      default:
 400                          Debug.Assert(false);
 401                          break;
 402                  }
 403              }
 404  
 405              remainingBits -= (int)extraColorEndpointModeBits;
 406  
 407              // Do we have a dual plane situation?
 408              int planeSelectorBits = 0;
 409  
 410              if (texelParams.DualPlane)
 411              {
 412                  planeSelectorBits = 2;
 413              }
 414  
 415              remainingBits -= planeSelectorBits;
 416  
 417              // Read color data...
 418              int colorDataBits = remainingBits;
 419  
 420              while (remainingBits > 0)
 421              {
 422                  int numberBits = Math.Min(remainingBits, 8);
 423                  int bits = bitStream.ReadBits(numberBits);
 424                  colorEndpointStream.WriteBits(bits, numberBits);
 425                  remainingBits -= 8;
 426              }
 427  
 428              // Read the plane selection bits
 429              planeIndices = bitStream.ReadBits(planeSelectorBits);
 430  
 431              // Read the rest of the CEM
 432              if (baseMode != 0)
 433              {
 434                  uint extraColorEndpointMode = (uint)bitStream.ReadBits((int)extraColorEndpointModeBits);
 435                  uint tempColorEndpointMode = (extraColorEndpointMode << 6) | baseColorEndpointMode;
 436                  tempColorEndpointMode >>= 2;
 437  
 438                  Span<bool> c = stackalloc bool[4];
 439  
 440                  for (int i = 0; i < numberPartitions; i++)
 441                  {
 442                      c[i] = (tempColorEndpointMode & 1) != 0;
 443                      tempColorEndpointMode >>= 1;
 444                  }
 445  
 446                  Span<byte> m = stackalloc byte[4];
 447  
 448                  for (int i = 0; i < numberPartitions; i++)
 449                  {
 450                      m[i] = (byte)(tempColorEndpointMode & 3);
 451                      tempColorEndpointMode >>= 2;
 452                      Debug.Assert(m[i] <= 3);
 453                  }
 454  
 455                  for (int i = 0; i < numberPartitions; i++)
 456                  {
 457                      colorEndpointMode[i] = baseMode;
 458  
 459                      if (!(c[i]))
 460                      {
 461                          colorEndpointMode[i] -= 1;
 462                      }
 463  
 464                      colorEndpointMode[i] <<= 2;
 465                      colorEndpointMode[i] |= m[i];
 466                  }
 467              }
 468              else if (numberPartitions > 1)
 469              {
 470                  uint tempColorEndpointMode = baseColorEndpointMode >> 2;
 471  
 472                  for (int i = 0; i < numberPartitions; i++)
 473                  {
 474                      colorEndpointMode[i] = tempColorEndpointMode;
 475                  }
 476              }
 477  
 478              // Make sure everything up till here is sane.
 479              for (int i = 0; i < numberPartitions; i++)
 480              {
 481                  Debug.Assert(colorEndpointMode[i] < 16);
 482              }
 483              Debug.Assert(bitStream.BitsLeft == texelParams.GetPackedBitSize());
 484  
 485              // Decode both color data and texel weight data
 486              Span<int> colorValues = stackalloc int[32]; // Four values * two endpoints * four maximum partitions
 487              DecodeColorValues(colorValues, ref colorEndpointStream, colorEndpointMode, numberPartitions, colorDataBits);
 488  
 489              EndPointSet endPoints;
 490  
 491              unsafe
 492              {
 493                  // Skip struct initialization
 494                  _ = &endPoints;
 495              }
 496  
 497              int colorValuesPosition = 0;
 498  
 499              for (int i = 0; i < numberPartitions; i++)
 500              {
 501                  ComputeEndpoints(endPoints.Get(i), colorValues, colorEndpointMode[i], ref colorValuesPosition);
 502              }
 503  
 504              // Read the texel weight data.
 505              Buffer16 texelWeightData = inputBlock;
 506  
 507              // Reverse everything
 508              for (int i = 0; i < 8; i++)
 509              {
 510                  byte a = ReverseByte(texelWeightData[i]);
 511                  byte b = ReverseByte(texelWeightData[15 - i]);
 512  
 513                  texelWeightData[i] = b;
 514                  texelWeightData[15 - i] = a;
 515              }
 516  
 517              // Make sure that higher non-texel bits are set to zero
 518              int clearByteStart = (texelParams.GetPackedBitSize() >> 3) + 1;
 519              texelWeightData[clearByteStart - 1] &= (byte)((1 << (texelParams.GetPackedBitSize() % 8)) - 1);
 520  
 521              int cLen = 16 - clearByteStart;
 522              for (int i = clearByteStart; i < clearByteStart + cLen; i++)
 523              {
 524                  texelWeightData[i] = 0;
 525              }
 526  
 527              IntegerSequence texelWeightValues;
 528  
 529              unsafe
 530              {
 531                  // Skip struct initialization
 532                  _ = &texelWeightValues;
 533              }
 534  
 535              texelWeightValues.Reset();
 536  
 537              BitStream128 weightBitStream = new(texelWeightData);
 538  
 539              IntegerEncoded.DecodeIntegerSequence(ref texelWeightValues, ref weightBitStream, texelParams.MaxWeight, texelParams.GetNumWeightValues());
 540  
 541              // Blocks can be at most 12x12, so we can have as many as 144 weights
 542              Weights weights;
 543  
 544              unsafe
 545              {
 546                  // Skip struct initialization
 547                  _ = &weights;
 548              }
 549  
 550              UnquantizeTexelWeights(ref weights, ref texelWeightValues, ref texelParams, blockWidth, blockHeight);
 551  
 552              ushort[] table = Bits.Replicate8_16Table;
 553  
 554              // Now that we have endpoints and weights, we can interpolate and generate
 555              // the proper decoding...
 556              for (int j = 0; j < blockHeight; j++)
 557              {
 558                  for (int i = 0; i < blockWidth; i++)
 559                  {
 560                      int partition = Select2dPartition(partitionIndex, i, j, numberPartitions, ((blockHeight * blockWidth) < 32));
 561                      Debug.Assert(partition < numberPartitions);
 562  
 563                      AstcPixel pixel = new();
 564                      for (int component = 0; component < 4; component++)
 565                      {
 566                          int component0 = endPoints.Get(partition)[0].GetComponent(component);
 567                          component0 = table[component0];
 568                          int component1 = endPoints.Get(partition)[1].GetComponent(component);
 569                          component1 = table[component1];
 570  
 571                          int plane = 0;
 572  
 573                          if (texelParams.DualPlane && (((planeIndices + 1) & 3) == component))
 574                          {
 575                              plane = 1;
 576                          }
 577  
 578                          int weight = weights.Get(plane)[j * blockWidth + i];
 579                          int finalComponent = (component0 * (64 - weight) + component1 * weight + 32) / 64;
 580  
 581                          if (finalComponent == 65535)
 582                          {
 583                              pixel.SetComponent(component, 255);
 584                          }
 585                          else
 586                          {
 587                              double finalComponentFloat = finalComponent;
 588                              pixel.SetComponent(component, (int)(255.0 * (finalComponentFloat / 65536.0) + 0.5));
 589                          }
 590                      }
 591  
 592                      outputBuffer[j * blockWidth + i] = pixel.Pack();
 593                  }
 594              }
 595  
 596              return true;
 597          }
 598  
 599          // Blocks can be at most 12x12, so we can have as many as 144 weights
 600          [StructLayout(LayoutKind.Sequential, Size = 144 * sizeof(int) * Count)]
 601          private struct Weights
 602          {
 603              private int _start;
 604  
 605              public const int Count = 2;
 606  
 607              public Span<int> this[int index]
 608              {
 609                  get
 610                  {
 611                      if ((uint)index >= Count)
 612                      {
 613                          throw new ArgumentOutOfRangeException(nameof(index), index, null);
 614                      }
 615  
 616                      ref int start = ref Unsafe.Add(ref _start, index * 144);
 617  
 618                      return MemoryMarshal.CreateSpan(ref start, 144);
 619                  }
 620              }
 621  
 622              [MethodImpl(MethodImplOptions.AggressiveInlining)]
 623              public Span<int> Get(int index)
 624              {
 625                  ref int start = ref Unsafe.Add(ref _start, index * 144);
 626  
 627                  return MemoryMarshal.CreateSpan(ref start, 144);
 628              }
 629          }
 630  
 631          private static int Select2dPartition(int seed, int x, int y, int partitionCount, bool isSmallBlock)
 632          {
 633              return SelectPartition(seed, x, y, 0, partitionCount, isSmallBlock);
 634          }
 635  
 636          private static int SelectPartition(int seed, int x, int y, int z, int partitionCount, bool isSmallBlock)
 637          {
 638              if (partitionCount == 1)
 639              {
 640                  return 0;
 641              }
 642  
 643              if (isSmallBlock)
 644              {
 645                  x <<= 1;
 646                  y <<= 1;
 647                  z <<= 1;
 648              }
 649  
 650              seed += (partitionCount - 1) * 1024;
 651  
 652              int rightNum = Hash52((uint)seed);
 653              byte seed01 = (byte)(rightNum & 0xF);
 654              byte seed02 = (byte)((rightNum >> 4) & 0xF);
 655              byte seed03 = (byte)((rightNum >> 8) & 0xF);
 656              byte seed04 = (byte)((rightNum >> 12) & 0xF);
 657              byte seed05 = (byte)((rightNum >> 16) & 0xF);
 658              byte seed06 = (byte)((rightNum >> 20) & 0xF);
 659              byte seed07 = (byte)((rightNum >> 24) & 0xF);
 660              byte seed08 = (byte)((rightNum >> 28) & 0xF);
 661              byte seed09 = (byte)((rightNum >> 18) & 0xF);
 662              byte seed10 = (byte)((rightNum >> 22) & 0xF);
 663              byte seed11 = (byte)((rightNum >> 26) & 0xF);
 664              byte seed12 = (byte)(((rightNum >> 30) | (rightNum << 2)) & 0xF);
 665  
 666              seed01 *= seed01;
 667              seed02 *= seed02;
 668              seed03 *= seed03;
 669              seed04 *= seed04;
 670              seed05 *= seed05;
 671              seed06 *= seed06;
 672              seed07 *= seed07;
 673              seed08 *= seed08;
 674              seed09 *= seed09;
 675              seed10 *= seed10;
 676              seed11 *= seed11;
 677              seed12 *= seed12;
 678  
 679              int seedHash1, seedHash2, seedHash3;
 680  
 681              if ((seed & 1) != 0)
 682              {
 683                  seedHash1 = (seed & 2) != 0 ? 4 : 5;
 684                  seedHash2 = (partitionCount == 3) ? 6 : 5;
 685              }
 686              else
 687              {
 688                  seedHash1 = (partitionCount == 3) ? 6 : 5;
 689                  seedHash2 = (seed & 2) != 0 ? 4 : 5;
 690              }
 691  
 692              seedHash3 = (seed & 0x10) != 0 ? seedHash1 : seedHash2;
 693  
 694              seed01 >>= seedHash1;
 695              seed02 >>= seedHash2;
 696              seed03 >>= seedHash1;
 697              seed04 >>= seedHash2;
 698              seed05 >>= seedHash1;
 699              seed06 >>= seedHash2;
 700              seed07 >>= seedHash1;
 701              seed08 >>= seedHash2;
 702              seed09 >>= seedHash3;
 703              seed10 >>= seedHash3;
 704              seed11 >>= seedHash3;
 705              seed12 >>= seedHash3;
 706  
 707              int a = seed01 * x + seed02 * y + seed11 * z + (rightNum >> 14);
 708              int b = seed03 * x + seed04 * y + seed12 * z + (rightNum >> 10);
 709              int c = seed05 * x + seed06 * y + seed09 * z + (rightNum >> 6);
 710              int d = seed07 * x + seed08 * y + seed10 * z + (rightNum >> 2);
 711  
 712              a &= 0x3F;
 713              b &= 0x3F;
 714              c &= 0x3F;
 715              d &= 0x3F;
 716  
 717              if (partitionCount < 4)
 718              {
 719                  d = 0;
 720              }
 721  
 722              if (partitionCount < 3)
 723              {
 724                  c = 0;
 725              }
 726  
 727              if (a >= b && a >= c && a >= d)
 728              {
 729                  return 0;
 730              }
 731              else if (b >= c && b >= d)
 732              {
 733                  return 1;
 734              }
 735              else if (c >= d)
 736              {
 737                  return 2;
 738              }
 739  
 740              return 3;
 741          }
 742  
 743          static int Hash52(uint val)
 744          {
 745              val ^= val >> 15;
 746              val -= val << 17;
 747              val += val << 7;
 748              val += val << 4;
 749              val ^= val >> 5;
 750              val += val << 16;
 751              val ^= val >> 7;
 752              val ^= val >> 3;
 753              val ^= val << 6;
 754              val ^= val >> 17;
 755  
 756              return (int)val;
 757          }
 758  
 759          static void UnquantizeTexelWeights(
 760              ref Weights outputBuffer,
 761              ref IntegerSequence weights,
 762              ref TexelWeightParams texelParams,
 763              int blockWidth,
 764              int blockHeight)
 765          {
 766              int weightIndices = 0;
 767              Weights unquantized;
 768  
 769              unsafe
 770              {
 771                  // Skip struct initialization
 772                  _ = &unquantized;
 773              }
 774  
 775              Span<IntegerEncoded> weightsList = weights.List;
 776              Span<int> unquantized0 = unquantized[0];
 777              Span<int> unquantized1 = unquantized[1];
 778  
 779              for (int i = 0; i < weightsList.Length; i++)
 780              {
 781                  unquantized0[weightIndices] = UnquantizeTexelWeight(weightsList[i]);
 782  
 783                  if (texelParams.DualPlane)
 784                  {
 785                      i++;
 786                      unquantized1[weightIndices] = UnquantizeTexelWeight(weightsList[i]);
 787  
 788                      if (i == weightsList.Length)
 789                      {
 790                          break;
 791                      }
 792                  }
 793  
 794                  if (++weightIndices >= texelParams.Width * texelParams.Height)
 795                  {
 796                      break;
 797                  }
 798              }
 799  
 800              // Do infill if necessary (Section C.2.18) ...
 801              int ds = (1024 + blockWidth / 2) / (blockWidth - 1);
 802              int dt = (1024 + blockHeight / 2) / (blockHeight - 1);
 803  
 804              int planeScale = texelParams.DualPlane ? 2 : 1;
 805  
 806              for (int plane = 0; plane < planeScale; plane++)
 807              {
 808                  Span<int> unquantizedSpan = unquantized.Get(plane);
 809                  Span<int> outputSpan = outputBuffer.Get(plane);
 810  
 811                  for (int t = 0; t < blockHeight; t++)
 812                  {
 813                      for (int s = 0; s < blockWidth; s++)
 814                      {
 815                          int cs = ds * s;
 816                          int ct = dt * t;
 817  
 818                          int gs = (cs * (texelParams.Width - 1) + 32) >> 6;
 819                          int gt = (ct * (texelParams.Height - 1) + 32) >> 6;
 820  
 821                          int js = gs >> 4;
 822                          int fs = gs & 0xF;
 823  
 824                          int jt = gt >> 4;
 825                          int ft = gt & 0x0F;
 826  
 827                          int w11 = (fs * ft + 8) >> 4;
 828  
 829                          int v0 = js + jt * texelParams.Width;
 830  
 831                          int weight = 8;
 832  
 833                          int wxh = texelParams.Width * texelParams.Height;
 834  
 835                          if (v0 < wxh)
 836                          {
 837                              weight += unquantizedSpan[v0] * (16 - fs - ft + w11);
 838  
 839                              if (v0 + 1 < wxh)
 840                              {
 841                                  weight += unquantizedSpan[v0 + 1] * (fs - w11);
 842                              }
 843                          }
 844  
 845                          if (v0 + texelParams.Width < wxh)
 846                          {
 847                              weight += unquantizedSpan[v0 + texelParams.Width] * (ft - w11);
 848  
 849                              if (v0 + texelParams.Width + 1 < wxh)
 850                              {
 851                                  weight += unquantizedSpan[v0 + texelParams.Width + 1] * w11;
 852                              }
 853                          }
 854  
 855                          outputSpan[t * blockWidth + s] = weight >> 4;
 856                      }
 857                  }
 858              }
 859          }
 860  
 861          static int UnquantizeTexelWeight(IntegerEncoded intEncoded)
 862          {
 863              int bitValue = intEncoded.BitValue;
 864              int bitLength = intEncoded.NumberBits;
 865  
 866              int a = Bits.Replicate1_7(bitValue & 1);
 867              int b = 0, c = 0, d = 0;
 868  
 869              int result = 0;
 870  
 871              switch (intEncoded.GetEncoding())
 872              {
 873                  case IntegerEncoded.EIntegerEncoding.JustBits:
 874                      result = Bits.Replicate(bitValue, bitLength, 6);
 875                      break;
 876  
 877                  case IntegerEncoded.EIntegerEncoding.Trit:
 878                      {
 879                          d = intEncoded.TritValue;
 880                          Debug.Assert(d < 3);
 881  
 882                          switch (bitLength)
 883                          {
 884                              case 0:
 885                                  {
 886                                      result = d switch
 887                                      {
 888                                          0 => 0,
 889                                          1 => 32,
 890                                          2 => 63,
 891                                          _ => 0,
 892                                      };
 893  
 894                                      break;
 895                                  }
 896  
 897                              case 1:
 898                                  {
 899                                      c = 50;
 900                                      break;
 901                                  }
 902  
 903                              case 2:
 904                                  {
 905                                      c = 23;
 906                                      int b2 = (bitValue >> 1) & 1;
 907                                      b = (b2 << 6) | (b2 << 2) | b2;
 908  
 909                                      break;
 910                                  }
 911  
 912                              case 3:
 913                                  {
 914                                      c = 11;
 915                                      int cb = (bitValue >> 1) & 3;
 916                                      b = (cb << 5) | cb;
 917  
 918                                      break;
 919                                  }
 920  
 921                              default:
 922                                  throw new AstcDecoderException("Invalid trit encoding for texel weight.");
 923                          }
 924  
 925                          break;
 926                      }
 927  
 928                  case IntegerEncoded.EIntegerEncoding.Quint:
 929                      {
 930                          d = intEncoded.QuintValue;
 931                          Debug.Assert(d < 5);
 932  
 933                          switch (bitLength)
 934                          {
 935                              case 0:
 936                                  {
 937                                      result = d switch
 938                                      {
 939                                          0 => 0,
 940                                          1 => 16,
 941                                          2 => 32,
 942                                          3 => 47,
 943                                          4 => 63,
 944                                          _ => 0,
 945                                      };
 946  
 947                                      break;
 948                                  }
 949  
 950                              case 1:
 951                                  {
 952                                      c = 28;
 953  
 954                                      break;
 955                                  }
 956  
 957                              case 2:
 958                                  {
 959                                      c = 13;
 960                                      int b2 = (bitValue >> 1) & 1;
 961                                      b = (b2 << 6) | (b2 << 1);
 962  
 963                                      break;
 964                                  }
 965  
 966                              default:
 967                                  throw new AstcDecoderException("Invalid quint encoding for texel weight.");
 968                          }
 969  
 970                          break;
 971                      }
 972              }
 973  
 974              if (intEncoded.GetEncoding() != IntegerEncoded.EIntegerEncoding.JustBits && bitLength > 0)
 975              {
 976                  // Decode the value...
 977                  result = d * c + b;
 978                  result ^= a;
 979                  result = (a & 0x20) | (result >> 2);
 980              }
 981  
 982              Debug.Assert(result < 64);
 983  
 984              // Change from [0,63] to [0,64]
 985              if (result > 32)
 986              {
 987                  result += 1;
 988              }
 989  
 990              return result;
 991          }
 992  
 993          static byte ReverseByte(byte b)
 994          {
 995              // Taken from http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith64Bits
 996              return (byte)((((b) * 0x80200802L) & 0x0884422110L) * 0x0101010101L >> 32);
 997          }
 998  
 999          static Span<uint> ReadUintColorValues(int number, Span<int> colorValues, ref int colorValuesPosition)
1000          {
1001              Span<int> ret = colorValues.Slice(colorValuesPosition, number);
1002  
1003              colorValuesPosition += number;
1004  
1005              return MemoryMarshal.Cast<int, uint>(ret);
1006          }
1007  
1008          static Span<int> ReadIntColorValues(int number, Span<int> colorValues, ref int colorValuesPosition)
1009          {
1010              Span<int> ret = colorValues.Slice(colorValuesPosition, number);
1011  
1012              colorValuesPosition += number;
1013  
1014              return ret;
1015          }
1016  
1017          static void ComputeEndpoints(
1018              Span<AstcPixel> endPoints,
1019              Span<int> colorValues,
1020              uint colorEndpointMode,
1021              ref int colorValuesPosition)
1022          {
1023              switch (colorEndpointMode)
1024              {
1025                  case 0:
1026                      {
1027                          Span<uint> val = ReadUintColorValues(2, colorValues, ref colorValuesPosition);
1028  
1029                          endPoints[0] = new AstcPixel(0xFF, (short)val[0], (short)val[0], (short)val[0]);
1030                          endPoints[1] = new AstcPixel(0xFF, (short)val[1], (short)val[1], (short)val[1]);
1031  
1032                          break;
1033                      }
1034  
1035  
1036                  case 1:
1037                      {
1038                          Span<uint> val = ReadUintColorValues(2, colorValues, ref colorValuesPosition);
1039                          int l0 = (int)((val[0] >> 2) | (val[1] & 0xC0));
1040                          int l1 = (int)Math.Min(l0 + (val[1] & 0x3F), 0xFFU);
1041  
1042                          endPoints[0] = new AstcPixel(0xFF, (short)l0, (short)l0, (short)l0);
1043                          endPoints[1] = new AstcPixel(0xFF, (short)l1, (short)l1, (short)l1);
1044  
1045                          break;
1046                      }
1047  
1048                  case 4:
1049                      {
1050                          Span<uint> val = ReadUintColorValues(4, colorValues, ref colorValuesPosition);
1051  
1052                          endPoints[0] = new AstcPixel((short)val[2], (short)val[0], (short)val[0], (short)val[0]);
1053                          endPoints[1] = new AstcPixel((short)val[3], (short)val[1], (short)val[1], (short)val[1]);
1054  
1055                          break;
1056                      }
1057  
1058                  case 5:
1059                      {
1060                          Span<int> val = ReadIntColorValues(4, colorValues, ref colorValuesPosition);
1061  
1062                          Bits.BitTransferSigned(ref val[1], ref val[0]);
1063                          Bits.BitTransferSigned(ref val[3], ref val[2]);
1064  
1065                          endPoints[0] = new AstcPixel((short)val[2], (short)val[0], (short)val[0], (short)val[0]);
1066                          endPoints[1] = new AstcPixel((short)(val[2] + val[3]), (short)(val[0] + val[1]), (short)(val[0] + val[1]), (short)(val[0] + val[1]));
1067  
1068                          endPoints[0].ClampByte();
1069                          endPoints[1].ClampByte();
1070  
1071                          break;
1072                      }
1073  
1074                  case 6:
1075                      {
1076                          Span<uint> val = ReadUintColorValues(4, colorValues, ref colorValuesPosition);
1077  
1078                          endPoints[0] = new AstcPixel(0xFF, (short)(val[0] * val[3] >> 8), (short)(val[1] * val[3] >> 8), (short)(val[2] * val[3] >> 8));
1079                          endPoints[1] = new AstcPixel(0xFF, (short)val[0], (short)val[1], (short)val[2]);
1080  
1081                          break;
1082                      }
1083  
1084                  case 8:
1085                      {
1086                          Span<uint> val = ReadUintColorValues(6, colorValues, ref colorValuesPosition);
1087  
1088                          if (val[1] + val[3] + val[5] >= val[0] + val[2] + val[4])
1089                          {
1090                              endPoints[0] = new AstcPixel(0xFF, (short)val[0], (short)val[2], (short)val[4]);
1091                              endPoints[1] = new AstcPixel(0xFF, (short)val[1], (short)val[3], (short)val[5]);
1092                          }
1093                          else
1094                          {
1095                              endPoints[0] = AstcPixel.BlueContract(0xFF, (short)val[1], (short)val[3], (short)val[5]);
1096                              endPoints[1] = AstcPixel.BlueContract(0xFF, (short)val[0], (short)val[2], (short)val[4]);
1097                          }
1098  
1099                          break;
1100                      }
1101  
1102                  case 9:
1103                      {
1104                          Span<int> val = ReadIntColorValues(6, colorValues, ref colorValuesPosition);
1105  
1106                          Bits.BitTransferSigned(ref val[1], ref val[0]);
1107                          Bits.BitTransferSigned(ref val[3], ref val[2]);
1108                          Bits.BitTransferSigned(ref val[5], ref val[4]);
1109  
1110                          if (val[1] + val[3] + val[5] >= 0)
1111                          {
1112                              endPoints[0] = new AstcPixel(0xFF, (short)val[0], (short)val[2], (short)val[4]);
1113                              endPoints[1] = new AstcPixel(0xFF, (short)(val[0] + val[1]), (short)(val[2] + val[3]), (short)(val[4] + val[5]));
1114                          }
1115                          else
1116                          {
1117                              endPoints[0] = AstcPixel.BlueContract(0xFF, val[0] + val[1], val[2] + val[3], val[4] + val[5]);
1118                              endPoints[1] = AstcPixel.BlueContract(0xFF, val[0], val[2], val[4]);
1119                          }
1120  
1121                          endPoints[0].ClampByte();
1122                          endPoints[1].ClampByte();
1123  
1124                          break;
1125                      }
1126  
1127                  case 10:
1128                      {
1129                          Span<uint> val = ReadUintColorValues(6, colorValues, ref colorValuesPosition);
1130  
1131                          endPoints[0] = new AstcPixel((short)val[4], (short)(val[0] * val[3] >> 8), (short)(val[1] * val[3] >> 8), (short)(val[2] * val[3] >> 8));
1132                          endPoints[1] = new AstcPixel((short)val[5], (short)val[0], (short)val[1], (short)val[2]);
1133  
1134                          break;
1135                      }
1136  
1137                  case 12:
1138                      {
1139                          Span<uint> val = ReadUintColorValues(8, colorValues, ref colorValuesPosition);
1140  
1141                          if (val[1] + val[3] + val[5] >= val[0] + val[2] + val[4])
1142                          {
1143                              endPoints[0] = new AstcPixel((short)val[6], (short)val[0], (short)val[2], (short)val[4]);
1144                              endPoints[1] = new AstcPixel((short)val[7], (short)val[1], (short)val[3], (short)val[5]);
1145                          }
1146                          else
1147                          {
1148                              endPoints[0] = AstcPixel.BlueContract((short)val[7], (short)val[1], (short)val[3], (short)val[5]);
1149                              endPoints[1] = AstcPixel.BlueContract((short)val[6], (short)val[0], (short)val[2], (short)val[4]);
1150                          }
1151  
1152                          break;
1153                      }
1154  
1155                  case 13:
1156                      {
1157                          Span<int> val = ReadIntColorValues(8, colorValues, ref colorValuesPosition);
1158  
1159                          Bits.BitTransferSigned(ref val[1], ref val[0]);
1160                          Bits.BitTransferSigned(ref val[3], ref val[2]);
1161                          Bits.BitTransferSigned(ref val[5], ref val[4]);
1162                          Bits.BitTransferSigned(ref val[7], ref val[6]);
1163  
1164                          if (val[1] + val[3] + val[5] >= 0)
1165                          {
1166                              endPoints[0] = new AstcPixel((short)val[6], (short)val[0], (short)val[2], (short)val[4]);
1167                              endPoints[1] = new AstcPixel((short)(val[7] + val[6]), (short)(val[0] + val[1]), (short)(val[2] + val[3]), (short)(val[4] + val[5]));
1168                          }
1169                          else
1170                          {
1171                              endPoints[0] = AstcPixel.BlueContract(val[6] + val[7], val[0] + val[1], val[2] + val[3], val[4] + val[5]);
1172                              endPoints[1] = AstcPixel.BlueContract(val[6], val[0], val[2], val[4]);
1173                          }
1174  
1175                          endPoints[0].ClampByte();
1176                          endPoints[1].ClampByte();
1177  
1178                          break;
1179                      }
1180  
1181                  default:
1182                      throw new AstcDecoderException("Unsupported color endpoint mode (is it HDR?)");
1183              }
1184          }
1185  
1186          static void DecodeColorValues(
1187              Span<int> outputValues,
1188              ref BitStream128 colorBitStream,
1189              Span<uint> modes,
1190              int numberPartitions,
1191              int numberBitsForColorData)
1192          {
1193              // First figure out how many color values we have
1194              int numberValues = 0;
1195  
1196              for (int i = 0; i < numberPartitions; i++)
1197              {
1198                  numberValues += (int)((modes[i] >> 2) + 1) << 1;
1199              }
1200  
1201              // Then based on the number of values and the remaining number of bits,
1202              // figure out the max value for each of them...
1203              int range = 256;
1204  
1205              while (--range > 0)
1206              {
1207                  IntegerEncoded intEncoded = IntegerEncoded.CreateEncoding(range);
1208                  int bitLength = intEncoded.GetBitLength(numberValues);
1209  
1210                  if (bitLength <= numberBitsForColorData)
1211                  {
1212                      // Find the smallest possible range that matches the given encoding
1213                      while (--range > 0)
1214                      {
1215                          IntegerEncoded newIntEncoded = IntegerEncoded.CreateEncoding(range);
1216                          if (!newIntEncoded.MatchesEncoding(intEncoded))
1217                          {
1218                              break;
1219                          }
1220                      }
1221  
1222                      // Return to last matching range.
1223                      range++;
1224                      break;
1225                  }
1226              }
1227  
1228              // We now have enough to decode our integer sequence.
1229              IntegerSequence integerEncodedSequence;
1230  
1231              unsafe
1232              {
1233                  // Skip struct initialization
1234                  _ = &integerEncodedSequence;
1235              }
1236  
1237              integerEncodedSequence.Reset();
1238  
1239              IntegerEncoded.DecodeIntegerSequence(ref integerEncodedSequence, ref colorBitStream, range, numberValues);
1240  
1241              // Once we have the decoded values, we need to dequantize them to the 0-255 range
1242              // This procedure is outlined in ASTC spec C.2.13
1243              int outputIndices = 0;
1244  
1245              foreach (ref IntegerEncoded intEncoded in integerEncodedSequence.List)
1246              {
1247                  int bitLength = intEncoded.NumberBits;
1248                  int bitValue = intEncoded.BitValue;
1249  
1250                  Debug.Assert(bitLength >= 1);
1251  
1252                  int b = 0, c = 0, d = 0;
1253                  // A is just the lsb replicated 9 times.
1254                  int a = Bits.Replicate(bitValue & 1, 1, 9);
1255  
1256                  switch (intEncoded.GetEncoding())
1257                  {
1258                      case IntegerEncoded.EIntegerEncoding.JustBits:
1259                          {
1260                              outputValues[outputIndices++] = Bits.Replicate(bitValue, bitLength, 8);
1261  
1262                              break;
1263                          }
1264  
1265                      case IntegerEncoded.EIntegerEncoding.Trit:
1266                          {
1267                              d = intEncoded.TritValue;
1268  
1269                              switch (bitLength)
1270                              {
1271                                  case 1:
1272                                      {
1273                                          c = 204;
1274  
1275                                          break;
1276                                      }
1277  
1278                                  case 2:
1279                                      {
1280                                          c = 93;
1281                                          // B = b000b0bb0
1282                                          int b2 = (bitValue >> 1) & 1;
1283                                          b = (b2 << 8) | (b2 << 4) | (b2 << 2) | (b2 << 1);
1284  
1285                                          break;
1286                                      }
1287  
1288                                  case 3:
1289                                      {
1290                                          c = 44;
1291                                          // B = cb000cbcb
1292                                          int cb = (bitValue >> 1) & 3;
1293                                          b = (cb << 7) | (cb << 2) | cb;
1294  
1295                                          break;
1296                                      }
1297  
1298  
1299                                  case 4:
1300                                      {
1301                                          c = 22;
1302                                          // B = dcb000dcb
1303                                          int dcb = (bitValue >> 1) & 7;
1304                                          b = (dcb << 6) | dcb;
1305  
1306                                          break;
1307                                      }
1308  
1309                                  case 5:
1310                                      {
1311                                          c = 11;
1312                                          // B = edcb000ed
1313                                          int edcb = (bitValue >> 1) & 0xF;
1314                                          b = (edcb << 5) | (edcb >> 2);
1315  
1316                                          break;
1317                                      }
1318  
1319                                  case 6:
1320                                      {
1321                                          c = 5;
1322                                          // B = fedcb000f
1323                                          int fedcb = (bitValue >> 1) & 0x1F;
1324                                          b = (fedcb << 4) | (fedcb >> 4);
1325  
1326                                          break;
1327                                      }
1328  
1329                                  default:
1330                                      throw new AstcDecoderException("Unsupported trit encoding for color values.");
1331                              }
1332  
1333                              break;
1334                          }
1335  
1336                      case IntegerEncoded.EIntegerEncoding.Quint:
1337                          {
1338                              d = intEncoded.QuintValue;
1339  
1340                              switch (bitLength)
1341                              {
1342                                  case 1:
1343                                      {
1344                                          c = 113;
1345  
1346                                          break;
1347                                      }
1348  
1349                                  case 2:
1350                                      {
1351                                          c = 54;
1352                                          // B = b0000bb00
1353                                          int b2 = (bitValue >> 1) & 1;
1354                                          b = (b2 << 8) | (b2 << 3) | (b2 << 2);
1355  
1356                                          break;
1357                                      }
1358  
1359                                  case 3:
1360                                      {
1361                                          c = 26;
1362                                          // B = cb0000cbc
1363                                          int cb = (bitValue >> 1) & 3;
1364                                          b = (cb << 7) | (cb << 1) | (cb >> 1);
1365  
1366                                          break;
1367                                      }
1368  
1369                                  case 4:
1370                                      {
1371                                          c = 13;
1372                                          // B = dcb0000dc
1373                                          int dcb = (bitValue >> 1) & 7;
1374                                          b = (dcb << 6) | (dcb >> 1);
1375  
1376                                          break;
1377                                      }
1378  
1379                                  case 5:
1380                                      {
1381                                          c = 6;
1382                                          // B = edcb0000e
1383                                          int edcb = (bitValue >> 1) & 0xF;
1384                                          b = (edcb << 5) | (edcb >> 3);
1385  
1386                                          break;
1387                                      }
1388  
1389                                  default:
1390                                      throw new AstcDecoderException("Unsupported quint encoding for color values.");
1391                              }
1392                              break;
1393                          }
1394                  }
1395  
1396                  if (intEncoded.GetEncoding() != IntegerEncoded.EIntegerEncoding.JustBits)
1397                  {
1398                      int T = d * c + b;
1399                      T ^= a;
1400                      T = (a & 0x80) | (T >> 2);
1401  
1402                      outputValues[outputIndices++] = T;
1403                  }
1404              }
1405  
1406              // Make sure that each of our values is in the proper range...
1407              for (int i = 0; i < numberValues; i++)
1408              {
1409                  Debug.Assert(outputValues[i] <= 255);
1410              }
1411          }
1412  
1413          static void FillVoidExtentLdr(ref BitStream128 bitStream, Span<int> outputBuffer, int blockWidth, int blockHeight)
1414          {
1415              // Don't actually care about the void extent, just read the bits...
1416              for (int i = 0; i < 4; ++i)
1417              {
1418                  bitStream.ReadBits(13);
1419              }
1420  
1421              // Decode the RGBA components and renormalize them to the range [0, 255]
1422              ushort r = (ushort)bitStream.ReadBits(16);
1423              ushort g = (ushort)bitStream.ReadBits(16);
1424              ushort b = (ushort)bitStream.ReadBits(16);
1425              ushort a = (ushort)bitStream.ReadBits(16);
1426  
1427              int rgba = (r >> 8) | (g & 0xFF00) | ((b) & 0xFF00) << 8 | ((a) & 0xFF00) << 16;
1428  
1429              for (int j = 0; j < blockHeight; j++)
1430              {
1431                  for (int i = 0; i < blockWidth; i++)
1432                  {
1433                      outputBuffer[j * blockWidth + i] = rgba;
1434                  }
1435              }
1436          }
1437  
1438          static void DecodeBlockInfo(ref BitStream128 bitStream, out TexelWeightParams texelParams)
1439          {
1440              texelParams = new TexelWeightParams();
1441  
1442              // Read the entire block mode all at once
1443              ushort modeBits = (ushort)bitStream.ReadBits(11);
1444  
1445              // Does this match the void extent block mode?
1446              if ((modeBits & 0x01FF) == 0x1FC)
1447              {
1448                  if ((modeBits & 0x200) != 0)
1449                  {
1450                      texelParams.VoidExtentHdr = true;
1451                  }
1452                  else
1453                  {
1454                      texelParams.VoidExtentLdr = true;
1455                  }
1456  
1457                  // Next two bits must be one.
1458                  if ((modeBits & 0x400) == 0 || bitStream.ReadBits(1) == 0)
1459                  {
1460                      texelParams.Error = true;
1461                  }
1462  
1463                  return;
1464              }
1465  
1466              // First check if the last four bits are zero
1467              if ((modeBits & 0xF) == 0)
1468              {
1469                  texelParams.Error = true;
1470  
1471                  return;
1472              }
1473  
1474              // If the last two bits are zero, then if bits
1475              // [6-8] are all ones, this is also reserved.
1476              if ((modeBits & 0x3) == 0 && (modeBits & 0x1C0) == 0x1C0)
1477              {
1478                  texelParams.Error = true;
1479  
1480                  return;
1481              }
1482  
1483              // Otherwise, there is no error... Figure out the layout
1484              // of the block mode. Layout is determined by a number
1485              // between 0 and 9 corresponding to table C.2.8 of the
1486              // ASTC spec.
1487              int layout;
1488  
1489              if ((modeBits & 0x1) != 0 || (modeBits & 0x2) != 0)
1490              {
1491                  // layout is in [0-4]
1492                  if ((modeBits & 0x8) != 0)
1493                  {
1494                      // layout is in [2-4]
1495                      if ((modeBits & 0x4) != 0)
1496                      {
1497                          // layout is in [3-4]
1498                          if ((modeBits & 0x100) != 0)
1499                          {
1500                              layout = 4;
1501                          }
1502                          else
1503                          {
1504                              layout = 3;
1505                          }
1506                      }
1507                      else
1508                      {
1509                          layout = 2;
1510                      }
1511                  }
1512                  else
1513                  {
1514                      // layout is in [0-1]
1515                      if ((modeBits & 0x4) != 0)
1516                      {
1517                          layout = 1;
1518                      }
1519                      else
1520                      {
1521                          layout = 0;
1522                      }
1523                  }
1524              }
1525              else
1526              {
1527                  // layout is in [5-9]
1528                  if ((modeBits & 0x100) != 0)
1529                  {
1530                      // layout is in [7-9]
1531                      if ((modeBits & 0x80) != 0)
1532                      {
1533                          // layout is in [7-8]
1534                          Debug.Assert((modeBits & 0x40) == 0);
1535  
1536                          if ((modeBits & 0x20) != 0)
1537                          {
1538                              layout = 8;
1539                          }
1540                          else
1541                          {
1542                              layout = 7;
1543                          }
1544                      }
1545                      else
1546                      {
1547                          layout = 9;
1548                      }
1549                  }
1550                  else
1551                  {
1552                      // layout is in [5-6]
1553                      if ((modeBits & 0x80) != 0)
1554                      {
1555                          layout = 6;
1556                      }
1557                      else
1558                      {
1559                          layout = 5;
1560                      }
1561                  }
1562              }
1563  
1564              Debug.Assert(layout < 10);
1565  
1566              // Determine R
1567              int r = (modeBits >> 4) & 1;
1568              if (layout < 5)
1569              {
1570                  r |= (modeBits & 0x3) << 1;
1571              }
1572              else
1573              {
1574                  r |= (modeBits & 0xC) >> 1;
1575              }
1576  
1577              Debug.Assert(2 <= r && r <= 7);
1578  
1579              // Determine width & height
1580              switch (layout)
1581              {
1582                  case 0:
1583                      {
1584                          int a = (modeBits >> 5) & 0x3;
1585                          int b = (modeBits >> 7) & 0x3;
1586  
1587                          texelParams.Width = b + 4;
1588                          texelParams.Height = a + 2;
1589  
1590                          break;
1591                      }
1592  
1593                  case 1:
1594                      {
1595                          int a = (modeBits >> 5) & 0x3;
1596                          int b = (modeBits >> 7) & 0x3;
1597  
1598                          texelParams.Width = b + 8;
1599                          texelParams.Height = a + 2;
1600  
1601                          break;
1602                      }
1603  
1604                  case 2:
1605                      {
1606                          int a = (modeBits >> 5) & 0x3;
1607                          int b = (modeBits >> 7) & 0x3;
1608  
1609                          texelParams.Width = a + 2;
1610                          texelParams.Height = b + 8;
1611  
1612                          break;
1613                      }
1614  
1615                  case 3:
1616                      {
1617                          int a = (modeBits >> 5) & 0x3;
1618                          int b = (modeBits >> 7) & 0x1;
1619  
1620                          texelParams.Width = a + 2;
1621                          texelParams.Height = b + 6;
1622  
1623                          break;
1624                      }
1625  
1626                  case 4:
1627                      {
1628                          int a = (modeBits >> 5) & 0x3;
1629                          int b = (modeBits >> 7) & 0x1;
1630  
1631                          texelParams.Width = b + 2;
1632                          texelParams.Height = a + 2;
1633  
1634                          break;
1635                      }
1636  
1637                  case 5:
1638                      {
1639                          int a = (modeBits >> 5) & 0x3;
1640  
1641                          texelParams.Width = 12;
1642                          texelParams.Height = a + 2;
1643  
1644                          break;
1645                      }
1646  
1647                  case 6:
1648                      {
1649                          int a = (modeBits >> 5) & 0x3;
1650  
1651                          texelParams.Width = a + 2;
1652                          texelParams.Height = 12;
1653  
1654                          break;
1655                      }
1656  
1657                  case 7:
1658                      {
1659                          texelParams.Width = 6;
1660                          texelParams.Height = 10;
1661  
1662                          break;
1663                      }
1664  
1665                  case 8:
1666                      {
1667                          texelParams.Width = 10;
1668                          texelParams.Height = 6;
1669                          break;
1670                      }
1671  
1672                  case 9:
1673                      {
1674                          int a = (modeBits >> 5) & 0x3;
1675                          int b = (modeBits >> 9) & 0x3;
1676  
1677                          texelParams.Width = a + 6;
1678                          texelParams.Height = b + 6;
1679  
1680                          break;
1681                      }
1682  
1683                  default:
1684                      // Don't know this layout...
1685                      texelParams.Error = true;
1686                      break;
1687              }
1688  
1689              // Determine whether or not we're using dual planes
1690              // and/or high precision layouts.
1691              bool d = ((layout != 9) && ((modeBits & 0x400) != 0));
1692              bool h = (layout != 9) && ((modeBits & 0x200) != 0);
1693  
1694              if (h)
1695              {
1696                  ReadOnlySpan<byte> maxWeights = new byte[] { 9, 11, 15, 19, 23, 31 };
1697                  texelParams.MaxWeight = maxWeights[r - 2];
1698              }
1699              else
1700              {
1701                  ReadOnlySpan<byte> maxWeights = new byte[] { 1, 2, 3, 4, 5, 7 };
1702                  texelParams.MaxWeight = maxWeights[r - 2];
1703              }
1704  
1705              texelParams.DualPlane = d;
1706          }
1707      }
1708  }