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 }