public IntColor SignExtend(LdrColorA prec) { this.R = SIGN_EXTEND(this.R, prec.R); this.G = SIGN_EXTEND(this.G, prec.G); this.B = SIGN_EXTEND(this.B, prec.B); return(this); }
private static LdrColorA Unquantize(LdrColorA c, LdrColorA RGBAPrec) { LdrColorA q = new LdrColorA(); q.r = Unquantize(c.r, RGBAPrec.r); q.g = Unquantize(c.g, RGBAPrec.g); q.b = Unquantize(c.b, RGBAPrec.b); q.a = RGBAPrec.a > 0 ? Unquantize(c.a, RGBAPrec.a) : (byte)255u; return(q); }
public Bc7ModeInfo(byte uParts, byte uPartBits, byte upBits, byte uRotBits, byte uIndModeBits, byte uIndPrec, byte uIndPrec2, LdrColorA rgbaPrec, LdrColorA rgbaPrecWithP) { this.UPartitions = uParts; this.UPartitionBits = uPartBits; this.UPBits = upBits; this.URotationBits = uRotBits; this.UIndexModeBits = uIndModeBits; this.UIndexPrec = uIndPrec; this.UIndexPrec2 = uIndPrec2; this.RgbaPrec = rgbaPrec; this.RgbaPrecWithP = rgbaPrecWithP; }
private static LdrColorA Unquantize(LdrColorA c, LdrColorA rgbaPrec) { var q = new LdrColorA { R = Unquantize(c.R, rgbaPrec.R), G = Unquantize(c.G, rgbaPrec.G), B = Unquantize(c.B, rgbaPrec.B), A = rgbaPrec.A > 0 ? Unquantize(c.A, rgbaPrec.A) : (byte)255u }; return(q); }
public ModeInfo(byte uParts, byte uPartBits, byte upBits, byte uRotBits, byte uIndModeBits, byte uIndPrec, byte uIndPrec2, LdrColorA rgbaPrec, LdrColorA rgbaPrecWithP) { uPartitions = uParts; uPartitionBits = uPartBits; uPBits = upBits; uRotationBits = uRotBits; uIndexModeBits = uIndModeBits; uIndexPrec = uIndPrec; uIndexPrec2 = uIndPrec2; RGBAPrec = rgbaPrec; RGBAPrecWithP = rgbaPrecWithP; }
/// <inheritdoc/> public byte[] Decompress(byte[] blockData, int width, int height) { byte[] currentBlock = new byte[this.CompressedBytesPerBlock]; IBlock self = this; return(Helper.InMemoryDecode <Bc7>(blockData, width, height, (stream, data, streamIndex, dataIndex, stride) => { // I would prefer to use Span, but not sure if I should reference System.Memory in this project copy data instead. Buffer.BlockCopy(blockData, streamIndex, currentBlock, 0, currentBlock.Length); streamIndex += currentBlock.Length; uint uFirst = 0; while (uFirst < 128 && GetBit(currentBlock, ref uFirst) == 0) { } byte uMode = (byte)(uFirst - 1); if (uMode < 8) { byte uPartitions = ModeInfos[uMode].UPartitions; Debug.Assert(uPartitions < Constants.BC7_MAX_REGIONS, $"uPartitions should be smaller then {Constants.BC7_MAX_REGIONS}"); byte uNumEndPts = (byte)((uPartitions + 1u) << 1); byte uIndexPrec = ModeInfos[uMode].UIndexPrec; byte uIndexPrec2 = ModeInfos[uMode].UIndexPrec2; int i; uint uStartBit = uMode + 1u; int[] p = new int[6]; byte uShape = GetBits(currentBlock, ref uStartBit, ModeInfos[uMode].UPartitionBits); Debug.Assert(uShape < Constants.BC7_MAX_SHAPES, $"uShape should be smaller then {Constants.BC7_MAX_SHAPES}"); byte uRotation = GetBits(currentBlock, ref uStartBit, ModeInfos[uMode].URotationBits); Debug.Assert(uRotation < 4, "uRotation should be less then 4"); byte uIndexMode = GetBits(currentBlock, ref uStartBit, ModeInfos[uMode].UIndexModeBits); Debug.Assert(uIndexMode < 2, "uIndexMode should be less then 2"); var c = new LdrColorA[Constants.BC7_MAX_REGIONS << 1]; for (i = 0; i < c.Length; ++i) { c[i] = new LdrColorA(); } LdrColorA rgbaPrec = ModeInfos[uMode].RgbaPrec; LdrColorA rgbaPrecWithP = ModeInfos[uMode].RgbaPrecWithP; Debug.Assert(uNumEndPts <= (Constants.BC7_MAX_REGIONS << 1), $"uNumEndPts should be smaller or equal to {Constants.BC7_MAX_REGIONS << 1}"); // Red channel for (i = 0; i < uNumEndPts; i++) { if (uStartBit + rgbaPrec.R > 128) { Debug.WriteLine("BC7: Invalid block encountered during decoding"); Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NumPixelsPerBlock, self.DivSize, stride); return dataIndex; } c[i].R = GetBits(currentBlock, ref uStartBit, rgbaPrec.R); } // Green channel for (i = 0; i < uNumEndPts; i++) { if (uStartBit + rgbaPrec.G > 128) { Debug.WriteLine("BC7: Invalid block encountered during decoding"); Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NumPixelsPerBlock, self.DivSize, stride); return dataIndex; } c[i].G = GetBits(currentBlock, ref uStartBit, rgbaPrec.G); } // Blue channel for (i = 0; i < uNumEndPts; i++) { if (uStartBit + rgbaPrec.B > 128) { Debug.WriteLine("BC7: Invalid block encountered during decoding"); Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NumPixelsPerBlock, self.DivSize, stride); return dataIndex; } c[i].B = GetBits(currentBlock, ref uStartBit, rgbaPrec.B); } // Alpha channel for (i = 0; i < uNumEndPts; i++) { if (uStartBit + rgbaPrec.A > 128) { Debug.WriteLine("BC7: Invalid block encountered during decoding"); Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NumPixelsPerBlock, self.DivSize, stride); return dataIndex; } c[i].A = (byte)(rgbaPrec.A != 0 ? GetBits(currentBlock, ref uStartBit, rgbaPrec.A) : 255u); } // P-bits Debug.Assert(ModeInfos[uMode].UPBits <= 6, "ModeInfos[uMode].UPBits should be less then 7"); for (i = 0; i < ModeInfos[uMode].UPBits; i++) { if (uStartBit > 127) { Debug.WriteLine("BC7: Invalid block encountered during decoding"); Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NumPixelsPerBlock, self.DivSize, stride); return dataIndex; } p[i] = GetBit(currentBlock, ref uStartBit); } if (ModeInfos[uMode].UPBits != 0) { for (i = 0; i < uNumEndPts; i++) { int pi = i * ModeInfos[uMode].UPBits / uNumEndPts; for (byte ch = 0; ch < Constants.BC7_NUM_CHANNELS; ch++) { if (rgbaPrec[ch] != rgbaPrecWithP[ch]) { c[i][ch] = (byte)((c[i][ch] << 1) | p[pi]); } } } } for (i = 0; i < uNumEndPts; i++) { c[i] = Unquantize(c[i], rgbaPrecWithP); } byte[] w1 = new byte[Constants.NumPixelsPerBlock], w2 = new byte[Constants.NumPixelsPerBlock]; // read color indices for (i = 0; i < Constants.NumPixelsPerBlock; i++) { uint uNumBits = Helpers.IsFixUpOffset(ModeInfos[uMode].UPartitions, uShape, i) ? uIndexPrec - 1u : uIndexPrec; if (uStartBit + uNumBits > 128) { Debug.WriteLine("BC7: Invalid block encountered during decoding"); Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NumPixelsPerBlock, self.DivSize, stride); return dataIndex; } w1[i] = GetBits(currentBlock, ref uStartBit, uNumBits); } // read alpha indices if (uIndexPrec2 != 0) { for (i = 0; i < Constants.NumPixelsPerBlock; i++) { uint uNumBits = i != 0 ? uIndexPrec2 : uIndexPrec2 - 1u; if (uStartBit + uNumBits > 128) { Debug.WriteLine("BC7: Invalid block encountered during decoding"); Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NumPixelsPerBlock, self.DivSize, stride); return dataIndex; } w2[i] = GetBits(currentBlock, ref uStartBit, uNumBits); } } for (i = 0; i < Constants.NumPixelsPerBlock; ++i) { byte uRegion = Constants.PartitionTable[uPartitions][uShape][i]; var outPixel = new LdrColorA(); if (uIndexPrec2 == 0) { LdrColorA.Interpolate(c[uRegion << 1], c[(uRegion << 1) + 1], w1[i], w1[i], uIndexPrec, uIndexPrec, outPixel); } else { if (uIndexMode == 0) { LdrColorA.Interpolate(c[uRegion << 1], c[(uRegion << 1) + 1], w1[i], w2[i], uIndexPrec, uIndexPrec2, outPixel); } else { LdrColorA.Interpolate(c[uRegion << 1], c[(uRegion << 1) + 1], w2[i], w1[i], uIndexPrec2, uIndexPrec, outPixel); } } switch (uRotation) { case 1: outPixel.SwapRedWithAlpha(); break; case 2: outPixel.SwapGreenWithAlpha(); break; case 3: outPixel.SwapBlueWithAlpha(); break; } // Note: whether it's sRGB is not taken into consideration // we're returning data that could be either/or depending // on the input BC7 format data[dataIndex++] = outPixel.R; data[dataIndex++] = outPixel.G; data[dataIndex++] = outPixel.B; data[dataIndex++] = outPixel.A; // Is mult 4? if (((i + 1) & 0x3) == 0) { dataIndex += self.PixelDepthBytes * (stride - self.DivSize); } } } else { Debug.WriteLine("BC7: Reserved mode 8 encountered during decoding"); // Per the BC7 format spec, we must return transparent black for (int i = 0; i < Constants.NumPixelsPerBlock; ++i) { data[dataIndex++] = 0; data[dataIndex++] = 0; data[dataIndex++] = 0; data[dataIndex++] = 0; // Is mult 4? if (((i + 1) & 0x3) == 0) { dataIndex += self.PixelDepthBytes * (stride - self.DivSize); } } } return streamIndex; })); }
public byte[] Decompress(byte[] blockData, int width, int height) { var currentBlock = new byte[CompressedBytesPerBlock]; IBlock self = this; return(Helper.InMemoryDecode <Bc7>(blockData, width, height, (byte[] stream, byte[] data, int streamIndex, int dataIndex, int stride) => { // I would prefer to use Span, but not sure if I should reference System.Memory in this project // copy data instead Buffer.BlockCopy(blockData, (int)streamIndex, currentBlock, 0, currentBlock.Length); streamIndex += (int)currentBlock.Length; uint uFirst = 0; while (uFirst < 128 && GetBit(currentBlock, ref uFirst) == 0) { } byte uMode = (byte)(uFirst - 1); if (uMode < 8) { byte uPartitions = ms_aInfo[uMode].uPartitions; Debug.Assert(uPartitions < Constants.BC7_MAX_REGIONS); var uNumEndPts = (byte)((uPartitions + 1u) << 1); byte uIndexPrec = ms_aInfo[uMode].uIndexPrec; byte uIndexPrec2 = ms_aInfo[uMode].uIndexPrec2; int i; uint uStartBit = uMode + 1u; int[] P = new int[6]; byte uShape = GetBits(currentBlock, ref uStartBit, ms_aInfo[uMode].uPartitionBits); Debug.Assert(uShape < Constants.BC7_MAX_SHAPES); byte uRotation = GetBits(currentBlock, ref uStartBit, ms_aInfo[uMode].uRotationBits); Debug.Assert(uRotation < 4); byte uIndexMode = GetBits(currentBlock, ref uStartBit, ms_aInfo[uMode].uIndexModeBits); Debug.Assert(uIndexMode < 2); LdrColorA[] c = new LdrColorA[Constants.BC7_MAX_REGIONS << 1]; for (i = 0; i < c.Length; ++i) { c[i] = new LdrColorA(); } LdrColorA RGBAPrec = ms_aInfo[uMode].RGBAPrec; LdrColorA RGBAPrecWithP = ms_aInfo[uMode].RGBAPrecWithP; Debug.Assert(uNumEndPts <= (Constants.BC7_MAX_REGIONS << 1)); // Red channel for (i = 0; i < uNumEndPts; i++) { if (uStartBit + RGBAPrec.r > 128) { Debug.WriteLine("BC7: Invalid block encountered during decoding"); Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NUM_PIXELS_PER_BLOCK, self.DivSize, stride); return dataIndex; } c[i].r = GetBits(currentBlock, ref uStartBit, RGBAPrec.r); } // Green channel for (i = 0; i < uNumEndPts; i++) { if (uStartBit + RGBAPrec.g > 128) { Debug.WriteLine("BC7: Invalid block encountered during decoding"); Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NUM_PIXELS_PER_BLOCK, self.DivSize, stride); return dataIndex; } c[i].g = GetBits(currentBlock, ref uStartBit, RGBAPrec.g); } // Blue channel for (i = 0; i < uNumEndPts; i++) { if (uStartBit + RGBAPrec.b > 128) { Debug.WriteLine("BC7: Invalid block encountered during decoding"); Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NUM_PIXELS_PER_BLOCK, self.DivSize, stride); return dataIndex; } c[i].b = GetBits(currentBlock, ref uStartBit, RGBAPrec.b); } // Alpha channel for (i = 0; i < uNumEndPts; i++) { if (uStartBit + RGBAPrec.a > 128) { Debug.WriteLine("BC7: Invalid block encountered during decoding"); Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NUM_PIXELS_PER_BLOCK, self.DivSize, stride); return dataIndex; } c[i].a = (byte)(RGBAPrec.a != 0 ? GetBits(currentBlock, ref uStartBit, RGBAPrec.a) : 255u); } // P-bits Debug.Assert(ms_aInfo[uMode].uPBits <= 6); for (i = 0; i < ms_aInfo[uMode].uPBits; i++) { if (uStartBit > 127) { Debug.WriteLine("BC7: Invalid block encountered during decoding"); Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NUM_PIXELS_PER_BLOCK, self.DivSize, stride); return dataIndex; } P[i] = GetBit(currentBlock, ref uStartBit); } if (ms_aInfo[uMode].uPBits != 0) { for (i = 0; i < uNumEndPts; i++) { int pi = i * ms_aInfo[uMode].uPBits / uNumEndPts; for (byte ch = 0; ch < Constants.BC7_NUM_CHANNELS; ch++) { if (RGBAPrec[ch] != RGBAPrecWithP[ch]) { c[i][ch] = (byte)((c[i][ch] << 1) | P[pi]); } } } } for (i = 0; i < uNumEndPts; i++) { c[i] = Unquantize(c[i], RGBAPrecWithP); } byte[] w1 = new byte[Constants.NUM_PIXELS_PER_BLOCK], w2 = new byte[Constants.NUM_PIXELS_PER_BLOCK]; // read color indices for (i = 0; i < Constants.NUM_PIXELS_PER_BLOCK; i++) { uint uNumBits = Helpers.IsFixUpOffset(ms_aInfo[uMode].uPartitions, uShape, i) ? uIndexPrec - 1u : uIndexPrec; if (uStartBit + uNumBits > 128) { Debug.WriteLine("BC7: Invalid block encountered during decoding"); Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NUM_PIXELS_PER_BLOCK, self.DivSize, stride); return dataIndex; } w1[i] = GetBits(currentBlock, ref uStartBit, uNumBits); } // read alpha indices if (uIndexPrec2 != 0) { for (i = 0; i < Constants.NUM_PIXELS_PER_BLOCK; i++) { uint uNumBits = (i != 0 ? uIndexPrec2 : uIndexPrec2 - 1u); if (uStartBit + uNumBits > 128) { Debug.WriteLine("BC7: Invalid block encountered during decoding"); Helpers.FillWithErrorColors(data, ref dataIndex, Constants.NUM_PIXELS_PER_BLOCK, self.DivSize, stride); return dataIndex; } w2[i] = GetBits(currentBlock, ref uStartBit, uNumBits); } } for (i = 0; i < Constants.NUM_PIXELS_PER_BLOCK; ++i) { byte uRegion = Constants.g_aPartitionTable[uPartitions][uShape][i]; LdrColorA outPixel = new LdrColorA(); if (uIndexPrec2 == 0) { LdrColorA.Interpolate(c[uRegion << 1], c[(uRegion << 1) + 1], w1[i], w1[i], uIndexPrec, uIndexPrec, outPixel); } else { if (uIndexMode == 0) { LdrColorA.Interpolate(c[uRegion << 1], c[(uRegion << 1) + 1], w1[i], w2[i], uIndexPrec, uIndexPrec2, outPixel); } else { LdrColorA.Interpolate(c[uRegion << 1], c[(uRegion << 1) + 1], w2[i], w1[i], uIndexPrec2, uIndexPrec, outPixel); } } switch (uRotation) { case 1: ByteSwap(ref outPixel.r, ref outPixel.a); break; case 2: ByteSwap(ref outPixel.g, ref outPixel.a); break; case 3: ByteSwap(ref outPixel.b, ref outPixel.a); break; } // Note: whether it's sRGB is not taken into consideration // we're returning data that could be either/or depending // on the input BC7 format data[dataIndex++] = outPixel.b; data[dataIndex++] = outPixel.g; data[dataIndex++] = outPixel.r; data[dataIndex++] = outPixel.a; // Is mult 4? if (((i + 1) & 0x3) == 0) { dataIndex += self.PixelDepthBytes * (stride - self.DivSize); } } } else { Debug.WriteLine("BC7: Reserved mode 8 encountered during decoding"); // Per the BC7 format spec, we must return transparent black for (int i = 0; i < Constants.NUM_PIXELS_PER_BLOCK; ++i) { data[dataIndex++] = 0; data[dataIndex++] = 0; data[dataIndex++] = 0; data[dataIndex++] = 0; // Is mult 4? if (((i + 1) & 0x3) == 0) { dataIndex += self.PixelDepthBytes * (stride - self.DivSize); } } } return streamIndex; })); }