public static void ExportDDSHeader(Stream destination, DDSConvertParameters @params) { using (BinaryWriter binWriter = new BinaryWriter(destination, Encoding.UTF8, true)) { binWriter.Write(MagicNumber); binWriter.Write(HeaderSize); binWriter.Write((uint)@params.DFlags); binWriter.Write(@params.Height); binWriter.Write(@params.Width); binWriter.Write(@params.PitchOrLinearSize); binWriter.Write(@params.Depth); binWriter.Write(@params.MipMapCount); // read alphabitdepth? for (int i = 0; i < 11; i++) // reserved { binWriter.Write(0); } DDSPixelFormat pixelFormat = new DDSPixelFormat() { Flags = @params.PixelFormatFlags, FourCC = @params.FourCC, RGBBitCount = @params.RGBBitCount, RBitMask = @params.RBitMask, GBitMask = @params.GBitMask, BBitMask = @params.BBitMask, ABitMask = @params.ABitMask, }; pixelFormat.Write(binWriter); binWriter.Write((uint)@params.Caps); binWriter.Write((uint)Caps2); binWriter.Write(0); // caps3 binWriter.Write(0); // caps4 binWriter.Write(0); // reserved (texturestage?) } }
private static void ExportR16ToDDS(BinaryReader sourceReader, Stream destination, DDSConvertParameters @params) { DDSConvertParameters bgraParams = new DDSConvertParameters(); @params.CopyTo(bgraParams); bgraParams.RGBBitCount = 32; bgraParams.RBitMask = 0xFF0000; bgraParams.GBitMask = 0xFF00; bgraParams.BBitMask = 0xFF; bgraParams.ABitMask = 0xFF000000; ExportDDSHeader(destination, bgraParams); long pixelCount = @params.BitMapDepth * @params.Height * @params.Width; for (int i = 0; i < pixelCount; i++) { ushort pixel = sourceReader.ReadUInt16(); float f = Half.ToHalf(pixel); byte R = (byte)Math.Ceiling(f * 255.0); destination.WriteByte(0); // B destination.WriteByte(0); // G destination.WriteByte(R); // R destination.WriteByte(0xFF); // A } }
public static void ExportDDS(Stream source, Stream destination, DDSConvertParameters @params) { using (BinaryReader sourceReader = new BinaryReader(source)) { ExportDDS(sourceReader, destination, @params); } }
public static void DecompressRGB(Stream source, Stream destination, DDSConvertParameters @params) { using (BinaryReader reader = new BinaryReader(source)) { DecompressRGBA(reader, destination, @params, false); } }
public static void DecompressDXT1(Stream source, Stream destination, DDSConvertParameters @params) { using (BinaryReader sourceReader = new BinaryReader(source)) { DecompressDXT1(sourceReader, destination, @params); } }
private static void ExportARGB32ToDDS(BinaryReader sourceReader, Stream destination, DDSConvertParameters @params) { DDSConvertParameters bgraParams = new DDSConvertParameters(); @params.CopyTo(bgraParams); bgraParams.RBitMask = 0xFF0000; bgraParams.GBitMask = 0xFF00; bgraParams.BBitMask = 0xFF; bgraParams.ABitMask = 0xFF000000; ExportDDSHeader(destination, bgraParams); long pixelCount = @params.BitMapDepth * @params.Height * @params.Width; for (int i = 0; i < pixelCount; i++) { byte A = sourceReader.ReadByte(); byte R = sourceReader.ReadByte(); byte G = sourceReader.ReadByte(); byte B = sourceReader.ReadByte(); destination.WriteByte(B); // B destination.WriteByte(G); // G destination.WriteByte(R); // R destination.WriteByte(A); // A } }
public static void ExportDDSHeader(byte[] buffer, int offset, DDSConvertParameters @params) { using (MemoryStream stream = new MemoryStream(buffer)) { stream.Position = offset; ExportDDSHeader(stream, @params); } }
public static long GetDecompressedSize(DDSConvertParameters @params) { int width = @params.Width; int height = @params.Height; int depth = @params.BitMapDepth; int bpp = GetRGBABytesPerPixel(@params); int bpc = GetBytesPerColor(@params); int bps = width * bpp * bpc; long sizeOfPlane = bps * height; return(depth * sizeOfPlane + height * bps + width * bpp); }
private static int GetBytesPerPixel(DDSConvertParameters @params) { if (@params.PixelFormatFlags.IsFourCC()) { switch (@params.FourCC) { case DDSFourCCType.DXT1: case DDSFourCCType.DXT2: case DDSFourCCType.DXT3: case DDSFourCCType.DXT4: case DDSFourCCType.DXT5: return(4); case DDSFourCCType.ATI1: return(1); case DDSFourCCType.ATI2: case DDSFourCCType.RXGB: return(3); case DDSFourCCType.oNULL: return(2); case DDSFourCCType.DollarNULL: case DDSFourCCType.qNULL: case DDSFourCCType.sNULL: return(8); case DDSFourCCType.tNULL: return(16); case DDSFourCCType.pNULL: case DDSFourCCType.rNULL: return(4); default: throw new Exception($"Unsupported CCType {@params.FourCC}"); } } else { return(@params.RGBBitCount / 8); } }
public static void ExportDDS(BinaryReader sourceReader, Stream destination, DDSConvertParameters @params) { if (IsRGBA32(@params)) { ExportRGBA32ToDDS(sourceReader, destination, @params); } else if (IsARGB32(@params)) { ExportARGB32ToDDS(sourceReader, destination, @params); } else if (IsRGBA16(@params)) { ExportRGBA16ToDDS(sourceReader, destination, @params); } else if (IsAlpha8(@params)) { ExportAlpha8ToDDS(sourceReader, destination, @params); } else if (IsR8(@params)) { ExportR8ToDDS(sourceReader, destination, @params); } else if (IsR16(@params)) { ExportR16ToDDS(sourceReader, destination, @params); } else if (IsRG16(@params)) { ExportRG16ToDDS(sourceReader, destination, @params); } else { ExportDDSHeader(destination, @params); for (int i = 0; i < @params.DataLength; i += 2) { ushort value = sourceReader.ReadUInt16(); byte value0 = unchecked ((byte)(value >> 0)); byte value1 = unchecked ((byte)(value >> 8)); destination.WriteByte(value0); destination.WriteByte(value1); } } }
private static int GetRGBABytesPerPixel(DDSConvertParameters @params) { if (@params.PixelFormatFlags.IsFourCC()) { switch (@params.FourCC) { case DDSFourCCType.DXT1: case DDSFourCCType.DXT2: case DDSFourCCType.DXT3: case DDSFourCCType.DXT4: case DDSFourCCType.DXT5: return(4); default: throw new Exception($"Unsupported CCType {@params.FourCC}"); } } else { return(4); } }
private static void ExportRGBA16ToDDS(BinaryReader sourceReader, Stream destination, DDSConvertParameters @params) { DDSConvertParameters bgraParams = new DDSConvertParameters(); @params.CopyTo(bgraParams); bgraParams.RBitMask = 0xF00; bgraParams.BBitMask = 0xF; ExportDDSHeader(destination, bgraParams); long pixelCount = @params.BitMapDepth * @params.Height * @params.Width; for (int i = 0; i < pixelCount; i++) { int pixel = sourceReader.ReadUInt16(); int c1 = (0x00F0 & pixel) >> 4; // B int c2 = (0x0F00 & pixel) >> 4; // G destination.WriteByte((byte)(c1 | c2)); c1 = (0xF000 & pixel) >> 12; // R c2 = (0x000F & pixel) << 4; // A destination.WriteByte((byte)(c1 | c2)); } }
public static void DecompressDXT5(BinaryReader sourceReader, Stream destination, DDSConvertParameters @params) { int depth = @params.BitMapDepth; int width = @params.Width; int height = @params.Height; int bpp = GetRGBABytesPerPixel(@params); int bpc = GetBytesPerColor(@params); int bps = width * bpp * bpc; long sizeOfPlane = bps * height; Color8888[] colors = new Color8888[4]; ushort[] alphas = new ushort[8]; byte[] alphaMask = new byte[6]; long position = destination.Position; for (int z = 0; z < depth; z++) { // mirror Y for (int y = height - 1; y >= 0; y -= 4) { for (int x = 0; x < width; x += 4) { ushort alpha = sourceReader.ReadUInt16(); alphas[0] = unchecked ((byte)(alpha >> 0)); alphas[1] = unchecked ((byte)(alpha >> 8)); ushort alphaMask0 = sourceReader.ReadUInt16(); ushort alphaMask1 = sourceReader.ReadUInt16(); ushort alphaMask2 = sourceReader.ReadUInt16(); alphaMask[0] = unchecked ((byte)(alphaMask0 >> 0)); alphaMask[1] = unchecked ((byte)(alphaMask0 >> 8)); alphaMask[2] = unchecked ((byte)(alphaMask1 >> 0)); alphaMask[3] = unchecked ((byte)(alphaMask1 >> 8)); alphaMask[4] = unchecked ((byte)(alphaMask2 >> 0)); alphaMask[5] = unchecked ((byte)(alphaMask2 >> 8)); colors[0] = DxtcRead2bColor(sourceReader); colors[1] = DxtcRead2bColor(sourceReader); uint bitMask0 = sourceReader.ReadUInt16(); uint bitMask1 = sourceReader.ReadUInt16(); uint bitMask = (bitMask0 << 0) | (bitMask1 << 16); // Four-color block: derive the other two colors. // 00 = color_0, 01 = color_1, 10 = color_2, 11 = color_3 // These 2-bit codes correspond to the 2-bit fields // stored in the 64-bit block. colors[2].Blue = unchecked ((byte)((2 * colors[0].Blue + colors[1].Blue + 1) / 3)); colors[2].Green = unchecked ((byte)((2 * colors[0].Green + colors[1].Green + 1) / 3)); colors[2].Red = unchecked ((byte)((2 * colors[0].Red + colors[1].Red + 1) / 3)); colors[3].Blue = unchecked ((byte)((colors[0].Blue + 2 * colors[1].Blue + 1) / 3)); colors[3].Green = unchecked ((byte)((colors[0].Green + 2 * colors[1].Green + 1) / 3)); colors[3].Red = unchecked ((byte)((colors[0].Red + 2 * colors[1].Red + 1) / 3)); int bitIndex = 0; for (int ly = 0; ly < 4; ly++) { for (int lx = 0; lx < 4; lx++, bitIndex++) { int colorIndex = (int)((bitMask & (0x03 << bitIndex * 2)) >> bitIndex * 2); Color8888 col = colors[colorIndex]; // only put pixels out < width or height if ((x + lx) < width && (y - ly) < height && (y - ly) >= 0) { // mirror Y long offset = z * sizeOfPlane + (y - ly) * bps + (x + lx) * bpp; destination.Position = position + offset; destination.WriteByte(col.Red); destination.WriteByte(col.Green); destination.WriteByte(col.Blue); } } } // 8-alpha or 6-alpha block? if (alphas[0] > alphas[1]) { // 8-alpha block: derive the other six alphas. // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated. alphas[2] = unchecked ((ushort)((6 * alphas[0] + 1 * alphas[1] + 3) / 7)); // bit code 010 alphas[3] = unchecked ((ushort)((5 * alphas[0] + 2 * alphas[1] + 3) / 7)); // bit code 011 alphas[4] = unchecked ((ushort)((4 * alphas[0] + 3 * alphas[1] + 3) / 7)); // bit code 100 alphas[5] = unchecked ((ushort)((3 * alphas[0] + 4 * alphas[1] + 3) / 7)); // bit code 101 alphas[6] = unchecked ((ushort)((2 * alphas[0] + 5 * alphas[1] + 3) / 7)); // bit code 110 alphas[7] = unchecked ((ushort)((1 * alphas[0] + 6 * alphas[1] + 3) / 7)); // bit code 111 } else { // 6-alpha block. // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated. alphas[2] = unchecked ((ushort)((4 * alphas[0] + 1 * alphas[1] + 2) / 5)); // Bit code 010 alphas[3] = unchecked ((ushort)((3 * alphas[0] + 2 * alphas[1] + 2) / 5)); // Bit code 011 alphas[4] = unchecked ((ushort)((2 * alphas[0] + 3 * alphas[1] + 2) / 5)); // Bit code 100 alphas[5] = unchecked ((ushort)((1 * alphas[0] + 4 * alphas[1] + 2) / 5)); // Bit code 101 alphas[6] = 0x00; // Bit code 110 alphas[7] = 0xFF; // Bit code 111 } // Note: Have to separate the next two loops, // it operates on a 6-byte system. // First three bytes //uint bits = (uint)(alphamask[0]); uint bits = unchecked ((uint)((alphaMask[0]) | (alphaMask[1] << 8) | (alphaMask[2] << 16))); for (int ly = 0; ly < 2; ly++) { for (int lx = 0; lx < 4; lx++, bits >>= 3) { // only put pixels out < width or height if ((x + lx) < width && (y - ly) < height && (y - ly) >= 0) { byte alphaValue = unchecked ((byte)alphas[bits & 0x07]); // mirror Y long offset = z * sizeOfPlane + (y - ly) * bps + (x + lx) * bpp + 3; destination.Position = position + offset; destination.WriteByte(alphaValue); } } } // Last three bytes //bits = (uint)(alphamask[3]); bits = unchecked ((uint)((alphaMask[3]) | (alphaMask[4] << 8) | (alphaMask[5] << 16))); for (int ly = 2; ly < 4; ly++) { for (int lx = 0; lx < 4; lx++, bits >>= 3) { // only put pixels out < width or height if ((x + lx) < width && (y - ly) < height && (y - ly) >= 0) { byte alphaValue = unchecked ((byte)alphas[bits & 0x07]); // mirror Y long offset = z * sizeOfPlane + (y - ly) * bps + (x + lx) * bpp + 3; destination.Position = position + offset; destination.WriteByte(alphaValue); } } } } } } }
public static void DecompressRGBA(BinaryReader sourceReader, Stream destination, DDSConvertParameters @params) { DecompressRGBA(sourceReader, destination, @params, true); }
private static bool IsRG16(DDSConvertParameters @params) { return(@params.RBitMask == 0xFF && @params.GBitMask == 0xFF00 && @params.BBitMask == 0 && @params.ABitMask == 0); }
private static bool IsAlpha8(DDSConvertParameters @params) { return(@params.RBitMask == 0 && @params.GBitMask == 0 && @params.BBitMask == 0 && @params.ABitMask == 0xFF); }
private static void DecompressRGBA(BinaryReader sourceReader, Stream destination, DDSConvertParameters @params, bool isAlpha) { int depth = @params.BitMapDepth; int width = @params.Width; int height = @params.Height; int bpp = GetRGBABytesPerPixel(@params); int bpc = GetBytesPerColor(@params); int bps = width * bpp * bpc; long sizeOfPlane = bps * height; int pixelSize = (@params.RGBBitCount + 7) / 8; ColorMask rMask = ComputeMaskParams(@params.RBitMask); ColorMask gMask = ComputeMaskParams(@params.GBitMask); ColorMask bMask = ComputeMaskParams(@params.BBitMask); ColorMask aMask = ComputeMaskParams(@params.ABitMask); long position = destination.Position; for (int z = 0; z < depth; z++) { // mirror Y for (int j = height - 1; j >= 0; j--) { long offset = z * sizeOfPlane + j * bps; destination.Position = position + offset; for (int i = 0; i < width; i++) { uint pixel = ReadPixel(sourceReader, pixelSize); uint pixelColor = pixel & @params.RBitMask; byte red = unchecked ((byte)(((pixelColor >> rMask.Shift1) * rMask.Mult) >> rMask.Shift2)); pixelColor = pixel & @params.GBitMask; byte green = unchecked ((byte)(((pixelColor >> gMask.Shift1) * gMask.Mult) >> gMask.Shift2)); pixelColor = pixel & @params.BBitMask; byte blue = unchecked ((byte)(((pixelColor >> bMask.Shift1) * bMask.Mult) >> bMask.Shift2)); byte alpha = 0xFF; if (isAlpha) { pixelColor = pixel & @params.ABitMask; alpha = unchecked ((byte)(((pixelColor >> aMask.Shift1) * aMask.Mult) >> aMask.Shift2)); } destination.WriteByte(blue); destination.WriteByte(green); destination.WriteByte(red); destination.WriteByte(alpha); } } } }
public static void DecompressDXT1(BinaryReader sourceReader, Stream destination, DDSConvertParameters @params) { int depth = @params.BitMapDepth; int width = @params.Width; int height = @params.Height; int bpp = GetRGBABytesPerPixel(@params); int bpc = GetBytesPerColor(@params); int bps = width * bpp * bpc; long sizeOfPlane = bps * height; Color8888[] colors = new Color8888[4]; long position = destination.Position; for (int z = 0; z < depth; z++) { // mirror Y for (int y = height - 1; y >= 0; y -= 4) { for (int x = 0; x < width; x += 4) { ushort color0 = sourceReader.ReadUInt16(); ushort color1 = sourceReader.ReadUInt16(); uint bitMask0 = sourceReader.ReadUInt16(); uint bitMask1 = sourceReader.ReadUInt16(); uint bitMask = (bitMask0 << 0) | (bitMask1 << 16); colors[0] = DxtcRead3bColor(color0); colors[1] = DxtcRead3bColor(color1); if (color0 > color1) { // Four-color block: derive the other two colors. // 00 = color_0, 01 = color_1, 10 = color_2, 11 = color_3 // These 2-bit codes correspond to the 2-bit fields // stored in the 64-bit block. colors[2].Blue = unchecked ((byte)((2 * colors[0].Blue + colors[1].Blue + 1) / 3)); colors[2].Green = unchecked ((byte)((2 * colors[0].Green + colors[1].Green + 1) / 3)); colors[2].Red = unchecked ((byte)((2 * colors[0].Red + colors[1].Red + 1) / 3)); colors[2].Alpha = 0xFF; colors[3].Alpha = 0xFF; } else { // Three-color block: derive the other color. // 00 = color_0, 01 = color_1, 10 = color_2, // 11 = transparent. // These 2-bit codes correspond to the 2-bit fields // stored in the 64-bit block. colors[2].Blue = unchecked ((byte)((colors[0].Blue + colors[1].Blue) / 2)); colors[2].Green = unchecked ((byte)((colors[0].Green + colors[1].Green) / 2)); colors[2].Red = unchecked ((byte)((colors[0].Red + colors[1].Red) / 2)); colors[2].Alpha = 0xFF; colors[3].Alpha = 0x0; } colors[3].Blue = unchecked ((byte)((colors[0].Blue + 2 * colors[1].Blue + 1) / 3)); colors[3].Green = unchecked ((byte)((colors[0].Green + 2 * colors[1].Green + 1) / 3)); colors[3].Red = unchecked ((byte)((colors[0].Red + 2 * colors[1].Red + 1) / 3)); int bitIndex = 0; for (int ly = 0; ly < 4; ly++) { for (int lx = 0; lx < 4; lx++, bitIndex++) { int colorIndex = unchecked ((int)((bitMask & (3 << bitIndex * 2)) >> bitIndex * 2)); Color8888 color = colors[colorIndex]; if ((x + lx) < width && (y - ly) < height && (y - ly) >= 0) { // mirror Y long offset = z * sizeOfPlane + (y - ly) * bps + (x + lx) * bpp; destination.Position = position + offset; destination.WriteByte(color.Red); destination.WriteByte(color.Green); destination.WriteByte(color.Blue); destination.WriteByte(color.Alpha); } } } } } } }