Created
May 8, 2013 09:22
-
-
Save Konctantin/5539302 to your computer and use it in GitHub Desktop.
blp
This file contains hidden or 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
| /* | |
| * Example | |
| using System; | |
| using System.Drawing; | |
| using System.Drawing.Imaging; | |
| namespace BLP | |
| { | |
| class Program | |
| { | |
| static void Main(string[] args) | |
| { | |
| Bitmap bm = new BLP("dxt1.blp").GetBitmap; | |
| bm.Save("test.png", ImageFormat.Png); | |
| bm.Save("test.jpg", ImageFormat.Jpeg); | |
| bm.Save("test.bmp", ImageFormat.Bmp); | |
| Graphics g = bm.GetGraphics; | |
| } | |
| } | |
| } | |
| */ | |
| using System; | |
| using System.Drawing; | |
| using System.IO; | |
| using System.Runtime.InteropServices; | |
| namespace BLPDecoder | |
| { | |
| public class BLP | |
| { | |
| #region Members | |
| private BinaryReader br; | |
| private BLPHeader header; | |
| private Point offset; | |
| private Size size; | |
| private Bitmap image; | |
| #endregion | |
| #region Constructor | |
| public BLP(string path) | |
| { | |
| br = new BinaryReader(new FileStream(path, FileMode.Open)); | |
| Read(); | |
| } | |
| public BLP(Stream input) | |
| { | |
| br = new BinaryReader(input); | |
| Read(); | |
| } | |
| #endregion | |
| #region Init Methods | |
| private void Read() | |
| { | |
| header.Read(br); | |
| size = new Size(header.Width, header.Height); | |
| image = new Bitmap(header.Width, header.Height); | |
| switch (header.Compressed) | |
| { | |
| case CompressedType.RAW: | |
| RawColor(); | |
| break; | |
| case CompressedType.RawAlpha1bit: | |
| RawColor(); | |
| RawAlpha1bit(); | |
| break; | |
| case CompressedType.RawAlpha8bit: | |
| RawColor(); | |
| RawAlpha8bit(); | |
| break; | |
| case CompressedType.DXT1: | |
| DXT1(header.AlphaDepth); | |
| break; | |
| case CompressedType.DXT3: | |
| DXT3(); | |
| break; | |
| case CompressedType.DXT5: | |
| DXT5(); | |
| break; | |
| default: throw new Exception("Invalid Encoding"); | |
| } | |
| br.Close(); | |
| } | |
| #endregion | |
| #region Properties | |
| public Bitmap GetBitmap | |
| { | |
| get { return image; } | |
| } | |
| public Graphics GetGraphics | |
| { | |
| get { return Graphics.FromImage(image); } | |
| } | |
| #endregion | |
| #region Decode Methods | |
| private void DXT64bitColor(int offsetX, int offsetY, byte alpha) | |
| { | |
| ushort c0 = br.ReadUInt16(); | |
| ushort c1 = br.ReadUInt16(); | |
| BLPColor[] c = new BLPColor[4]; | |
| c[0] = RGB565ToColor(c0); | |
| c[1] = RGB565ToColor(c1); | |
| if ((alpha & 0x01) == 0 || c0 > c1) | |
| { | |
| c[2] = ColorRatio(c[0], c[1], 0.66667f); | |
| c[3] = ColorRatio(c[0], c[1], 0.33333f); | |
| } | |
| else | |
| { | |
| c[2] = ColorRatio(c[0], c[1], 0.5f); | |
| c[3].SetZero(); | |
| } | |
| for (int j = 0; j < 4; j++) | |
| { | |
| byte bits = br.ReadByte(); | |
| for (int i = 0; i < 4; i++) | |
| { | |
| if (alpha == 0xFF) | |
| image.SetPixel(offsetX + i, offsetY + j, c[bits & 0x03].GetColorAlpha256); | |
| else | |
| image.SetPixel(offsetX + i, offsetY + j, c[bits & 0x03].GetColor); | |
| bits >>= 2; | |
| } | |
| } | |
| } | |
| private void Alpha4bit4x4(int offsetX, int offsetY) | |
| { | |
| for (byte j = 0; j < 4; j++) | |
| { | |
| for (byte i = 0; i < 4; ) | |
| { | |
| byte alpha = br.ReadByte(); | |
| SetAlpha(offsetX + i++, offsetY + j, (byte)((alpha & 0x0F) << 4)); | |
| SetAlpha(offsetX + i++, offsetY + j, (byte)((alpha & 0x0F))); | |
| } | |
| } | |
| } | |
| private void DXT64bitAlpha(int offsetX, int offsetY) | |
| { | |
| byte[] a = new byte[8]; | |
| a[0] = br.ReadByte(); | |
| a[1] = br.ReadByte(); | |
| for (int i = 2, j = a.Length - 2; i < a.Length; ++i, --j) | |
| { | |
| a[i] = (byte)((j * a[0] + (i - 1) * a[1]) / 7.0f); | |
| } | |
| byte bits = 0, n = 0, index; | |
| for (byte j = 0; j < 4; j++) | |
| { | |
| for (byte i = 0; i < 4; i++) | |
| { | |
| if (n >= 3) | |
| { | |
| index = (byte)(bits & 0x07); | |
| bits >>= 3; | |
| n -= 3; | |
| } | |
| else | |
| { | |
| index = (byte)(bits << (3 - n)); | |
| bits = br.ReadByte(); | |
| if (n > 0) | |
| { | |
| int m = (n == 1) ? 0x01 : 0x03; | |
| index |= (byte)(bits & m); | |
| } | |
| bits >>= 3 - n; | |
| n += 5; | |
| } | |
| SetAlpha(offsetX + i, offsetY + j, a[index]); | |
| } | |
| } | |
| } | |
| private void DXT1(byte alphaDepth) | |
| { | |
| for (int j = 0; j < header.Height; j += 4) | |
| { | |
| for (int i = 0; i < header.Width; i += 4) | |
| { | |
| DXT64bitColor(offset.X + i, offset.Y + j, alphaDepth); | |
| } | |
| } | |
| } | |
| private void DXT3() | |
| { | |
| for (int j = 0; j < header.Height; j += 4) | |
| { | |
| for (int i = 0; i < header.Width; i += 4) | |
| { | |
| Alpha4bit4x4(offset.X + i, offset.Y + j); | |
| DXT64bitColor(offset.X + i, offset.Y + j, 255); | |
| } | |
| } | |
| } | |
| private void DXT5() | |
| { | |
| for (int j = 0; j < header.Height; j += 4) | |
| { | |
| for (int i = 0; i < header.Width; i += 4) | |
| { | |
| DXT64bitAlpha(offset.X + i, offset.Y + j); | |
| DXT64bitColor(offset.X + i, offset.Y + j, 255); | |
| } | |
| } | |
| } | |
| private void RawColor() | |
| { | |
| for (int j = 0; j < header.Height; j++) | |
| { | |
| for (int i = 0; i < header.Width; i++) | |
| { | |
| image.SetPixel(offset.X + i, offset.Y + j, header.Palette[br.ReadByte()].GetColorAlpha256); | |
| } | |
| } | |
| } | |
| private void RawAlpha1bit() | |
| { | |
| byte alpha, bits = 0, n = 0; | |
| for (int j = 0; j < header.Height; j++) | |
| { | |
| for (int i = 0; i < header.Width; i++) | |
| { | |
| if (n != 0) | |
| n--; | |
| else | |
| { | |
| bits = br.ReadByte(); | |
| n = 7; | |
| } | |
| alpha = (byte)(((bits & 0x01) != 0) ? 255 : 0); | |
| bits >>= 1; | |
| } | |
| } | |
| } | |
| private void RawAlpha8bit() | |
| { | |
| for (int j = 0; j < header.Height; j++) | |
| { | |
| for (int i = 0; i < header.Width; i++) | |
| { | |
| SetAlpha(offset.X + i, offset.Y + j, br.ReadByte()); | |
| } | |
| } | |
| } | |
| #endregion | |
| #region Support methods | |
| private void SetAlpha(int x, int y, byte alpha) | |
| { | |
| BLPColor c; | |
| c.B = image.GetPixel(x, y).B; | |
| c.G = image.GetPixel(x, y).G; | |
| c.R = image.GetPixel(x, y).R; | |
| c.A = alpha; | |
| } | |
| private BLPColor RGB565ToColor(ushort a) | |
| { | |
| BLPColor c; | |
| c.R = (byte)((a & 0xF800) >> 8); | |
| c.G = (byte)((a & 0x07E0) >> 3); | |
| c.B = (byte)((a & 0x001F) << 3); | |
| c.A = 255; | |
| return c; | |
| } | |
| private BLPColor ColorRatio(BLPColor c0, BLPColor c1, float r0) | |
| { | |
| BLPColor c; | |
| c.R = (byte)(r0 * c0.R + (1.0f - r0) * c1.R); | |
| c.G = (byte)(r0 * c0.G + (1.0f - r0) * c1.G); | |
| c.B = (byte)(r0 * c0.B + (1.0f - r0) * c1.B); | |
| c.A = 255; | |
| return c; | |
| } | |
| #endregion | |
| #region BLP struct | |
| [StructLayout(LayoutKind.Sequential, Pack = 1)] | |
| public struct BLPHeader | |
| { | |
| [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.I1)] | |
| public byte[] FourCC; | |
| public int Type; | |
| public byte Encoding; | |
| public byte AlphaDepth; | |
| public byte AlphaEncoding; | |
| public byte HasMips; | |
| public int Width; | |
| public int Height; | |
| [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16, ArraySubType = UnmanagedType.I4)] | |
| public int[] Offsets; | |
| [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16, ArraySubType = UnmanagedType.I4)] | |
| public int[] Lengths; | |
| [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] | |
| public BLPColor[] Palette; | |
| public int Size { get { return Width * Height; } } | |
| public void Read(BinaryReader stream) | |
| { | |
| int len = Marshal.SizeOf(typeof(BLPHeader)); | |
| byte[] buffer = new byte[len]; | |
| stream.Read(buffer, 0, len); | |
| GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); | |
| this = (BLPHeader)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(BLPHeader)); | |
| handle.Free(); | |
| } | |
| public CompressedType Compressed | |
| { | |
| get | |
| { | |
| if (Encoding == 1) | |
| { | |
| if (AlphaDepth == 1) | |
| return CompressedType.RawAlpha1bit; | |
| else if (AlphaDepth == 8) | |
| return CompressedType.RawAlpha8bit; | |
| else if (AlphaDepth == 0) | |
| return CompressedType.RAW; | |
| else | |
| return CompressedType.UNK; | |
| } | |
| else | |
| { | |
| if (AlphaDepth == 0 || AlphaDepth == 1) | |
| return CompressedType.DXT1; | |
| else if (AlphaDepth == 8 && AlphaEncoding == 1) | |
| return CompressedType.DXT3; | |
| else if (AlphaDepth == 8 && AlphaEncoding == 7) | |
| return CompressedType.DXT5; | |
| else | |
| return CompressedType.UNK; | |
| } | |
| } | |
| } | |
| } | |
| public struct BLPColor | |
| { | |
| public byte B; | |
| public byte G; | |
| public byte R; | |
| public byte A; | |
| public void SetZero() | |
| { | |
| B = G = R = A = 0; | |
| } | |
| public Color GetColor | |
| { | |
| get { return Color.FromArgb((int)A, (int)R, (int)G, (int)B); } | |
| } | |
| public Color GetColorAlpha256 | |
| { | |
| get { return Color.FromArgb((int)255, (int)R, (int)G, (int)B); } | |
| } | |
| } | |
| public enum CompressedType | |
| { | |
| RAW, | |
| RawAlpha1bit, | |
| RawAlpha8bit, | |
| DXT1, | |
| DXT3, | |
| DXT5, | |
| UNK, | |
| }; | |
| #endregion | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment