private static void DecodeBlock(Block block, Span <uint> output, int w, int h, int width) { int mode = BitOperations.TrailingZeroCount((byte)block.Low | 0x100); if (mode == 8) { // Mode is invalid, the spec mandates that hardware fills the block with // a transparent black color. for (int ty = 0; ty < h; ty++) { int baseOffs = ty * width; for (int tx = 0; tx < w; tx++) { int offs = baseOffs + tx; output[offs] = 0; } } return; } BC7ModeInfo modeInfo = BC67Tables.BC7ModeInfos[mode]; int offset = mode + 1; int partition = (int)block.Decode(ref offset, modeInfo.PartitionBitCount); int rotation = (int)block.Decode(ref offset, modeInfo.RotationBitCount); int indexMode = (int)block.Decode(ref offset, modeInfo.IndexModeBitCount); Debug.Assert(partition < 64); Debug.Assert(rotation < 4); Debug.Assert(indexMode < 2); int endPointCount = modeInfo.SubsetCount * 2; Span <RgbaColor32> endPoints = stackalloc RgbaColor32[endPointCount]; Span <byte> pValues = stackalloc byte[modeInfo.PBits]; endPoints.Fill(new RgbaColor32(0, 0, 0, 255)); for (int i = 0; i < endPointCount; i++) { endPoints[i].R = (int)block.Decode(ref offset, modeInfo.ColorDepth); } for (int i = 0; i < endPointCount; i++) { endPoints[i].G = (int)block.Decode(ref offset, modeInfo.ColorDepth); } for (int i = 0; i < endPointCount; i++) { endPoints[i].B = (int)block.Decode(ref offset, modeInfo.ColorDepth); } if (modeInfo.AlphaDepth != 0) { for (int i = 0; i < endPointCount; i++) { endPoints[i].A = (int)block.Decode(ref offset, modeInfo.AlphaDepth); } } for (int i = 0; i < modeInfo.PBits; i++) { pValues[i] = (byte)block.Decode(ref offset, 1); } for (int i = 0; i < endPointCount; i++) { int pBit = -1; if (modeInfo.PBits != 0) { int pIndex = (i * modeInfo.PBits) / endPointCount; pBit = pValues[pIndex]; } Unquantize(ref endPoints[i], modeInfo.ColorDepth, modeInfo.AlphaDepth, pBit); } byte[] partitionTable = BC67Tables.PartitionTable[modeInfo.SubsetCount - 1][partition]; byte[] fixUpTable = BC67Tables.FixUpIndices[modeInfo.SubsetCount - 1][partition]; Span <byte> colorIndices = stackalloc byte[16]; for (int i = 0; i < 16; i++) { byte subset = partitionTable[i]; int bitCount = i == fixUpTable[subset] ? modeInfo.ColorIndexBitCount - 1 : modeInfo.ColorIndexBitCount; colorIndices[i] = (byte)block.Decode(ref offset, bitCount); Debug.Assert(colorIndices[i] < 16); } Span <byte> alphaIndices = stackalloc byte[16]; if (modeInfo.AlphaIndexBitCount != 0) { for (int i = 0; i < 16; i++) { int bitCount = i != 0 ? modeInfo.AlphaIndexBitCount : modeInfo.AlphaIndexBitCount - 1; alphaIndices[i] = (byte)block.Decode(ref offset, bitCount); Debug.Assert(alphaIndices[i] < 16); } } for (int ty = 0; ty < h; ty++) { int baseOffs = ty * width; for (int tx = 0; tx < w; tx++) { int i = ty * 4 + tx; RgbaColor32 color; byte subset = partitionTable[i]; RgbaColor32 color1 = endPoints[subset * 2]; RgbaColor32 color2 = endPoints[subset * 2 + 1]; if (modeInfo.AlphaIndexBitCount != 0) { if (indexMode == 0) { color = BC67Utils.Interpolate(color1, color2, colorIndices[i], alphaIndices[i], modeInfo.ColorIndexBitCount, modeInfo.AlphaIndexBitCount); } else { color = BC67Utils.Interpolate(color1, color2, alphaIndices[i], colorIndices[i], modeInfo.AlphaIndexBitCount, modeInfo.ColorIndexBitCount); } } else { color = BC67Utils.Interpolate(color1, color2, colorIndices[i], colorIndices[i], modeInfo.ColorIndexBitCount, modeInfo.ColorIndexBitCount); } if (rotation != 0) { int a = color.A; switch (rotation) { case 1: color.A = color.R; color.R = a; break; case 2: color.A = color.G; color.G = a; break; case 3: color.A = color.B; color.B = a; break; } } RgbaColor8 color8 = color.GetColor8(); output[baseOffs + tx] = color8.ToUInt32(); } } }
private static void DecodeBlock(Block block, Span <ulong> output, int w, int h, int width, bool signed) { int mode = (int)(block.Low & 3); if ((mode & 2) != 0) { mode = (int)(block.Low & 0x1f); } Span <RgbaColor32> endPoints = stackalloc RgbaColor32[4]; int subsetCount = DecodeEndPoints(ref block, endPoints, mode, signed); if (subsetCount == 0) { // Mode is invalid, the spec mandates that hardware fills the block with // a opaque black color. for (int ty = 0; ty < h; ty++) { int baseOffs = ty * width; for (int tx = 0; tx < w; tx++) { output[baseOffs + tx] = (ulong)HalfOne << 48; } } return; } int partition; int indexBitCount; ulong indices; if (subsetCount > 1) { partition = (int)((block.High >> 13) & 0x1F); indexBitCount = 3; int fixUpIndex = BC67Tables.FixUpIndices[subsetCount - 1][partition][1] * 3; ulong lowMask = (ulong.MaxValue >> (65 - fixUpIndex)) << 3; ulong highMask = ulong.MaxValue << (fixUpIndex + 3); indices = ((block.High >> 16) & highMask) | ((block.High >> 17) & lowMask) | ((block.High >> 18) & 3); } else { partition = 0; indexBitCount = 4; indices = (block.High & ~0xFUL) | ((block.High >> 1) & 7); } ulong indexMask = (1UL << indexBitCount) - 1; for (int ty = 0; ty < h; ty++) { int baseOffs = ty * width; for (int tx = 0; tx < w; tx++) { int offs = baseOffs + tx; int index = (int)(indices & indexMask); int endPointBase = BC67Tables.PartitionTable[subsetCount - 1][partition][ty * 4 + tx] << 1; RgbaColor32 color1 = endPoints[endPointBase]; RgbaColor32 color2 = endPoints[endPointBase + 1]; RgbaColor32 color = BC67Utils.Interpolate(color1, color2, index, indexBitCount); output[offs] = (ulong)FinishUnquantize(color.R, signed) | ((ulong)FinishUnquantize(color.G, signed) << 16) | ((ulong)FinishUnquantize(color.B, signed) << 32) | ((ulong)HalfOne << 48); indices >>= indexBitCount; } } }