Skip to content

Instantly share code, notes, and snippets.

@Konctantin
Created May 8, 2013 09:22
Show Gist options
  • Select an option

  • Save Konctantin/5539302 to your computer and use it in GitHub Desktop.

Select an option

Save Konctantin/5539302 to your computer and use it in GitHub Desktop.
blp
/*
* 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