Last active
November 18, 2023 16:39
-
-
Save soeminnminn/e9c4c99867743a717f5b to your computer and use it in GitHub Desktop.
Read DDS image in C# way. Converted from "[il_dds.c] https://github.com/DentonW/DevIL/blob/master/DevIL/src-IL/src/il_dds.c" (DevIL).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} |
Всё правильно декодируется, разбирайтесь с тем что на входе.
там скрин прикреплён с 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?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
"uint bps = header.width * header.pixelformat.rgbbitcount / 8;"
"bps" will equal to 0 if the DDS file doesn't contain RGBbitcount.