-
-
Save soeminnminn/e9c4c99867743a717f5b to your computer and use it in GitHub Desktop.
| using System; | |
| using System.Collections.Generic; | |
| using System.Text; | |
| using System.Drawing.Imaging; | |
| using System.Runtime.InteropServices; | |
| using System.IO; | |
| namespace S16.Drawing | |
| { | |
| #region DDSImage Class | |
| public class DDSImage : IDisposable | |
| { | |
| #region Variables | |
| private bool m_isValid = false; | |
| private System.Drawing.Bitmap m_bitmap = null; | |
| #endregion | |
| #region Constructor/Destructor | |
| public DDSImage(byte[] ddsImage) | |
| { | |
| if (ddsImage == null) return; | |
| if (ddsImage.Length == 0) return; | |
| using (MemoryStream stream = new MemoryStream(ddsImage.Length)) | |
| { | |
| stream.Write(ddsImage, 0, ddsImage.Length); | |
| stream.Seek(0, SeekOrigin.Begin); | |
| using (BinaryReader reader = new BinaryReader(stream)) | |
| { | |
| this.Parse(reader); | |
| } | |
| } | |
| } | |
| public DDSImage(Stream ddsImage) | |
| { | |
| if (ddsImage == null) return; | |
| if (!ddsImage.CanRead) return; | |
| using (BinaryReader reader = new BinaryReader(ddsImage)) | |
| { | |
| this.Parse(reader); | |
| } | |
| } | |
| private DDSImage(System.Drawing.Bitmap bitmap) | |
| { | |
| this.m_bitmap = bitmap; | |
| } | |
| #endregion | |
| #region Override Methods | |
| #endregion | |
| #region Private Methods | |
| private void Parse(BinaryReader reader) | |
| { | |
| DDSStruct header = new DDSStruct(); | |
| PixelFormat pixelFormat = PixelFormat.UNKNOWN; | |
| byte[] data = null; | |
| if (this.ReadHeader(reader, ref header)) | |
| { | |
| this.m_isValid = true; | |
| // patches for stuff | |
| if (header.depth == 0) header.depth = 1; | |
| uint blocksize = 0; | |
| pixelFormat = this.GetFormat(header, ref blocksize); | |
| if (pixelFormat == PixelFormat.UNKNOWN) | |
| { | |
| throw new InvalidFileHeaderException(); | |
| } | |
| data = this.ReadData(reader, header); | |
| if (data != null) | |
| { | |
| byte[] rawData = this.DecompressData(header, data, pixelFormat); | |
| this.m_bitmap = this.CreateBitmap((int)header.width, (int)header.height, rawData); | |
| } | |
| } | |
| } | |
| private byte[] ReadData(BinaryReader reader, DDSStruct header) | |
| { | |
| byte[] compdata = null; | |
| uint compsize = 0; | |
| if ((header.flags & DDSD_LINEARSIZE) > 1) | |
| { | |
| compdata = reader.ReadBytes((int)header.sizeorpitch); | |
| compsize = (uint)compdata.Length; | |
| } | |
| else | |
| { | |
| uint bps = header.width * header.pixelformat.rgbbitcount / 8; | |
| compsize = bps * header.height * header.depth; | |
| compdata = new byte[compsize]; | |
| MemoryStream mem = new MemoryStream((int)compsize); | |
| byte[] temp; | |
| for (int z = 0; z < header.depth; z++) | |
| { | |
| for (int y = 0; y < header.height; y++) | |
| { | |
| temp = reader.ReadBytes((int)bps); | |
| mem.Write(temp, 0, temp.Length); | |
| } | |
| } | |
| mem.Seek(0, SeekOrigin.Begin); | |
| mem.Read(compdata, 0, compdata.Length); | |
| mem.Close(); | |
| } | |
| return compdata; | |
| } | |
| private System.Drawing.Bitmap CreateBitmap(int width, int height, byte[] rawData) | |
| { | |
| System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb); | |
| BitmapData data = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height) | |
| , ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); | |
| IntPtr scan = data.Scan0; | |
| int size = bitmap.Width * bitmap.Height * 4; | |
| unsafe | |
| { | |
| byte* p = (byte*)scan; | |
| for (int i = 0; i < size; i += 4) | |
| { | |
| // iterate through bytes. | |
| // Bitmap stores it's data in RGBA order. | |
| // DDS stores it's data in BGRA order. | |
| p[i] = rawData[i + 2]; // blue | |
| p[i + 1] = rawData[i + 1]; // green | |
| p[i + 2] = rawData[i]; // red | |
| p[i + 3] = rawData[i + 3]; // alpha | |
| } | |
| } | |
| bitmap.UnlockBits(data); | |
| return bitmap; | |
| } | |
| private bool ReadHeader(BinaryReader reader, ref DDSStruct header) | |
| { | |
| byte[] signature = reader.ReadBytes(4); | |
| if (!(signature[0] == 'D' && signature[1] == 'D' && signature[2] == 'S' && signature[3] == ' ')) | |
| return false; | |
| header.size = reader.ReadUInt32(); | |
| if (header.size != 124) | |
| return false; | |
| //convert the data | |
| header.flags = reader.ReadUInt32(); | |
| header.height = reader.ReadUInt32(); | |
| header.width = reader.ReadUInt32(); | |
| header.sizeorpitch = reader.ReadUInt32(); | |
| header.depth = reader.ReadUInt32(); | |
| header.mipmapcount = reader.ReadUInt32(); | |
| header.alphabitdepth = reader.ReadUInt32(); | |
| header.reserved = new uint[10]; | |
| for (int i = 0; i < 10; i++) | |
| { | |
| header.reserved[i] = reader.ReadUInt32(); | |
| } | |
| //pixelfromat | |
| header.pixelformat.size = reader.ReadUInt32(); | |
| header.pixelformat.flags = reader.ReadUInt32(); | |
| header.pixelformat.fourcc = reader.ReadUInt32(); | |
| header.pixelformat.rgbbitcount = reader.ReadUInt32(); | |
| header.pixelformat.rbitmask = reader.ReadUInt32(); | |
| header.pixelformat.gbitmask = reader.ReadUInt32(); | |
| header.pixelformat.bbitmask = reader.ReadUInt32(); | |
| header.pixelformat.alphabitmask = reader.ReadUInt32(); | |
| //caps | |
| header.ddscaps.caps1 = reader.ReadUInt32(); | |
| header.ddscaps.caps2 = reader.ReadUInt32(); | |
| header.ddscaps.caps3 = reader.ReadUInt32(); | |
| header.ddscaps.caps4 = reader.ReadUInt32(); | |
| header.texturestage = reader.ReadUInt32(); | |
| return true; | |
| } | |
| private PixelFormat GetFormat(DDSStruct header, ref uint blocksize) | |
| { | |
| PixelFormat format = PixelFormat.UNKNOWN; | |
| if ((header.pixelformat.flags & DDPF_FOURCC) == DDPF_FOURCC) | |
| { | |
| blocksize = ((header.width + 3) / 4) * ((header.height + 3) / 4) * header.depth; | |
| switch (header.pixelformat.fourcc) | |
| { | |
| case FOURCC_DXT1: | |
| format = PixelFormat.DXT1; | |
| blocksize *= 8; | |
| break; | |
| case FOURCC_DXT2: | |
| format = PixelFormat.DXT2; | |
| blocksize *= 16; | |
| break; | |
| case FOURCC_DXT3: | |
| format = PixelFormat.DXT3; | |
| blocksize *= 16; | |
| break; | |
| case FOURCC_DXT4: | |
| format = PixelFormat.DXT4; | |
| blocksize *= 16; | |
| break; | |
| case FOURCC_DXT5: | |
| format = PixelFormat.DXT5; | |
| blocksize *= 16; | |
| break; | |
| case FOURCC_ATI1: | |
| format = PixelFormat.ATI1N; | |
| blocksize *= 8; | |
| break; | |
| case FOURCC_ATI2: | |
| format = PixelFormat.THREEDC; | |
| blocksize *= 16; | |
| break; | |
| case FOURCC_RXGB: | |
| format = PixelFormat.RXGB; | |
| blocksize *= 16; | |
| break; | |
| case FOURCC_DOLLARNULL: | |
| format = PixelFormat.A16B16G16R16; | |
| blocksize = header.width * header.height * header.depth * 8; | |
| break; | |
| case FOURCC_oNULL: | |
| format = PixelFormat.R16F; | |
| blocksize = header.width * header.height * header.depth * 2; | |
| break; | |
| case FOURCC_pNULL: | |
| format = PixelFormat.G16R16F; | |
| blocksize = header.width * header.height * header.depth * 4; | |
| break; | |
| case FOURCC_qNULL: | |
| format = PixelFormat.A16B16G16R16F; | |
| blocksize = header.width * header.height * header.depth * 8; | |
| break; | |
| case FOURCC_rNULL: | |
| format = PixelFormat.R32F; | |
| blocksize = header.width * header.height * header.depth * 4; | |
| break; | |
| case FOURCC_sNULL: | |
| format = PixelFormat.G32R32F; | |
| blocksize = header.width * header.height * header.depth * 8; | |
| break; | |
| case FOURCC_tNULL: | |
| format = PixelFormat.A32B32G32R32F; | |
| blocksize = header.width * header.height * header.depth * 16; | |
| break; | |
| default: | |
| format = PixelFormat.UNKNOWN; | |
| blocksize *= 16; | |
| break; | |
| } // switch | |
| } | |
| else | |
| { | |
| // uncompressed image | |
| if ((header.pixelformat.flags & DDPF_LUMINANCE) == DDPF_LUMINANCE) | |
| { | |
| if ((header.pixelformat.flags & DDPF_ALPHAPIXELS) == DDPF_ALPHAPIXELS) | |
| { | |
| format = PixelFormat.LUMINANCE_ALPHA; | |
| } | |
| else | |
| { | |
| format = PixelFormat.LUMINANCE; | |
| } | |
| } | |
| else | |
| { | |
| if ((header.pixelformat.flags & DDPF_ALPHAPIXELS) == DDPF_ALPHAPIXELS) | |
| { | |
| format = PixelFormat.RGBA; | |
| } | |
| else | |
| { | |
| format = PixelFormat.RGB; | |
| } | |
| } | |
| blocksize = (header.width * header.height * header.depth * (header.pixelformat.rgbbitcount >> 3)); | |
| } | |
| return format; | |
| } | |
| #region Helper Methods | |
| // iCompFormatToBpp | |
| private uint PixelFormatToBpp(PixelFormat pf, uint rgbbitcount) | |
| { | |
| switch (pf) | |
| { | |
| case PixelFormat.LUMINANCE: | |
| case PixelFormat.LUMINANCE_ALPHA: | |
| case PixelFormat.RGBA: | |
| case PixelFormat.RGB: | |
| return rgbbitcount / 8; | |
| case PixelFormat.THREEDC: | |
| case PixelFormat.RXGB: | |
| return 3; | |
| case PixelFormat.ATI1N: | |
| return 1; | |
| case PixelFormat.R16F: | |
| return 2; | |
| case PixelFormat.A16B16G16R16: | |
| case PixelFormat.A16B16G16R16F: | |
| case PixelFormat.G32R32F: | |
| return 8; | |
| case PixelFormat.A32B32G32R32F: | |
| return 16; | |
| default: | |
| return 4; | |
| } | |
| } | |
| // iCompFormatToBpc | |
| private uint PixelFormatToBpc(PixelFormat pf) | |
| { | |
| switch (pf) | |
| { | |
| case PixelFormat.R16F: | |
| case PixelFormat.G16R16F: | |
| case PixelFormat.A16B16G16R16F: | |
| return 4; | |
| case PixelFormat.R32F: | |
| case PixelFormat.G32R32F: | |
| case PixelFormat.A32B32G32R32F: | |
| return 4; | |
| case PixelFormat.A16B16G16R16: | |
| return 2; | |
| default: | |
| return 1; | |
| } | |
| } | |
| private bool Check16BitComponents(DDSStruct header) | |
| { | |
| if (header.pixelformat.rgbbitcount != 32) | |
| return false; | |
| // a2b10g10r10 format | |
| if (header.pixelformat.rbitmask == 0x3FF00000 && header.pixelformat.gbitmask == 0x000FFC00 && header.pixelformat.bbitmask == 0x000003FF | |
| && header.pixelformat.alphabitmask == 0xC0000000) | |
| return true; | |
| // a2r10g10b10 format | |
| else if (header.pixelformat.rbitmask == 0x000003FF && header.pixelformat.gbitmask == 0x000FFC00 && header.pixelformat.bbitmask == 0x3FF00000 | |
| && header.pixelformat.alphabitmask == 0xC0000000) | |
| return true; | |
| return false; | |
| } | |
| private void CorrectPremult(uint pixnum, ref byte[] buffer) | |
| { | |
| for (uint i = 0; i < pixnum; i++) | |
| { | |
| byte alpha = buffer[i + 3]; | |
| if (alpha == 0) continue; | |
| int red = (buffer[i] << 8) / alpha; | |
| int green = (buffer[i + 1] << 8) / alpha; | |
| int blue = (buffer[i + 2] << 8) / alpha; | |
| buffer[i] = (byte)red; | |
| buffer[i + 1] = (byte)green; | |
| buffer[i + 2] = (byte)blue; | |
| } | |
| } | |
| private void ComputeMaskParams(uint mask, ref int shift1, ref int mul, ref int shift2) | |
| { | |
| shift1 = 0; mul = 1; shift2 = 0; | |
| while ((mask & 1) == 0) | |
| { | |
| mask >>= 1; | |
| shift1++; | |
| } | |
| uint bc = 0; | |
| while ((mask & (1 << (int)bc)) != 0) bc++; | |
| while ((mask * mul) < 255) | |
| mul = (mul << (int)bc) + 1; | |
| mask *= (uint)mul; | |
| while ((mask & ~0xff) != 0) | |
| { | |
| mask >>= 1; | |
| shift2++; | |
| } | |
| } | |
| private unsafe void DxtcReadColors(byte* data, ref Colour8888[] op) | |
| { | |
| byte r0, g0, b0, r1, g1, b1; | |
| b0 = (byte)(data[0] & 0x1F); | |
| g0 = (byte)(((data[0] & 0xE0) >> 5) | ((data[1] & 0x7) << 3)); | |
| r0 = (byte)((data[1] & 0xF8) >> 3); | |
| b1 = (byte)(data[2] & 0x1F); | |
| g1 = (byte)(((data[2] & 0xE0) >> 5) | ((data[3] & 0x7) << 3)); | |
| r1 = (byte)((data[3] & 0xF8) >> 3); | |
| op[0].red = (byte)(r0 << 3 | r0 >> 2); | |
| op[0].green = (byte)(g0 << 2 | g0 >> 3); | |
| op[0].blue = (byte)(b0 << 3 | b0 >> 2); | |
| op[1].red = (byte)(r1 << 3 | r1 >> 2); | |
| op[1].green = (byte)(g1 << 2 | g1 >> 3); | |
| op[1].blue = (byte)(b1 << 3 | b1 >> 2); | |
| } | |
| private void DxtcReadColor(ushort data, ref Colour8888 op) | |
| { | |
| byte r, g, b; | |
| b = (byte)(data & 0x1f); | |
| g = (byte)((data & 0x7E0) >> 5); | |
| r = (byte)((data & 0xF800) >> 11); | |
| op.red = (byte)(r << 3 | r >> 2); | |
| op.green = (byte)(g << 2 | g >> 3); | |
| op.blue = (byte)(b << 3 | r >> 2); | |
| } | |
| private unsafe void DxtcReadColors(byte* data, ref Colour565 color_0, ref Colour565 color_1) | |
| { | |
| color_0.blue = (byte)(data[0] & 0x1F); | |
| color_0.green = (byte)(((data[0] & 0xE0) >> 5) | ((data[1] & 0x7) << 3)); | |
| color_0.red = (byte)((data[1] & 0xF8) >> 3); | |
| color_0.blue = (byte)(data[2] & 0x1F); | |
| color_0.green = (byte)(((data[2] & 0xE0) >> 5) | ((data[3] & 0x7) << 3)); | |
| color_0.red = (byte)((data[3] & 0xF8) >> 3); | |
| } | |
| private void GetBitsFromMask(uint mask, ref uint shiftLeft, ref uint shiftRight) | |
| { | |
| uint temp, i; | |
| if (mask == 0) | |
| { | |
| shiftLeft = shiftRight = 0; | |
| return; | |
| } | |
| temp = mask; | |
| for (i = 0; i < 32; i++, temp >>= 1) | |
| { | |
| if ((temp & 1) != 0) | |
| break; | |
| } | |
| shiftRight = i; | |
| // Temp is preserved, so use it again: | |
| for (i = 0; i < 8; i++, temp >>= 1) | |
| { | |
| if ((temp & 1) == 0) | |
| break; | |
| } | |
| shiftLeft = 8 - i; | |
| } | |
| // This function simply counts how many contiguous bits are in the mask. | |
| private uint CountBitsFromMask(uint mask) | |
| { | |
| uint i, testBit = 0x01, count = 0; | |
| bool foundBit = false; | |
| for (i = 0; i < 32; i++, testBit <<= 1) | |
| { | |
| if ((mask & testBit) != 0) | |
| { | |
| if (!foundBit) | |
| foundBit = true; | |
| count++; | |
| } | |
| else if (foundBit) | |
| return count; | |
| } | |
| return count; | |
| } | |
| private uint HalfToFloat(ushort y) | |
| { | |
| int s = (y >> 15) & 0x00000001; | |
| int e = (y >> 10) & 0x0000001f; | |
| int m = y & 0x000003ff; | |
| if (e == 0) | |
| { | |
| if (m == 0) | |
| { | |
| // | |
| // Plus or minus zero | |
| // | |
| return (uint)(s << 31); | |
| } | |
| else | |
| { | |
| // | |
| // Denormalized number -- renormalize it | |
| // | |
| while ((m & 0x00000400) == 0) | |
| { | |
| m <<= 1; | |
| e -= 1; | |
| } | |
| e += 1; | |
| m &= ~0x00000400; | |
| } | |
| } | |
| else if (e == 31) | |
| { | |
| if (m == 0) | |
| { | |
| // | |
| // Positive or negative infinity | |
| // | |
| return (uint)((s << 31) | 0x7f800000); | |
| } | |
| else | |
| { | |
| // | |
| // Nan -- preserve sign and significand bits | |
| // | |
| return (uint)((s << 31) | 0x7f800000 | (m << 13)); | |
| } | |
| } | |
| // | |
| // Normalized number | |
| // | |
| e = e + (127 - 15); | |
| m = m << 13; | |
| // | |
| // Assemble s, e and m. | |
| // | |
| return (uint)((s << 31) | (e << 23) | m); | |
| } | |
| private unsafe void ConvFloat16ToFloat32(uint* dest, ushort* src, uint size) | |
| { | |
| uint i; | |
| for (i = 0; i < size; ++i, ++dest, ++src) | |
| { | |
| //float: 1 sign bit, 8 exponent bits, 23 mantissa bits | |
| //half: 1 sign bit, 5 exponent bits, 10 mantissa bits | |
| *dest = HalfToFloat(*src); | |
| } | |
| } | |
| private unsafe void ConvG16R16ToFloat32(uint* dest, ushort* src, uint size) | |
| { | |
| uint i; | |
| for (i = 0; i < size; i += 3) | |
| { | |
| //float: 1 sign bit, 8 exponent bits, 23 mantissa bits | |
| //half: 1 sign bit, 5 exponent bits, 10 mantissa bits | |
| *dest++ = HalfToFloat(*src++); | |
| *dest++ = HalfToFloat(*src++); | |
| *((float*)dest++) = 1.0f; | |
| } | |
| } | |
| private unsafe void ConvR16ToFloat32(uint* dest, ushort* src, uint size) | |
| { | |
| uint i; | |
| for (i = 0; i < size; i += 3) | |
| { | |
| //float: 1 sign bit, 8 exponent bits, 23 mantissa bits | |
| //half: 1 sign bit, 5 exponent bits, 10 mantissa bits | |
| *dest++ = HalfToFloat(*src++); | |
| *((float*)dest++) = 1.0f; | |
| *((float*)dest++) = 1.0f; | |
| } | |
| } | |
| #endregion | |
| #region Decompress Methods | |
| private byte[] DecompressData(DDSStruct header, byte[] data, PixelFormat pixelFormat) | |
| { | |
| System.Diagnostics.Debug.WriteLine(pixelFormat); | |
| // allocate bitmap | |
| byte[] rawData = null; | |
| switch (pixelFormat) | |
| { | |
| case PixelFormat.RGBA: | |
| rawData = this.DecompressRGBA(header, data, pixelFormat); | |
| break; | |
| case PixelFormat.RGB: | |
| rawData = this.DecompressRGB(header, data, pixelFormat); | |
| break; | |
| case PixelFormat.LUMINANCE: | |
| case PixelFormat.LUMINANCE_ALPHA: | |
| rawData = this.DecompressLum(header, data, pixelFormat); | |
| break; | |
| case PixelFormat.DXT1: | |
| rawData = this.DecompressDXT1(header, data, pixelFormat); | |
| break; | |
| case PixelFormat.DXT2: | |
| rawData = this.DecompressDXT2(header, data, pixelFormat); | |
| break; | |
| case PixelFormat.DXT3: | |
| rawData = this.DecompressDXT3(header, data, pixelFormat); | |
| break; | |
| case PixelFormat.DXT4: | |
| rawData = this.DecompressDXT4(header, data, pixelFormat); | |
| break; | |
| case PixelFormat.DXT5: | |
| rawData = this.DecompressDXT5(header, data, pixelFormat); | |
| break; | |
| case PixelFormat.THREEDC: | |
| rawData = this.Decompress3Dc(header, data, pixelFormat); | |
| break; | |
| case PixelFormat.ATI1N: | |
| rawData = this.DecompressAti1n(header, data, pixelFormat); | |
| break; | |
| case PixelFormat.RXGB: | |
| rawData = this.DecompressRXGB(header, data, pixelFormat); | |
| break; | |
| case PixelFormat.R16F: | |
| case PixelFormat.G16R16F: | |
| case PixelFormat.A16B16G16R16F: | |
| case PixelFormat.R32F: | |
| case PixelFormat.G32R32F: | |
| case PixelFormat.A32B32G32R32F: | |
| rawData = this.DecompressFloat(header, data, pixelFormat); | |
| break; | |
| default: | |
| throw new UnknownFileFormatException(); | |
| } | |
| return rawData; | |
| } | |
| private unsafe byte[] DecompressDXT1(DDSStruct header, byte[] data, PixelFormat pixelFormat) | |
| { | |
| // allocate bitmap | |
| int bpp = (int)(PixelFormatToBpp(pixelFormat, header.pixelformat.rgbbitcount)); | |
| int bps = (int)(header.width * bpp * PixelFormatToBpc(pixelFormat)); | |
| int sizeofplane = (int)(bps * header.height); | |
| int width = (int)header.width; | |
| int height = (int)header.height; | |
| int depth = (int)header.depth; | |
| // DXT1 decompressor | |
| byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp]; | |
| Colour8888[] colours = new Colour8888[4]; | |
| colours[0].alpha = 0xFF; | |
| colours[1].alpha = 0xFF; | |
| colours[2].alpha = 0xFF; | |
| fixed (byte* bytePtr = data) | |
| { | |
| byte* temp = bytePtr; | |
| for (int z = 0; z < depth; z++) | |
| { | |
| for (int y = 0; y < height; y += 4) | |
| { | |
| for (int x = 0; x < width; x += 4) | |
| { | |
| ushort colour0 = *((ushort*)temp); | |
| ushort colour1 = *((ushort*)(temp + 2)); | |
| DxtcReadColor(colour0, ref colours[0]); | |
| DxtcReadColor(colour1, ref colours[1]); | |
| uint bitmask = ((uint*)temp)[1]; | |
| temp += 8; | |
| if (colour0 > colour1) | |
| { | |
| // 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. | |
| colours[2].blue = (byte)((2 * colours[0].blue + colours[1].blue + 1) / 3); | |
| colours[2].green = (byte)((2 * colours[0].green + colours[1].green + 1) / 3); | |
| colours[2].red = (byte)((2 * colours[0].red + colours[1].red + 1) / 3); | |
| //colours[2].alpha = 0xFF; | |
| colours[3].blue = (byte)((colours[0].blue + 2 * colours[1].blue + 1) / 3); | |
| colours[3].green = (byte)((colours[0].green + 2 * colours[1].green + 1) / 3); | |
| colours[3].red = (byte)((colours[0].red + 2 * colours[1].red + 1) / 3); | |
| colours[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. | |
| colours[2].blue = (byte)((colours[0].blue + colours[1].blue) / 2); | |
| colours[2].green = (byte)((colours[0].green + colours[1].green) / 2); | |
| colours[2].red = (byte)((colours[0].red + colours[1].red) / 2); | |
| //colours[2].alpha = 0xFF; | |
| colours[3].blue = (byte)((colours[0].blue + 2 * colours[1].blue + 1) / 3); | |
| colours[3].green = (byte)((colours[0].green + 2 * colours[1].green + 1) / 3); | |
| colours[3].red = (byte)((colours[0].red + 2 * colours[1].red + 1) / 3); | |
| colours[3].alpha = 0x00; | |
| } | |
| for (int j = 0, k = 0; j < 4; j++) | |
| { | |
| for (int i = 0; i < 4; i++, k++) | |
| { | |
| int select = (int)((bitmask & (0x03 << k * 2)) >> k * 2); | |
| Colour8888 col = colours[select]; | |
| if (((x + i) < width) && ((y + j) < height)) | |
| { | |
| uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp); | |
| rawData[offset + 0] = (byte)col.red; | |
| rawData[offset + 1] = (byte)col.green; | |
| rawData[offset + 2] = (byte)col.blue; | |
| rawData[offset + 3] = (byte)col.alpha; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return rawData; | |
| } | |
| private byte[] DecompressDXT2(DDSStruct header, byte[] data, PixelFormat pixelFormat) | |
| { | |
| // allocate bitmap | |
| int width = (int)header.width; | |
| int height = (int)header.height; | |
| int depth = (int)header.depth; | |
| // Can do color & alpha same as dxt3, but color is pre-multiplied | |
| // so the result will be wrong unless corrected. | |
| byte[] rawData = DecompressDXT3(header, data, pixelFormat); | |
| CorrectPremult((uint)(width * height * depth), ref rawData); | |
| return rawData; | |
| } | |
| private unsafe byte[] DecompressDXT3(DDSStruct header, byte[] data, PixelFormat pixelFormat) | |
| { | |
| // allocate bitmap | |
| int bpp = (int)(PixelFormatToBpp(pixelFormat, header.pixelformat.rgbbitcount)); | |
| int bps = (int)(header.width * bpp * PixelFormatToBpc(pixelFormat)); | |
| int sizeofplane = (int)(bps * header.height); | |
| int width = (int)header.width; | |
| int height = (int)header.height; | |
| int depth = (int)header.depth; | |
| // DXT3 decompressor | |
| byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp]; | |
| Colour8888[] colours = new Colour8888[4]; | |
| fixed (byte* bytePtr = data) | |
| { | |
| byte* temp = bytePtr; | |
| for (int z = 0; z < depth; z++) | |
| { | |
| for (int y = 0; y < height; y += 4) | |
| { | |
| for (int x = 0; x < width; x += 4) | |
| { | |
| byte* alpha = temp; | |
| temp += 8; | |
| DxtcReadColors(temp, ref colours); | |
| temp += 4; | |
| uint bitmask = ((uint*)temp)[1]; | |
| temp += 4; | |
| // 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. | |
| colours[2].blue = (byte)((2 * colours[0].blue + colours[1].blue + 1) / 3); | |
| colours[2].green = (byte)((2 * colours[0].green + colours[1].green + 1) / 3); | |
| colours[2].red = (byte)((2 * colours[0].red + colours[1].red + 1) / 3); | |
| //colours[2].alpha = 0xFF; | |
| colours[3].blue = (byte)((colours[0].blue + 2 * colours[1].blue + 1) / 3); | |
| colours[3].green = (byte)((colours[0].green + 2 * colours[1].green + 1) / 3); | |
| colours[3].red = (byte)((colours[0].red + 2 * colours[1].red + 1) / 3); | |
| //colours[3].alpha = 0xFF; | |
| for (int j = 0, k = 0; j < 4; j++) | |
| { | |
| for (int i = 0; i < 4; k++, i++) | |
| { | |
| int select = (int)((bitmask & (0x03 << k * 2)) >> k * 2); | |
| if (((x + i) < width) && ((y + j) < height)) | |
| { | |
| uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp); | |
| rawData[offset + 0] = (byte)colours[select].red; | |
| rawData[offset + 1] = (byte)colours[select].green; | |
| rawData[offset + 2] = (byte)colours[select].blue; | |
| } | |
| } | |
| } | |
| for (int j = 0; j < 4; j++) | |
| { | |
| //ushort word = (ushort)(alpha[2 * j] + 256 * alpha[2 * j + 1]); | |
| ushort word = (ushort)(alpha[2 * j] | (alpha[2 * j + 1] << 8)); | |
| for (int i = 0; i < 4; i++) | |
| { | |
| if (((x + i) < width) && ((y + j) < height)) | |
| { | |
| uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp + 3); | |
| rawData[offset] = (byte)(word & 0x0F); | |
| rawData[offset] = (byte)(rawData[offset] | (rawData[offset] << 4)); | |
| } | |
| word >>= 4; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return rawData; | |
| } | |
| private byte[] DecompressDXT4(DDSStruct header, byte[] data, PixelFormat pixelFormat) | |
| { | |
| // allocate bitmap | |
| int width = (int)header.width; | |
| int height = (int)header.height; | |
| int depth = (int)header.depth; | |
| // Can do color & alpha same as dxt5, but color is pre-multiplied | |
| // so the result will be wrong unless corrected. | |
| byte[] rawData = DecompressDXT5(header, data, pixelFormat); | |
| CorrectPremult((uint)(width * height * depth), ref rawData); | |
| return rawData; | |
| } | |
| private unsafe byte[] DecompressDXT5(DDSStruct header, byte[] data, PixelFormat pixelFormat) | |
| { | |
| // allocate bitmap | |
| int bpp = (int)(PixelFormatToBpp(pixelFormat, header.pixelformat.rgbbitcount)); | |
| int bps = (int)(header.width * bpp * PixelFormatToBpc(pixelFormat)); | |
| int sizeofplane = (int)(bps * header.height); | |
| int width = (int)header.width; | |
| int height = (int)header.height; | |
| int depth = (int)header.depth; | |
| byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp]; | |
| Colour8888[] colours = new Colour8888[4]; | |
| ushort[] alphas = new ushort[8]; | |
| fixed (byte* bytePtr = data) | |
| { | |
| byte* temp = bytePtr; | |
| for (int z = 0; z < depth; z++) | |
| { | |
| for (int y = 0; y < height; y += 4) | |
| { | |
| for (int x = 0; x < width; x += 4) | |
| { | |
| if (y >= height || x >= width) | |
| break; | |
| alphas[0] = temp[0]; | |
| alphas[1] = temp[1]; | |
| byte* alphamask = (temp + 2); | |
| temp += 8; | |
| DxtcReadColors(temp, ref colours); | |
| uint bitmask = ((uint*)temp)[1]; | |
| temp += 8; | |
| // 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. | |
| colours[2].blue = (byte)((2 * colours[0].blue + colours[1].blue + 1) / 3); | |
| colours[2].green = (byte)((2 * colours[0].green + colours[1].green + 1) / 3); | |
| colours[2].red = (byte)((2 * colours[0].red + colours[1].red + 1) / 3); | |
| //colours[2].alpha = 0xFF; | |
| colours[3].blue = (byte)((colours[0].blue + 2 * colours[1].blue + 1) / 3); | |
| colours[3].green = (byte)((colours[0].green + 2 * colours[1].green + 1) / 3); | |
| colours[3].red = (byte)((colours[0].red + 2 * colours[1].red + 1) / 3); | |
| //colours[3].alpha = 0xFF; | |
| int k = 0; | |
| for (int j = 0; j < 4; j++) | |
| { | |
| for (int i = 0; i < 4; k++, i++) | |
| { | |
| int select = (int)((bitmask & (0x03 << k * 2)) >> k * 2); | |
| Colour8888 col = colours[select]; | |
| // only put pixels out < width or height | |
| if (((x + i) < width) && ((y + j) < height)) | |
| { | |
| uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp); | |
| rawData[offset] = (byte)col.red; | |
| rawData[offset + 1] = (byte)col.green; | |
| rawData[offset + 2] = (byte)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] = (ushort)((6 * alphas[0] + 1 * alphas[1] + 3) / 7); // bit code 010 | |
| alphas[3] = (ushort)((5 * alphas[0] + 2 * alphas[1] + 3) / 7); // bit code 011 | |
| alphas[4] = (ushort)((4 * alphas[0] + 3 * alphas[1] + 3) / 7); // bit code 100 | |
| alphas[5] = (ushort)((3 * alphas[0] + 4 * alphas[1] + 3) / 7); // bit code 101 | |
| alphas[6] = (ushort)((2 * alphas[0] + 5 * alphas[1] + 3) / 7); // bit code 110 | |
| alphas[7] = (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] = (ushort)((4 * alphas[0] + 1 * alphas[1] + 2) / 5); // Bit code 010 | |
| alphas[3] = (ushort)((3 * alphas[0] + 2 * alphas[1] + 2) / 5); // Bit code 011 | |
| alphas[4] = (ushort)((2 * alphas[0] + 3 * alphas[1] + 2) / 5); // Bit code 100 | |
| alphas[5] = (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 = (uint)((alphamask[0]) | (alphamask[1] << 8) | (alphamask[2] << 16)); | |
| for (int j = 0; j < 2; j++) | |
| { | |
| for (int i = 0; i < 4; i++) | |
| { | |
| // only put pixels out < width or height | |
| if (((x + i) < width) && ((y + j) < height)) | |
| { | |
| uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp + 3); | |
| rawData[offset] = (byte)alphas[bits & 0x07]; | |
| } | |
| bits >>= 3; | |
| } | |
| } | |
| // Last three bytes | |
| //bits = (uint)(alphamask[3]); | |
| bits = (uint)((alphamask[3]) | (alphamask[4] << 8) | (alphamask[5] << 16)); | |
| for (int j = 2; j < 4; j++) | |
| { | |
| for (int i = 0; i < 4; i++) | |
| { | |
| // only put pixels out < width or height | |
| if (((x + i) < width) && ((y + j) < height)) | |
| { | |
| uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp + 3); | |
| rawData[offset] = (byte)alphas[bits & 0x07]; | |
| } | |
| bits >>= 3; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return rawData; | |
| } | |
| private unsafe byte[] DecompressRGB(DDSStruct header, byte[] data, PixelFormat pixelFormat) | |
| { | |
| // allocate bitmap | |
| int bpp = (int)(this.PixelFormatToBpp(pixelFormat, header.pixelformat.rgbbitcount)); | |
| int bps = (int)(header.width * bpp * this.PixelFormatToBpc(pixelFormat)); | |
| int sizeofplane = (int)(bps * header.height); | |
| int width = (int)header.width; | |
| int height = (int)header.height; | |
| int depth = (int)header.depth; | |
| byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp]; | |
| uint valMask = (uint)((header.pixelformat.rgbbitcount == 32) ? ~0 : (1 << (int)header.pixelformat.rgbbitcount) - 1); | |
| uint pixSize = (uint)(((int)header.pixelformat.rgbbitcount + 7) / 8); | |
| int rShift1 = 0; int rMul = 0; int rShift2 = 0; | |
| ComputeMaskParams(header.pixelformat.rbitmask, ref rShift1, ref rMul, ref rShift2); | |
| int gShift1 = 0; int gMul = 0; int gShift2 = 0; | |
| ComputeMaskParams(header.pixelformat.gbitmask, ref gShift1, ref gMul, ref gShift2); | |
| int bShift1 = 0; int bMul = 0; int bShift2= 0; | |
| ComputeMaskParams(header.pixelformat.bbitmask, ref bShift1, ref bMul, ref bShift2); | |
| int offset = 0; | |
| int pixnum = width * height * depth; | |
| fixed (byte* bytePtr = data) | |
| { | |
| byte* temp = bytePtr; | |
| while (pixnum-- > 0) | |
| { | |
| uint px = *((uint*)temp) & valMask; | |
| temp += pixSize; | |
| uint pxc = px & header.pixelformat.rbitmask; | |
| rawData[offset + 0] = (byte)(((pxc >> rShift1) * rMul) >> rShift2); | |
| pxc = px & header.pixelformat.gbitmask; | |
| rawData[offset + 1] = (byte)(((pxc >> gShift1) * gMul) >> gShift2); | |
| pxc = px & header.pixelformat.bbitmask; | |
| rawData[offset + 2] = (byte)(((pxc >> bShift1) * bMul) >> bShift2); | |
| rawData[offset + 3] = 0xff; | |
| offset += 4; | |
| } | |
| } | |
| return rawData; | |
| } | |
| private unsafe byte[] DecompressRGBA(DDSStruct header, byte[] data, PixelFormat pixelFormat) | |
| { | |
| // allocate bitmap | |
| int bpp = (int)(this.PixelFormatToBpp(pixelFormat, header.pixelformat.rgbbitcount)); | |
| int bps = (int)(header.width * bpp * this.PixelFormatToBpc(pixelFormat)); | |
| int sizeofplane = (int)(bps * header.height); | |
| int width = (int)header.width; | |
| int height = (int)header.height; | |
| int depth = (int)header.depth; | |
| byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp]; | |
| uint valMask = (uint)((header.pixelformat.rgbbitcount == 32) ? ~0 : (1 << (int)header.pixelformat.rgbbitcount) - 1); | |
| // Funny x86s, make 1 << 32 == 1 | |
| uint pixSize = (header.pixelformat.rgbbitcount + 7) / 8; | |
| int rShift1 = 0; int rMul = 0; int rShift2 = 0; | |
| ComputeMaskParams(header.pixelformat.rbitmask, ref rShift1, ref rMul, ref rShift2); | |
| int gShift1 = 0; int gMul = 0; int gShift2 = 0; | |
| ComputeMaskParams(header.pixelformat.gbitmask, ref gShift1, ref gMul, ref gShift2); | |
| int bShift1 = 0; int bMul = 0; int bShift2 = 0; | |
| ComputeMaskParams(header.pixelformat.bbitmask, ref bShift1, ref bMul, ref bShift2); | |
| int aShift1 = 0; int aMul = 0; int aShift2 = 0; | |
| ComputeMaskParams(header.pixelformat.alphabitmask, ref aShift1, ref aMul, ref aShift2); | |
| int offset = 0; | |
| int pixnum = width * height * depth; | |
| fixed (byte* bytePtr = data) | |
| { | |
| byte* temp = bytePtr; | |
| while (pixnum-- > 0) | |
| { | |
| uint px = *((uint*)temp) & valMask; | |
| temp += pixSize; | |
| uint pxc = px & header.pixelformat.rbitmask; | |
| rawData[offset + 0] = (byte)(((pxc >> rShift1) * rMul) >> rShift2); | |
| pxc = px & header.pixelformat.gbitmask; | |
| rawData[offset + 1] = (byte)(((pxc >> gShift1) * gMul) >> gShift2); | |
| pxc = px & header.pixelformat.bbitmask; | |
| rawData[offset + 2] = (byte)(((pxc >> bShift1) * bMul) >> bShift2); | |
| pxc = px & header.pixelformat.alphabitmask; | |
| rawData[offset + 3] = (byte)(((pxc >> aShift1) * aMul) >> aShift2); | |
| offset += 4; | |
| } | |
| } | |
| return rawData; | |
| } | |
| private unsafe byte[] Decompress3Dc(DDSStruct header, byte[] data, PixelFormat pixelFormat) | |
| { | |
| // allocate bitmap | |
| int bpp = (int)(this.PixelFormatToBpp(pixelFormat, header.pixelformat.rgbbitcount)); | |
| int bps = (int)(header.width * bpp * this.PixelFormatToBpc(pixelFormat)); | |
| int sizeofplane = (int)(bps * header.height); | |
| int width = (int)header.width; | |
| int height = (int)header.height; | |
| int depth = (int)header.depth; | |
| byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp]; | |
| byte[] yColours = new byte[8]; | |
| byte[] xColours = new byte[8]; | |
| int offset = 0; | |
| fixed (byte* bytePtr = data) | |
| { | |
| byte* temp = bytePtr; | |
| for (int z = 0; z < depth; z++) | |
| { | |
| for (int y = 0; y < height; y += 4) | |
| { | |
| for (int x = 0; x < width; x += 4) | |
| { | |
| byte* temp2 = temp + 8; | |
| //Read Y palette | |
| int t1 = yColours[0] = temp[0]; | |
| int t2 = yColours[1] = temp[1]; | |
| temp += 2; | |
| if (t1 > t2) | |
| for (int i = 2; i < 8; ++i) | |
| yColours[i] = (byte)(t1 + ((t2 - t1) * (i - 1)) / 7); | |
| else | |
| { | |
| for (int i = 2; i < 6; ++i) | |
| yColours[i] = (byte)(t1 + ((t2 - t1) * (i - 1)) / 5); | |
| yColours[6] = 0; | |
| yColours[7] = 255; | |
| } | |
| // Read X palette | |
| t1 = xColours[0] = temp2[0]; | |
| t2 = xColours[1] = temp2[1]; | |
| temp2 += 2; | |
| if (t1 > t2) | |
| for (int i = 2; i < 8; ++i) | |
| xColours[i] = (byte)(t1 + ((t2 - t1) * (i - 1)) / 7); | |
| else | |
| { | |
| for (int i = 2; i < 6; ++i) | |
| xColours[i] = (byte)(t1 + ((t2 - t1) * (i - 1)) / 5); | |
| xColours[6] = 0; | |
| xColours[7] = 255; | |
| } | |
| //decompress pixel data | |
| int currentOffset = offset; | |
| for (int k = 0; k < 4; k += 2) | |
| { | |
| // First three bytes | |
| uint bitmask = ((uint)(temp[0]) << 0) | ((uint)(temp[1]) << 8) | ((uint)(temp[2]) << 16); | |
| uint bitmask2 = ((uint)(temp2[0]) << 0) | ((uint)(temp2[1]) << 8) | ((uint)(temp2[2]) << 16); | |
| for (int j = 0; j < 2; j++) | |
| { | |
| // only put pixels out < height | |
| if ((y + k + j) < height) | |
| { | |
| for (int i = 0; i < 4; i++) | |
| { | |
| // only put pixels out < width | |
| if (((x + i) < width)) | |
| { | |
| int t; | |
| byte tx, ty; | |
| t1 = currentOffset + (x + i) * 3; | |
| rawData[t1 + 1] = ty = yColours[bitmask & 0x07]; | |
| rawData[t1 + 0] = tx = xColours[bitmask2 & 0x07]; | |
| //calculate b (z) component ((r/255)^2 + (g/255)^2 + (b/255)^2 = 1 | |
| t = 127 * 128 - (tx - 127) * (tx - 128) - (ty - 127) * (ty - 128); | |
| if (t > 0) | |
| rawData[t1 + 2] = (byte)(Math.Sqrt(t) + 128); | |
| else | |
| rawData[t1 + 2] = 0x7F; | |
| } | |
| bitmask >>= 3; | |
| bitmask2 >>= 3; | |
| } | |
| currentOffset += bps; | |
| } | |
| } | |
| temp += 3; | |
| temp2 += 3; | |
| } | |
| //skip bytes that were read via Temp2 | |
| temp += 8; | |
| } | |
| offset += bps * 4; | |
| } | |
| } | |
| } | |
| return rawData; | |
| } | |
| private unsafe byte[] DecompressAti1n(DDSStruct header, byte[] data, PixelFormat pixelFormat) | |
| { | |
| // allocate bitmap | |
| int bpp = (int)(this.PixelFormatToBpp(pixelFormat, header.pixelformat.rgbbitcount)); | |
| int bps = (int)(header.width * bpp * this.PixelFormatToBpc(pixelFormat)); | |
| int sizeofplane = (int)(bps * header.height); | |
| int width = (int)header.width; | |
| int height = (int)header.height; | |
| int depth = (int)header.depth; | |
| byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp]; | |
| byte[] colours = new byte[8]; | |
| uint offset = 0; | |
| fixed (byte* bytePtr = data) | |
| { | |
| byte* temp = bytePtr; | |
| for (int z = 0; z < depth; z++) | |
| { | |
| for (int y = 0; y < height; y += 4) | |
| { | |
| for (int x = 0; x < width; x += 4) | |
| { | |
| //Read palette | |
| int t1 = colours[0] = temp[0]; | |
| int t2 = colours[1] = temp[1]; | |
| temp += 2; | |
| if (t1 > t2) | |
| for (int i = 2; i < 8; ++i) | |
| colours[i] = (byte)(t1 + ((t2 - t1) * (i - 1)) / 7); | |
| else | |
| { | |
| for (int i = 2; i < 6; ++i) | |
| colours[i] = (byte)(t1 + ((t2 - t1) * (i - 1)) / 5); | |
| colours[6] = 0; | |
| colours[7] = 255; | |
| } | |
| //decompress pixel data | |
| uint currOffset = offset; | |
| for (int k = 0; k < 4; k += 2) | |
| { | |
| // First three bytes | |
| uint bitmask = ((uint)(temp[0]) << 0) | ((uint)(temp[1]) << 8) | ((uint)(temp[2]) << 16); | |
| for (int j = 0; j < 2; j++) | |
| { | |
| // only put pixels out < height | |
| if ((y + k + j) < height) | |
| { | |
| for (int i = 0; i < 4; i++) | |
| { | |
| // only put pixels out < width | |
| if (((x + i) < width)) | |
| { | |
| t1 = (int)(currOffset + (x + i)); | |
| rawData[t1] = colours[bitmask & 0x07]; | |
| } | |
| bitmask >>= 3; | |
| } | |
| currOffset += (uint)bps; | |
| } | |
| } | |
| temp += 3; | |
| } | |
| } | |
| offset += (uint)(bps * 4); | |
| } | |
| } | |
| } | |
| return rawData; | |
| } | |
| private unsafe byte[] DecompressLum(DDSStruct header, byte[] data, PixelFormat pixelFormat) | |
| { | |
| // allocate bitmap | |
| int bpp = (int)(this.PixelFormatToBpp(pixelFormat, header.pixelformat.rgbbitcount)); | |
| int bps = (int)(header.width * bpp * this.PixelFormatToBpc(pixelFormat)); | |
| int sizeofplane = (int)(bps * header.height); | |
| int width = (int)header.width; | |
| int height = (int)header.height; | |
| int depth = (int)header.depth; | |
| byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp]; | |
| int lShift1 = 0; int lMul = 0; int lShift2 = 0; | |
| ComputeMaskParams(header.pixelformat.rbitmask, ref lShift1, ref lMul, ref lShift2); | |
| int offset = 0; | |
| int pixnum = width * height * depth; | |
| fixed (byte* bytePtr = data) | |
| { | |
| byte* temp = bytePtr; | |
| while (pixnum-- > 0) | |
| { | |
| byte px = *(temp++); | |
| rawData[offset + 0] = (byte)(((px >> lShift1) * lMul) >> lShift2); | |
| rawData[offset + 1] = (byte)(((px >> lShift1) * lMul) >> lShift2); | |
| rawData[offset + 2] = (byte)(((px >> lShift1) * lMul) >> lShift2); | |
| rawData[offset + 3] = (byte)(((px >> lShift1) * lMul) >> lShift2); | |
| offset += 4; | |
| } | |
| } | |
| return rawData; | |
| } | |
| private unsafe byte[] DecompressRXGB(DDSStruct header, byte[] data, PixelFormat pixelFormat) | |
| { | |
| // allocate bitmap | |
| int bpp = (int)(this.PixelFormatToBpp(pixelFormat, header.pixelformat.rgbbitcount)); | |
| int bps = (int)(header.width * bpp * this.PixelFormatToBpc(pixelFormat)); | |
| int sizeofplane = (int)(bps * header.height); | |
| int width = (int)header.width; | |
| int height = (int)header.height; | |
| int depth = (int)header.depth; | |
| byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp]; | |
| Colour565 color_0 = new Colour565(); | |
| Colour565 color_1 = new Colour565(); | |
| Colour8888[] colours = new Colour8888[4]; | |
| byte[] alphas = new byte[8]; | |
| fixed (byte* bytePtr = data) | |
| { | |
| byte* temp = bytePtr; | |
| for (int z = 0; z < depth; z++) | |
| { | |
| for (int y = 0; y < height; y += 4) | |
| { | |
| for (int x = 0; x < width; x += 4) | |
| { | |
| if (y >= height || x >= width) | |
| break; | |
| alphas[0] = temp[0]; | |
| alphas[1] = temp[1]; | |
| byte* alphamask = temp + 2; | |
| temp += 8; | |
| DxtcReadColors(temp, ref color_0, ref color_1); | |
| temp += 4; | |
| uint bitmask = ((uint*)temp)[1]; | |
| temp += 4; | |
| colours[0].red = (byte)(color_0.red << 3); | |
| colours[0].green = (byte)(color_0.green << 2); | |
| colours[0].blue = (byte)(color_0.blue << 3); | |
| colours[0].alpha = 0xFF; | |
| colours[1].red = (byte)(color_1.red << 3); | |
| colours[1].green = (byte)(color_1.green << 2); | |
| colours[1].blue = (byte)(color_1.blue << 3); | |
| colours[1].alpha = 0xFF; | |
| // 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. | |
| colours[2].blue = (byte)((2 * colours[0].blue + colours[1].blue + 1) / 3); | |
| colours[2].green = (byte)((2 * colours[0].green + colours[1].green + 1) / 3); | |
| colours[2].red = (byte)((2 * colours[0].red + colours[1].red + 1) / 3); | |
| colours[2].alpha = 0xFF; | |
| colours[3].blue = (byte)((colours[0].blue + 2 * colours[1].blue + 1) / 3); | |
| colours[3].green = (byte)((colours[0].green + 2 * colours[1].green + 1) / 3); | |
| colours[3].red = (byte)((colours[0].red + 2 * colours[1].red + 1) / 3); | |
| colours[3].alpha = 0xFF; | |
| int k = 0; | |
| for (int j = 0; j < 4; j++) | |
| { | |
| for (int i = 0; i < 4; i++, k++) | |
| { | |
| int select = (int)((bitmask & (0x03 << k * 2)) >> k * 2); | |
| Colour8888 col = colours[select]; | |
| // only put pixels out < width or height | |
| if (((x + i) < width) && ((y + j) < height)) | |
| { | |
| uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp); | |
| rawData[offset + 0] = col.red; | |
| rawData[offset + 1] = col.green; | |
| rawData[offset + 2] = 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] = (byte)((6 * alphas[0] + 1 * alphas[1] + 3) / 7); // bit code 010 | |
| alphas[3] = (byte)((5 * alphas[0] + 2 * alphas[1] + 3) / 7); // bit code 011 | |
| alphas[4] = (byte)((4 * alphas[0] + 3 * alphas[1] + 3) / 7); // bit code 100 | |
| alphas[5] = (byte)((3 * alphas[0] + 4 * alphas[1] + 3) / 7); // bit code 101 | |
| alphas[6] = (byte)((2 * alphas[0] + 5 * alphas[1] + 3) / 7); // bit code 110 | |
| alphas[7] = (byte)((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] = (byte)((4 * alphas[0] + 1 * alphas[1] + 2) / 5); // Bit code 010 | |
| alphas[3] = (byte)((3 * alphas[0] + 2 * alphas[1] + 2) / 5); // Bit code 011 | |
| alphas[4] = (byte)((2 * alphas[0] + 3 * alphas[1] + 2) / 5); // Bit code 100 | |
| alphas[5] = (byte)((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); | |
| for (int j = 0; j < 2; j++) | |
| { | |
| for (int i = 0; i < 4; i++) | |
| { | |
| // only put pixels out < width or height | |
| if (((x + i) < width) && ((y + j) < height)) | |
| { | |
| uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp + 3); | |
| rawData[offset] = alphas[bits & 0x07]; | |
| } | |
| bits >>= 3; | |
| } | |
| } | |
| // Last three bytes | |
| bits = *((uint*)&alphamask[3]); | |
| for (int j = 2; j < 4; j++) | |
| { | |
| for (int i = 0; i < 4; i++) | |
| { | |
| // only put pixels out < width or height | |
| if (((x + i) < width) && ((y + j) < height)) | |
| { | |
| uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp + 3); | |
| rawData[offset] = alphas[bits & 0x07]; | |
| } | |
| bits >>= 3; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return rawData; | |
| } | |
| private unsafe byte[] DecompressFloat(DDSStruct header, byte[] data, PixelFormat pixelFormat) | |
| { | |
| // allocate bitmap | |
| int bpp = (int)(this.PixelFormatToBpp(pixelFormat, header.pixelformat.rgbbitcount)); | |
| int bps = (int)(header.width * bpp * this.PixelFormatToBpc(pixelFormat)); | |
| int sizeofplane = (int)(bps * header.height); | |
| int width = (int)header.width; | |
| int height = (int)header.height; | |
| int depth = (int)header.depth; | |
| byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp]; | |
| int size = 0; | |
| fixed (byte* bytePtr = data) | |
| { | |
| byte* temp = bytePtr; | |
| fixed (byte* destPtr = rawData) | |
| { | |
| byte* destData = destPtr; | |
| switch (pixelFormat) | |
| { | |
| case PixelFormat.R32F: // Red float, green = blue = max | |
| size = width * height * depth * 3; | |
| for (int i = 0, j = 0; i < size; i += 3, j++) | |
| { | |
| ((float*)destData)[i] = ((float*)temp)[j]; | |
| ((float*)destData)[i + 1] = 1.0f; | |
| ((float*)destData)[i + 2] = 1.0f; | |
| } | |
| break; | |
| case PixelFormat.A32B32G32R32F: // Direct copy of float RGBA data | |
| Array.Copy(data, rawData, data.Length); | |
| break; | |
| case PixelFormat.G32R32F: // Red float, green float, blue = max | |
| size = width * height * depth * 3; | |
| for (int i = 0, j = 0; i < size; i += 3, j += 2) | |
| { | |
| ((float*)destData)[i] = ((float*)temp)[j]; | |
| ((float*)destData)[i + 1] = ((float*)temp)[j + 1]; | |
| ((float*)destData)[i + 2] = 1.0f; | |
| } | |
| break; | |
| case PixelFormat.R16F: // Red float, green = blue = max | |
| size = width * height * depth * bpp; | |
| ConvR16ToFloat32((uint*)destData, (ushort*)temp, (uint)size); | |
| break; | |
| case PixelFormat.A16B16G16R16F: // Just convert from half to float. | |
| size = width * height * depth * bpp; | |
| ConvFloat16ToFloat32((uint*)destData, (ushort*)temp, (uint)size); | |
| break; | |
| case PixelFormat.G16R16F: // Convert from half to float, set blue = max. | |
| size = width * height * depth * bpp; | |
| ConvG16R16ToFloat32((uint*)destData, (ushort*)temp, (uint)size); | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| } | |
| return rawData; | |
| } | |
| #region UNUSED | |
| private unsafe byte[] DecompressARGB(DDSStruct header, byte[] data, PixelFormat pixelFormat) | |
| { | |
| // allocate bitmap | |
| int bpp = (int)(PixelFormatToBpp(pixelFormat, header.pixelformat.rgbbitcount)); | |
| int bps = (int)(header.width * bpp * PixelFormatToBpc(pixelFormat)); | |
| int sizeofplane = (int)(bps * header.height); | |
| int width = (int)header.width; | |
| int height = (int)header.height; | |
| int depth = (int)header.depth; | |
| if (Check16BitComponents(header)) | |
| return DecompressARGB16(header, data, pixelFormat); | |
| int sizeOfData = (int)((header.width * header.pixelformat.rgbbitcount / 8) * header.height * header.depth); | |
| byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp]; | |
| if ((pixelFormat == PixelFormat.LUMINANCE) && (header.pixelformat.rgbbitcount == 16) && (header.pixelformat.rbitmask == 0xFFFF)) | |
| { | |
| Array.Copy(data, rawData, data.Length); | |
| return rawData; | |
| } | |
| uint readI = 0, tempBpp; | |
| uint redL = 0, redR = 0; | |
| uint greenL = 0, greenR = 0; | |
| uint blueL = 0, blueR = 0; | |
| uint alphaL = 0, alphaR = 0; | |
| GetBitsFromMask(header.pixelformat.rbitmask, ref redL, ref redR); | |
| GetBitsFromMask(header.pixelformat.gbitmask, ref greenL, ref greenR); | |
| GetBitsFromMask(header.pixelformat.bbitmask, ref blueL, ref blueR); | |
| GetBitsFromMask(header.pixelformat.alphabitmask, ref alphaL, ref alphaR); | |
| tempBpp = header.pixelformat.rgbbitcount / 8; | |
| fixed (byte* bytePtr = data) | |
| { | |
| byte* temp = bytePtr; | |
| for (int i = 0; i < sizeOfData; i += bpp) | |
| { | |
| //@TODO: This is SLOOOW... | |
| //but the old version crashed in release build under | |
| //winxp (and xp is right to stop this code - I always | |
| //wondered that it worked the old way at all) | |
| if (sizeOfData - i < 4) | |
| { | |
| //less than 4 byte to write? | |
| if (tempBpp == 3) | |
| { | |
| //this branch is extra-SLOOOW | |
| readI = (uint)(*temp | ((*(temp + 1)) << 8) | ((*(temp + 2)) << 16)); | |
| } | |
| else if (tempBpp == 1) | |
| readI = *((byte*)temp); | |
| else if (tempBpp == 2) | |
| readI = (uint)(temp[0] | (temp[1] << 8)); | |
| } | |
| else | |
| readI = (uint)(temp[0] | (temp[1] << 8) | (temp[2] << 16) | (temp[3] << 24)); | |
| temp += tempBpp; | |
| rawData[i] = (byte)((((int)readI & (int)header.pixelformat.rbitmask) >> (int)redR) << (int)redL); | |
| if (bpp >= 3) | |
| { | |
| rawData[i + 1] = (byte)((((int)readI & (int)header.pixelformat.gbitmask) >> (int)greenR) << (int)greenL); | |
| rawData[i + 2] = (byte)((((int)readI & header.pixelformat.bbitmask) >> (int)blueR) << (int)blueL); | |
| if (bpp == 4) | |
| { | |
| rawData[i + 3] = (byte)((((int)readI & (int)header.pixelformat.alphabitmask) >> (int)alphaR) << (int)alphaL); | |
| if (alphaL >= 7) | |
| { | |
| rawData[i + 3] = (byte)(rawData[i + 3] != 0 ? 0xFF : 0x00); | |
| } | |
| else if (alphaL >= 4) | |
| { | |
| rawData[i + 3] = (byte)(rawData[i + 3] | (rawData[i + 3] >> 4)); | |
| } | |
| } | |
| } | |
| else if (bpp == 2) | |
| { | |
| rawData[i + 1] = (byte)((((int)readI & (int)header.pixelformat.alphabitmask) >> (int)alphaR) << (int)alphaL); | |
| if (alphaL >= 7) | |
| { | |
| rawData[i + 1] = (byte)(rawData[i + 1] != 0 ? 0xFF : 0x00); | |
| } | |
| else if (alphaL >= 4) | |
| { | |
| rawData[i + 1] = (byte)(rawData[i + 1] | (rawData[i + 3] >> 4)); | |
| } | |
| } | |
| } | |
| } | |
| return rawData; | |
| } | |
| private unsafe byte[] DecompressARGB16(DDSStruct header, byte[] data, PixelFormat pixelFormat) | |
| { | |
| // allocate bitmap | |
| int bpp = (int)(PixelFormatToBpp(pixelFormat, header.pixelformat.rgbbitcount)); | |
| int bps = (int)(header.width * bpp * PixelFormatToBpc(pixelFormat)); | |
| int sizeofplane = (int)(bps * header.height); | |
| int width = (int)header.width; | |
| int height = (int)header.height; | |
| int depth = (int)header.depth; | |
| int sizeOfData = (int)((header.width * header.pixelformat.rgbbitcount / 8) * header.height * header.depth); | |
| byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp]; | |
| uint readI = 0, tempBpp = 0; | |
| uint redL = 0, redR = 0; | |
| uint greenL = 0, greenR = 0; | |
| uint blueL = 0, blueR = 0; | |
| uint alphaL = 0, alphaR = 0; | |
| uint redPad = 0, greenPad = 0, bluePad = 0, alphaPad = 0; | |
| GetBitsFromMask(header.pixelformat.rbitmask, ref redL, ref redR); | |
| GetBitsFromMask(header.pixelformat.gbitmask, ref greenL, ref greenR); | |
| GetBitsFromMask(header.pixelformat.bbitmask, ref blueL, ref blueR); | |
| GetBitsFromMask(header.pixelformat.alphabitmask, ref alphaL, ref alphaR); | |
| redPad = 16 - CountBitsFromMask(header.pixelformat.rbitmask); | |
| greenPad = 16 - CountBitsFromMask(header.pixelformat.gbitmask); | |
| bluePad = 16 - CountBitsFromMask(header.pixelformat.bbitmask); | |
| alphaPad = 16 - CountBitsFromMask(header.pixelformat.alphabitmask); | |
| redL = redL + redPad; | |
| greenL = greenL + greenPad; | |
| blueL = blueL + bluePad; | |
| alphaL = alphaL + alphaPad; | |
| tempBpp = header.pixelformat.rgbbitcount / 8; | |
| fixed (byte* bytePtr = data) | |
| { | |
| byte* temp = bytePtr; | |
| fixed (byte* destPtr = rawData) | |
| { | |
| byte* destData = destPtr; | |
| for (int i = 0; i < sizeOfData / 2; i += bpp) | |
| { | |
| //@TODO: This is SLOOOW... | |
| //but the old version crashed in release build under | |
| //winxp (and xp is right to stop this code - I always | |
| //wondered that it worked the old way at all) | |
| if (sizeOfData - i < 4) | |
| { | |
| //less than 4 byte to write? | |
| if (tempBpp == 3) | |
| { | |
| //this branch is extra-SLOOOW | |
| readI = (uint)(*temp | ((*(temp + 1)) << 8) | ((*(temp + 2)) << 16)); | |
| } | |
| else if (tempBpp == 1) | |
| readI = *((byte*)temp); | |
| else if (tempBpp == 2) | |
| readI = (uint)(temp[0] | (temp[1] << 8)); | |
| } | |
| else | |
| readI = (uint)(temp[0] | (temp[1] << 8) | (temp[2] << 16) | (temp[3] << 24)); | |
| temp += tempBpp; | |
| ((ushort*)destData)[i + 2] = (ushort)((((int)readI & (int)header.pixelformat.rbitmask) >> (int)redR) << (int)redL); | |
| if (bpp >= 3) | |
| { | |
| ((ushort*)destData)[i + 1] = (ushort)((((int)readI & (int)header.pixelformat.gbitmask) >> (int)greenR) << (int)greenL); | |
| ((ushort*)destData)[i] = (ushort)((((int)readI & (int)header.pixelformat.bbitmask) >> (int)blueR) << (int)blueL); | |
| if (bpp == 4) | |
| { | |
| ((ushort*)destData)[i + 3] = (ushort)((((int)readI & (int)header.pixelformat.alphabitmask) >> (int)alphaR) << (int)alphaL); | |
| if (alphaL >= 7) | |
| { | |
| ((ushort*)destData)[i + 3] = (ushort)(((ushort*)destData)[i + 3] != 0 ? 0xFF : 0x00); | |
| } | |
| else if (alphaL >= 4) | |
| { | |
| ((ushort*)destData)[i + 3] = (ushort)(((ushort*)destData)[i + 3] | (((ushort*)destData)[i + 3] >> 4)); | |
| } | |
| } | |
| } | |
| else if (bpp == 2) | |
| { | |
| ((ushort*)destData)[i + 1] = (ushort)((((int)readI & (int)header.pixelformat.alphabitmask) >> (int)alphaR) << (int)alphaL); | |
| if (alphaL >= 7) | |
| { | |
| ((ushort*)destData)[i + 1] = (ushort)(((ushort*)destData)[i + 1] != 0 ? 0xFF : 0x00); | |
| } | |
| else if (alphaL >= 4) | |
| { | |
| ((ushort*)destData)[i + 1] = (ushort)(((ushort*)destData)[i + 1] | (rawData[i + 3] >> 4)); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return rawData; | |
| } | |
| #endregion | |
| #endregion | |
| #endregion | |
| #region Public Methods | |
| public void Dispose() | |
| { | |
| if (this.m_bitmap != null) | |
| { | |
| this.m_bitmap.Dispose(); | |
| this.m_bitmap = null; | |
| } | |
| } | |
| #endregion | |
| #region Properties | |
| /// <summary> | |
| /// Returns a System.Imaging.Bitmap containing the DDS image. | |
| /// </summary> | |
| public System.Drawing.Bitmap BitmapImage | |
| { | |
| get { return this.m_bitmap; } | |
| } | |
| /// <summary> | |
| /// Returns the DDS image is valid format. | |
| /// </summary> | |
| public bool IsValid | |
| { | |
| get { return this.m_isValid; } | |
| } | |
| #endregion | |
| #region Operators | |
| public static implicit operator DDSImage(System.Drawing.Bitmap value) | |
| { | |
| return new DDSImage(value); | |
| } | |
| public static explicit operator System.Drawing.Bitmap(DDSImage value) | |
| { | |
| return value.BitmapImage; | |
| } | |
| #endregion | |
| #region Nested Types | |
| #region Colour8888 | |
| [StructLayout(LayoutKind.Sequential)] | |
| private struct Colour8888 | |
| { | |
| public byte red; | |
| public byte green; | |
| public byte blue; | |
| public byte alpha; | |
| } | |
| #endregion | |
| #region Colour565 | |
| [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] | |
| private struct Colour565 | |
| { | |
| public ushort blue; //: 5; | |
| public ushort green; //: 6; | |
| public ushort red; //: 5; | |
| } | |
| #endregion | |
| #region DDSStruct | |
| [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] | |
| private struct DDSStruct | |
| { | |
| public uint size; // equals size of struct (which is part of the data file!) | |
| public uint flags; | |
| public uint height; | |
| public uint width; | |
| public uint sizeorpitch; | |
| public uint depth; | |
| public uint mipmapcount; | |
| public uint alphabitdepth; | |
| //[MarshalAs(UnmanagedType.U4, SizeConst = 11)] | |
| public uint[] reserved;//[11]; | |
| [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] | |
| public struct pixelformatstruct | |
| { | |
| public uint size; // equals size of struct (which is part of the data file!) | |
| public uint flags; | |
| public uint fourcc; | |
| public uint rgbbitcount; | |
| public uint rbitmask; | |
| public uint gbitmask; | |
| public uint bbitmask; | |
| public uint alphabitmask; | |
| } | |
| public pixelformatstruct pixelformat; | |
| [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] | |
| public struct ddscapsstruct | |
| { | |
| public uint caps1; | |
| public uint caps2; | |
| public uint caps3; | |
| public uint caps4; | |
| } | |
| public ddscapsstruct ddscaps; | |
| public uint texturestage; | |
| //#ifndef __i386__ | |
| //void to_little_endian() | |
| //{ | |
| // size_t size = sizeof(DDSStruct); | |
| // assert(size % 4 == 0); | |
| // size /= 4; | |
| // for (size_t i=0; i<size; i++) | |
| // { | |
| // ((int32_t*) this)[i] = little_endian(((int32_t*) this)[i]); | |
| // } | |
| //} | |
| //#endif | |
| } | |
| #endregion | |
| #region DDSStruct Flags | |
| private const int DDSD_CAPS = 0x00000001; | |
| private const int DDSD_HEIGHT = 0x00000002; | |
| private const int DDSD_WIDTH = 0x00000004; | |
| private const int DDSD_PITCH = 0x00000008; | |
| private const int DDSD_PIXELFORMAT = 0x00001000; | |
| private const int DDSD_MIPMAPCOUNT = 0x00020000; | |
| private const int DDSD_LINEARSIZE = 0x00080000; | |
| private const int DDSD_DEPTH = 0x00800000; | |
| #endregion | |
| #region pixelformat values | |
| private const int DDPF_ALPHAPIXELS = 0x00000001; | |
| private const int DDPF_FOURCC = 0x00000004; | |
| private const int DDPF_RGB = 0x00000040; | |
| private const int DDPF_LUMINANCE = 0x00020000; | |
| #endregion | |
| #region ddscaps | |
| // caps1 | |
| private const int DDSCAPS_COMPLEX = 0x00000008; | |
| private const int DDSCAPS_TEXTURE = 0x00001000; | |
| private const int DDSCAPS_MIPMAP = 0x00400000; | |
| // caps2 | |
| private const int DDSCAPS2_CUBEMAP = 0x00000200; | |
| private const int DDSCAPS2_CUBEMAP_POSITIVEX = 0x00000400; | |
| private const int DDSCAPS2_CUBEMAP_NEGATIVEX = 0x00000800; | |
| private const int DDSCAPS2_CUBEMAP_POSITIVEY = 0x00001000; | |
| private const int DDSCAPS2_CUBEMAP_NEGATIVEY = 0x00002000; | |
| private const int DDSCAPS2_CUBEMAP_POSITIVEZ = 0x00004000; | |
| private const int DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x00008000; | |
| private const int DDSCAPS2_VOLUME = 0x00200000; | |
| #endregion | |
| #region fourccs | |
| private const uint FOURCC_DXT1 = 0x31545844; | |
| private const uint FOURCC_DXT2 = 0x32545844; | |
| private const uint FOURCC_DXT3 = 0x33545844; | |
| private const uint FOURCC_DXT4 = 0x34545844; | |
| private const uint FOURCC_DXT5 = 0x35545844; | |
| private const uint FOURCC_ATI1 = 0x31495441; | |
| private const uint FOURCC_ATI2 = 0x32495441; | |
| private const uint FOURCC_RXGB = 0x42475852; | |
| private const uint FOURCC_DOLLARNULL = 0x24; | |
| private const uint FOURCC_oNULL = 0x6f; | |
| private const uint FOURCC_pNULL = 0x70; | |
| private const uint FOURCC_qNULL = 0x71; | |
| private const uint FOURCC_rNULL = 0x72; | |
| private const uint FOURCC_sNULL = 0x73; | |
| private const uint FOURCC_tNULL = 0x74; | |
| #endregion | |
| #region PixelFormat | |
| /// <summary> | |
| /// Various pixel formats/compressors used by the DDS image. | |
| /// </summary> | |
| private enum PixelFormat | |
| { | |
| /// <summary> | |
| /// 32-bit image, with 8-bit red, green, blue and alpha. | |
| /// </summary> | |
| RGBA, | |
| /// <summary> | |
| /// 24-bit image with 8-bit red, green, blue. | |
| /// </summary> | |
| RGB, | |
| /// <summary> | |
| /// 16-bit DXT-1 compression, 1-bit alpha. | |
| /// </summary> | |
| DXT1, | |
| /// <summary> | |
| /// DXT-2 Compression | |
| /// </summary> | |
| DXT2, | |
| /// <summary> | |
| /// DXT-3 Compression | |
| /// </summary> | |
| DXT3, | |
| /// <summary> | |
| /// DXT-4 Compression | |
| /// </summary> | |
| DXT4, | |
| /// <summary> | |
| /// DXT-5 Compression | |
| /// </summary> | |
| DXT5, | |
| /// <summary> | |
| /// 3DC Compression | |
| /// </summary> | |
| THREEDC, | |
| /// <summary> | |
| /// ATI1n Compression | |
| /// </summary> | |
| ATI1N, | |
| LUMINANCE, | |
| LUMINANCE_ALPHA, | |
| RXGB, | |
| A16B16G16R16, | |
| R16F, | |
| G16R16F, | |
| A16B16G16R16F, | |
| R32F, | |
| G32R32F, | |
| A32B32G32R32F, | |
| /// <summary> | |
| /// Unknown pixel format. | |
| /// </summary> | |
| UNKNOWN | |
| } | |
| #endregion | |
| #endregion | |
| } | |
| #endregion | |
| #region Exceptions Class | |
| /// <summary> | |
| /// Thrown when an invalid file header has been encountered. | |
| /// </summary> | |
| public class InvalidFileHeaderException : Exception | |
| { | |
| } | |
| /// <summary> | |
| /// Thrown when the data does not contain a DDS image. | |
| /// </summary> | |
| public class NotADDSImageException : Exception | |
| { | |
| } | |
| /// <summary> | |
| /// Thrown when there is an unknown compressor used in the DDS file. | |
| /// </summary> | |
| public class UnknownFileFormatException : Exception | |
| { | |
| } | |
| #endregion | |
| } |
"uint bps = header.width * header.pixelformat.rgbbitcount / 8;"
"bps" will equal to 0 if the DDS file doesn't contain RGBbitcount.
Всё правильно декодируется, разбирайтесь с тем что на входе.
там скрин прикреплён с paint.net с того же файла. в паинте ок, в либе не ок. ну да, писать научили, а смотреть, видимо, не научили.
Нужно заменить в декомпрессоре DXT3 следующую строку:
private unsafe byte[] DecompressDXT3(DDSStruct header, byte[] data, PixelFormat pixelFormat)
{
// allocate bitmap
int bpp = (int)(PixelFormatToBpp(pixelFormat, header.pixelformat.rgbbitcount));
int bps = (int)(header.width * bpp * PixelFormatToBpc(pixelFormat));
int sizeofplane = (int)(bps * header.height);
int width = (int)header.width;
int height = (int)header.height;
int depth = (int)header.depth;
// DXT3 decompressor
byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp];
Colour8888[] colours = new Colour8888[4];
fixed (byte* bytePtr = data)
{
byte* temp = bytePtr;
for (int z = 0; z < depth; z++)
{
for (int y = 0; y < height; y += 4)
{
for (int x = 0; x < width; x += 4)
{
byte* alpha = temp;
temp += 8;
DxtcReadColors(temp, ref colours);
temp += 4;
**uint bitmask = ((uint*)temp)[1];// заменить 1 на 0**
temp += 4;
здесь неправильно бралась битовая маска. Проверил DXT1 и DXT5, они конвертятся нормально.
As other people have said (over TWO years ago "micenote commented on 15 Mar 2016") DXT5 decompression is broken, it throws an exception on this line
alphas[0] = temp[0];
I don't understand what needs changing to fix it, if I knew that, I probably wouldn't have needed to use this library, as I would have known how to write my own.
BC_7 support?
Грамотный нейм_спейс, и стрим есть, и из файла парсит, выручил настолько что из проекта удалил три сторонних dll. ОГРОМНОЕ СПАСИБО.
MemoryStream PCDStream = new MemoryStream(Data, 0, Data.Length);
var ResourceStream = ConvertPCD9ToDDSv22 (PCDStream,VersionDRM);
ResourceStream.Position = 0;
var DDSImage = new DDSImage (ResourceStream);
var Bitmap = DDSImage.BitmapImage;
previewPictureBox.Image = Bitmap;
И всё...)))
пыс.пыс
@Альфа-канал в DXT5 текстур декодируется неправильно.
Всё правильно декодируется, разбирайтесь с тем что на входе.