Mercurial > repos > IBBoard.Graphics
view DDSFile.cs @ 3:38851b01bdab default tip
Ignore build artefacts
author | IBBoard <dev@ibboard.co.uk> |
---|---|
date | Sun, 16 Oct 2016 19:56:57 +0100 |
parents | 1c89affbdd64 |
children |
line wrap: on
line source
// This file (DDSFile.cs) is a part of the IBBoard.Graphics library and is copyright 2009 IBBoard. // // The file and the library/program it is in are licensed under the GNU LGPL license, either version 3 of the License or (at your option) any later version. Please see COPYING.LGPL for more information and the full license. using System; using System.Drawing; using IBBoard.Graphics.OpenILPort; using IBBoard.Graphics.SquishWrapper; namespace IBBoard.Graphics { /// <summary> /// Summary description for DDSFile. /// </summary> public class DDSFile { private byte[] data; private Converter.DXTType type; private int width, height; private byte blockwidth; private byte alphawidth; public DDSFile(byte[] file) { data = file; height = data[12]+(data[13]<<8)+(data[14]<<16)+(data[15]<<24); width = data[16]+(data[17]<<8)+(data[18]<<16)+(data[19]<<24); switch(data[87]) { case 0x31: type = Converter.DXTType.DXT1; blockwidth=8; alphawidth = 0; break; case 0x33: type = Converter.DXTType.DXT3; blockwidth = 16; alphawidth = 8; break; case 0x35: type = Converter.DXTType.DXT5; blockwidth = 16; alphawidth = 8; break; default: throw new InvalidOperationException("Unsupported DDS type - only DXT1, DXT3 and DXT5 are currently supported"); } } private DDSFile(byte[] ddsData, int imgWidth, int imgHeight, Converter.DXTType compression) { data = new byte[128+ddsData.Length]; type = compression; width = imgWidth; height= imgHeight; switch(type) { case Converter.DXTType.DXT1: blockwidth=8; alphawidth = 0; Converter.DXT1_Header.CopyTo(data, 0); break; case Converter.DXTType.DXT3: blockwidth = 16; alphawidth = 8; Converter.DXT3_Header.CopyTo(data, 0); break; case Converter.DXTType.DXT5: blockwidth = 16; alphawidth = 8; Converter.DXT5_Header.CopyTo(data, 0); break; default: throw new InvalidOperationException("Unsupported DDS type - only DXT1, DXT3 and DXT5 are currently supported"); } data[12] = (byte)height; data[13] = (byte)(height >> 8); data[14] = (byte)(height >> 16); data[15] = (byte)(height >> 24); data[16] = (byte)width; data[17] = (byte)(width >> 8); data[18] = (byte)(width >> 16); data[19] = (byte)(width >> 24); int mips = CalcMips(width, height); data[28] = (byte)mips; data[29] = (byte)(mips >> 8); data[30] = (byte)(mips >> 16); data[31] = (byte)(mips >> 24); ddsData.CopyTo(data, 128); } public int Width { get{return width;} } public int Height { get{return height;} } public Converter.DXTType Type { get{ return type;} } private static int CalcMips(int width, int height) { return (int)Math.Ceiling(Math.Log(Math.Max(width, height))/Math.Log(2))+1; } private static int CalcDataSize(Converter.DXTType type, int width, int height, int mipmaps) { int val = 0; byte bytes = (byte)BlockSize(type); width = width/4; height = height/4; for (int i = 0; i<mipmaps; i++) { val+= width*height*bytes; width = (width>1)?width/2:1; height = (height>1)?height/2:1; } return val; } private static int BlockSize(Converter.DXTType type) { if (type == Converter.DXTType.DXT1) { return 8; } else if (type == Converter.DXTType.DXT5 || type == Converter.DXTType.DXT3) { return 16; } else { throw new InvalidOperationException("Unsupported DDS type - only DXT1, DXT3 and DXT5 are currently supported"); } } private ushort GetColour0(int pos) { int start = 128+(pos*blockwidth) + alphawidth; return (ushort)(data[start]+((ushort)data[start+1]<<8)); } private ushort GetColour1(int pos) { int start = 128+(pos*blockwidth) + alphawidth; return (ushort)(data[start+2]+((ushort)data[start+3]<<8)); } private byte GetPixelColour(int pos, byte pixel) { int start = 128+(pos*blockwidth) + alphawidth; return data[start+4+pixel]; } private byte[] GetInterpolatedAlpha(int pos) { int start = 128+(pos*blockwidth); return new byte[]{data[start], data[start+1], data[start+2], data[start+3], data[start+4], data[start+5], data[start+6], data[start+7]}; } private ushort[] GetLinearAlpha(int pos) { int start = 128+(pos*blockwidth); return new ushort[]{(ushort)(data[start]+ (data[start+1]<<8)), (ushort)(data[start+2]+ (ushort)(data[start+3]<<8)), (ushort)(data[start+4] + (data[start+5]<<8)), (ushort)(data[start+6] + (data[start+7]<<8))}; } public byte[] Bytes { get{return data;} } public byte[] GetTGAData() { byte[] tgaFile = new byte[width*height*4+18]; ImageConverter.TGA_Colour_Header.CopyTo(tgaFile, 0); tgaFile[12] = (byte)width; tgaFile[13] = (byte)(width>>8); tgaFile[14] = (byte)height; tgaFile[15] = (byte)(height>>8); int i, j; int texels_per_row = Math.Max(width >> 2, 1); int texel_rows = Math.Max(height >> 2, 1); int stride = width; byte[] interpolated_alpha; ushort[] linear_alpha; uint[] color_map = new uint[4]; byte[] alpha_map = new byte[8]; uint[,] alphadata = new uint[4,4]; uint rgba; int k, l; uint pixel_alpha_01, pixel_alpha_23; ushort colour0, colour1; int texelnum, tgacounter, pixelColour; byte index; if (this.Type == Converter.DXTType.DXT1) { for (i = 0; i < texel_rows; i++) { for (j = 0; j < texels_per_row; j++) { for (k=0; k<4; k++) { for (l=0; l<4; l++) { alphadata[k,l] = 0xffffffff; } } texelnum = i*texels_per_row + j; colour0 = GetColour0(texelnum); colour1 = GetColour1(texelnum); color_map[0] = ImageConverter.rgb565_to_rgba(colour0); color_map[1] = ImageConverter.rgb565_to_rgba(colour1); if (colour0 <= colour1) { color_map[2] = (uint)(((byte)color_map[1] + (byte)color_map[0]) / 2) + ((uint)(((byte)(color_map[1]>>8) + (byte)(color_map[0]>>8)) / 2)<<8) + ((uint)(((byte)(color_map[1]>>16) + (byte)(color_map[0]>>16)) / 2)<<16) + ((uint)(((byte)(color_map[1]>>24) + (byte)(color_map[0]>>24)) / 2)<<24); color_map[3] = 0x00000000;//colour map3 = transparent for (k=0; k<4; k++) { pixelColour = GetPixelColour(texelnum, (byte)k); tgacounter = (i*4*texels_per_row + j + (k * texels_per_row))*4; for (l=0; l<4; l++) { index = (byte)((pixelColour >> (l*2)) & 0x03); rgba = color_map[index]; tgaFile[(tgacounter + l)*4 + 18] = (byte)rgba; tgaFile[(tgacounter + l)*4 + 19] = (byte)(rgba>>8); tgaFile[(tgacounter + l)*4 + 20] = (byte)(rgba>>16); tgaFile[(tgacounter + l)*4 + 21] = (byte)((index==3)?0x00:0xff); } } } else { color_map[2] = (uint)((2 * (byte)color_map[0] + (byte)color_map[1] + 1) / 3) + ((uint)((2 * (byte)(color_map[0]>>8) + (byte)(color_map[1]>>8) + 1) / 3)<<8) + ((uint)((2 * (byte)(color_map[0]>>16) + (byte)(color_map[1]>>16) + 1) / 3)<<16) + ((uint)((2 * (byte)(color_map[0]>>24) + (byte)(color_map[1]>>24) + 1) / 3)<<24); color_map[3] = (uint)((2 * (byte)color_map[1] + (byte)color_map[0] + 1) / 3) + ((uint)((2 * (byte)(color_map[1]>>8) + (byte)(color_map[0]>>8) + 1) / 3)<<8) + ((uint)((2 * (byte)(color_map[1]>>16) + (byte)(color_map[0]>>16) + 1) / 3)<<16) + ((uint)((2 * (byte)(color_map[1]>>24) + (byte)(color_map[0]>>24) + 1) / 3)<<24); for (k=0; k<4; k++) { pixelColour = GetPixelColour(texelnum, (byte)k); tgacounter = (i*4*texels_per_row + j + (k * texels_per_row))*4; for (l=0; l<4; l++) { index = (byte)((pixelColour >> (l*2)) & 0x03); rgba = color_map[index] & alphadata[k,l]; tgaFile[(tgacounter + l)*4 + 18] = (byte)rgba; tgaFile[(tgacounter + l)*4 + 19] = (byte)(rgba>>8); tgaFile[(tgacounter + l)*4 + 20] = (byte)(rgba>>16); tgaFile[(tgacounter + l)*4 + 21] = 0xff; } } } } } } else if (this.Type == Converter.DXTType.DXT5) { for (i = 0; i < texel_rows; i++) { for (j = 0; j < texels_per_row; j++) { interpolated_alpha = GetInterpolatedAlpha(i*texels_per_row + j); alpha_map[0] = interpolated_alpha[0]; alpha_map[1] = interpolated_alpha[1]; if (alpha_map[0] <= alpha_map[1]) { alpha_map[2] = (byte)((4*alpha_map[0] + 1*alpha_map[1] + 2) / 5); // Bit code 010 alpha_map[3] = (byte)((3*alpha_map[0] + 2*alpha_map[1] + 2) / 5); // Bit code 011 alpha_map[4] = (byte)((2*alpha_map[0] + 3*alpha_map[1] + 2) / 5); // Bit code 100 alpha_map[5] = (byte)((1*alpha_map[0] + 4*alpha_map[1] + 2) / 5); // Bit code 101 alpha_map[6] = 0; // Bit code 110 alpha_map[7] = 255; // Bit code 111 } else { alpha_map[2] = (byte)((6*alpha_map[0] + 1*alpha_map[1] + 3) / 7); // bit code 010 alpha_map[3] = (byte)((5*alpha_map[0] + 2*alpha_map[1] + 3) / 7); // bit code 011 alpha_map[4] = (byte)((4*alpha_map[0] + 3*alpha_map[1] + 3) / 7); // bit code 100 alpha_map[5] = (byte)((3*alpha_map[0] + 4*alpha_map[1] + 3) / 7); // bit code 101 alpha_map[6] = (byte)((2*alpha_map[0] + 5*alpha_map[1] + 3) / 7); // bit code 110 alpha_map[7] = (byte)((1*alpha_map[0] + 6*alpha_map[1] + 3) / 7); // bit code 111 } pixel_alpha_01 = (uint)(((uint)interpolated_alpha[4] << 16) | ((uint)interpolated_alpha[3] << 8) | ((uint)interpolated_alpha[2])); pixel_alpha_23 = (uint)(((uint)interpolated_alpha[7] << 16) | ((uint)interpolated_alpha[6] << 8) | ((uint)interpolated_alpha[5])); alphadata[0,0] = (uint)(alpha_map[(pixel_alpha_01 >> 0) & 0x07] << 24) | 0x00ffffff; alphadata[0,1] = (uint)(alpha_map[(pixel_alpha_01 >> 3) & 0x07] << 24) | 0x00ffffff; alphadata[0,2] = (uint)(alpha_map[(pixel_alpha_01 >> 6) & 0x07] << 24) | 0x00ffffff; alphadata[0,3] = (uint)(alpha_map[(pixel_alpha_01 >> 9) & 0x07] << 24) | 0x00ffffff; alphadata[1,0] = (uint)(alpha_map[(pixel_alpha_01 >> 12) & 0x07] << 24) | 0x00ffffff; alphadata[1,1] = (uint)(alpha_map[(pixel_alpha_01 >> 15) & 0x07] << 24) | 0x00ffffff; alphadata[1,2] = (uint)(alpha_map[(pixel_alpha_01 >> 18) & 0x07] << 24) | 0x00ffffff; alphadata[1,3] = (uint)(alpha_map[(pixel_alpha_01 >> 21) & 0x07] << 24) | 0x00ffffff; alphadata[2,0] = (uint)(alpha_map[(pixel_alpha_23 >> 0) & 0x07] << 24) | 0x00ffffff; alphadata[2,1] = (uint)(alpha_map[(pixel_alpha_23 >> 3) & 0x07] << 24) | 0x00ffffff; alphadata[2,2] = (uint)(alpha_map[(pixel_alpha_23 >> 6) & 0x07] << 24) | 0x00ffffff; alphadata[2,3] = (uint)(alpha_map[(pixel_alpha_23 >> 9) & 0x07] << 24) | 0x00ffffff; alphadata[3,0] = (uint)(alpha_map[(pixel_alpha_23 >> 12) & 0x07] << 24) | 0x00ffffff; alphadata[3,1] = (uint)(alpha_map[(pixel_alpha_23 >> 15) & 0x07] << 24) | 0x00ffffff; alphadata[3,2] = (uint)(alpha_map[(pixel_alpha_23 >> 18) & 0x07] << 24) | 0x00ffffff; alphadata[3,3] = (uint)(alpha_map[(pixel_alpha_23 >> 21) & 0x07] << 24) | 0x00ffffff; texelnum = i*texels_per_row + j; color_map[0] = ImageConverter.rgb565_to_rgba(GetColour0(texelnum)); color_map[1] = ImageConverter.rgb565_to_rgba(GetColour1(texelnum)); color_map[2] = (uint)((2 * (byte)color_map[0] + (byte)color_map[1] + 1) / 3) + ((uint)((2 * (byte)(color_map[0]>>8) + (byte)(color_map[1]>>8) + 1) / 3)<<8) + ((uint)((2 * (byte)(color_map[0]>>16) + (byte)(color_map[1]>>16) + 1) / 3)<<16) + ((uint)((2 * (byte)(color_map[0]>>24) + (byte)(color_map[1]>>24) + 1) / 3)<<24); color_map[3] = (uint)((2 * (byte)color_map[1] + (byte)color_map[0] + 1) / 3) + ((uint)((2 * (byte)(color_map[1]>>8) + (byte)(color_map[0]>>8) + 1) / 3)<<8) + ((uint)((2 * (byte)(color_map[1]>>16) + (byte)(color_map[0]>>16) + 1) / 3)<<16) + ((uint)((2 * (byte)(color_map[1]>>24) + (byte)(color_map[0]>>24) + 1) / 3)<<24); for (k=0; k<4; k++) { pixelColour = GetPixelColour(texelnum, (byte)k); tgacounter = (i*4*texels_per_row + j + (k * texels_per_row))*4; for (l=0; l<4; l++) { index = (byte)((pixelColour >> (l*2)) & 0x03); rgba = color_map[index] & alphadata[k,l]; tgaFile[(tgacounter + l)*4 + 18] = (byte)rgba; tgaFile[(tgacounter + l)*4 + 19] = (byte)(rgba>>8); tgaFile[(tgacounter + l)*4 + 20] = (byte)(rgba>>16); tgaFile[(tgacounter + l)*4 + 21] = (byte)(rgba>>24); } } } } } else if (this.Type == Converter.DXTType.DXT3) { for (i = 0; i < texel_rows; i++) { for (j = 0; j < texels_per_row; j++) { linear_alpha = GetLinearAlpha(i*texels_per_row + j); for (k=0; k<4; k++) { alphadata[k,0] = (uint)((((uint)linear_alpha[k] << 4) & 0xf0) << 24) | 0x00ffffff; alphadata[k,1] = (uint)((((uint)linear_alpha[k] << 0) & 0xf0) << 24) | 0x00ffffff; alphadata[k,2] = (uint)((((uint)linear_alpha[k] >> 4) & 0xf0) << 24) | 0x00ffffff; alphadata[k,3] = (uint)((((uint)linear_alpha[k] >> 8) & 0xf0) << 24) | 0x00ffffff; } texelnum = i*texels_per_row + j; color_map[0] = ImageConverter.rgb565_to_rgba(GetColour0(texelnum)); color_map[1] = ImageConverter.rgb565_to_rgba(GetColour1(texelnum)); color_map[2] = (uint)((2 * (byte)color_map[0] + (byte)color_map[1] + 1) / 3) + ((uint)((2 * (byte)(color_map[0]>>8) + (byte)(color_map[1]>>8) + 1) / 3)<<8) + ((uint)((2 * (byte)(color_map[0]>>16) + (byte)(color_map[1]>>16) + 1) / 3)<<16) + ((uint)((2 * (byte)(color_map[0]>>24) + (byte)(color_map[1]>>24) + 1) / 3)<<24); color_map[3] = (uint)((2 * (byte)color_map[1] + (byte)color_map[0] + 1) / 3) + ((uint)((2 * (byte)(color_map[1]>>8) + (byte)(color_map[0]>>8) + 1) / 3)<<8) + ((uint)((2 * (byte)(color_map[1]>>16) + (byte)(color_map[0]>>16) + 1) / 3)<<16) + ((uint)((2 * (byte)(color_map[1]>>24) + (byte)(color_map[0]>>24) + 1) / 3)<<24); for (k=0; k<4; k++) { pixelColour = GetPixelColour(texelnum, (byte)k); tgacounter = (i*4*texels_per_row + j + (k * texels_per_row))*4; for (l=0; l<4; l++) { index = (byte)((pixelColour >> (l*2)) & 0x03); rgba = color_map[index] & alphadata[k,l]; tgaFile[(tgacounter + l)*4 + 18] = (byte)rgba; tgaFile[(tgacounter + l)*4 + 19] = (byte)(rgba>>8); tgaFile[(tgacounter + l)*4 + 20] = (byte)(rgba>>16); tgaFile[(tgacounter + l)*4 + 21] = (byte)(rgba>>24); } } } } } else { throw new InvalidOperationException("DDS file was not a DXT1, DXT3 or DXT5 file"); } return tgaFile; } public static DDSFile MakeFrom32bitBGRA(byte[] rawData, Converter.DXTType DXTCFormat, int width, int height) { byte[] data = new byte[rawData.Length]; rawData.CopyTo(data, 0); for (int i = 0; i < data.Length; i+= 4) { byte temp = data[i]; data[i] = data[i+2]; data[i+2] = temp; } return MakeFrom32bitRGBA(data, DXTCFormat, width, height); } public static DDSFile MakeFrom32bitRGBA(byte[] rawData, Converter.DXTType DXTCFormat, int width, int height) { SquishFlags format; switch(DXTCFormat) { case Converter.DXTType.DXT1: format = SquishFlags.kDxt1; break; case Converter.DXTType.DXT3: format = SquishFlags.kDxt3; break; case Converter.DXTType.DXT5: format = SquishFlags.kDxt5; break; default: throw new InvalidOperationException("Invalid DXT Type supplied: "+DXTCFormat.ToString()); } return MakeWithMipMapsFrom32bitRGBA(rawData, DXTCFormat, width, height, format); } private static DDSFile MakeWithMipMapsFrom32bitRGBA(byte[] rawData, Converter.DXTType DXTCFormat, int width, int height, SquishFlags format) { int mips = CalcMips(width, height); byte[] data = new byte[CalcDataSize(DXTCFormat, width, height, mips)]; int lastLocation = 0; byte[] temp; byte[] map = SquishWrapper.SquishWrapper.CompressImage(rawData, width, height, (int)format); map.CopyTo(data, 0); lastLocation+= map.Length; if (mips > 0) { for (int i = 1; i < mips; i++) { double reduction = (double)Math.Pow(2, i); int redWidth = (int)Math.Round(width / reduction); int redHeight = (int)Math.Round(height / reduction); map = new byte[redWidth * redHeight * 4]; OpenILPort.Converter.Zoom(map, rawData, width, height, redWidth, redHeight, OpenILPort.Converter.Filter.Triangle, 1.0); temp = SquishWrapper.SquishWrapper.CompressImage(map, redWidth, redHeight, (int)format); temp.CopyTo(data, lastLocation); lastLocation+= temp.Length; } } return MakeFromDDSCompressedImage(data, DXTCFormat, width, height); } public static DDSFile MakeFromDDSCompressedImage(byte[] rawData, Converter.DXTType DXTCFormat, int width, int height) { return new DDSFile(rawData, width, height, DXTCFormat); } } }