/// <summary> /// Initializes a new instance of the <see cref="Warcraft.BLP.BLP"/> class. /// </summary> /// <param name="inData">ExtendedData.</param> public BLP(byte[] inData) { using (var ms = new MemoryStream(inData)) { using (var br = new BinaryReader(ms)) { byte[] fileHeaderBytes; if (PeekFormat(br) == BLPFormat.BLP2) { fileHeaderBytes = br.ReadBytes(148); } else { fileHeaderBytes = br.ReadBytes(156); } Header = new BLPHeader(fileHeaderBytes); if (Header.CompressionType == TextureCompressionType.JPEG) { _jpegHeaderSize = br.ReadUInt32(); _jpegHeader = br.ReadBytes((int)_jpegHeaderSize); } else if (Header.CompressionType == TextureCompressionType.Palettized) { for (var i = 0; i < 256; ++i) { var b = br.ReadByte(); var g = br.ReadByte(); var r = br.ReadByte(); // The alpha in the palette is not used, but is stored for the sake of completion. var a = br.ReadByte(); var paletteColor = new Rgba32(r, g, b, a); _palette.Add(paletteColor); } } else { // Fill up an empty palette - the palette is always present, but we'll be going after offsets anyway for (var i = 0; i < 256; ++i) { var paletteColor = default(Rgba32); _palette.Add(paletteColor); } } // Read the raw mipmap data for (var i = 0; i < Header.GetNumMipMaps(); ++i) { br.BaseStream.Position = Header.MipMapOffsets[i]; _rawMipMaps.Add(br.ReadBytes((int)Header.MipMapSizes[i])); } } } }
/// <summary> /// Initializes a new instance of the <see cref="Warcraft.BLP.BLP"/> class. /// </summary> /// <param name="inData">Data.</param> public BLP(byte[] inData) { using (MemoryStream ms = new MemoryStream(inData)) { using (BinaryReader br = new BinaryReader(ms)) { byte[] fileHeaderBytes; if (PeekFormat(br) == BLPFormat.BLP2) { fileHeaderBytes = br.ReadBytes(148); } else { fileHeaderBytes = br.ReadBytes(156); } this.Header = new BLPHeader(fileHeaderBytes); if (this.Header.CompressionType == TextureCompressionType.JPEG) { this.JPEGHeaderSize = br.ReadUInt32(); this.JPEGHeader = br.ReadBytes((int)this.JPEGHeaderSize); } else if (this.Header.CompressionType == TextureCompressionType.Palettized) { for (int i = 0; i < 256; ++i) { byte b = br.ReadByte(); byte g = br.ReadByte(); byte r = br.ReadByte(); // The alpha in the palette is not used, but is stored for the sake of completion. byte a = br.ReadByte(); Color paletteColor = Color.FromArgb(a, r, g, b); this.Palette.Add(paletteColor); } } else { // Fill up an empty palette - the palette is always present, but we'll be going after offsets anyway for (int i = 0; i < 256; ++i) { Color paletteColor = Color.FromArgb(0, 0, 0, 0); this.Palette.Add(paletteColor); } } // Read the raw mipmap data for (int i = 0; i < this.Header.GetNumMipMaps(); ++i) { br.BaseStream.Position = this.Header.MipMapOffsets[i]; this.RawMipMaps.Add(br.ReadBytes((int)this.Header.MipMapSizes[i])); } } } }
/// <summary> /// Initializes a new instance of the <see cref="Warcraft.BLP.BLP"/> class. /// This constructor creates a BLP file using the specified compression from a bitmap object. /// If the compression type specifed is DXTC, the default pixel format used is DXT1 for opaque textures and DXT3 for the rest. /// </summary> /// <param name="image">Image.</param> /// <param name="compressionType">Compression type.</param> public BLP(Image <Rgba32> image, TextureCompressionType compressionType) { // Set up the header Header = new BLPHeader { CompressionType = compressionType }; if (compressionType == TextureCompressionType.Palettized) { Header.PixelFormat = BLPPixelFormat.Palettized; // Determine best alpha bit depth if (image.HasAlpha()) { var alphaLevels = new List <byte>(); for (var y = 0; y < image.Height; ++y) { for (var x = 0; x < image.Width; ++x) { var pixel = image[x, y]; if (!alphaLevels.Contains(pixel.A)) { alphaLevels.Add(pixel.A); } if (alphaLevels.Count > 16) { break; } } } if (alphaLevels.Count > 16) { // More than 16? Use a full byte Header.AlphaBitDepth = 8; } else if (alphaLevels.Count > 2) { // More than 2, but less than or equal to 16? Use half a byte Header.AlphaBitDepth = 4; } else { // Just 2? Use a bit instead Header.AlphaBitDepth = 1; } } else { // No alpha, so a bit depth of 0. Header.AlphaBitDepth = 0; } } else if (compressionType == TextureCompressionType.DXTC) { Header.AlphaBitDepth = 8; // Determine best DXTC type (1, 3 or 5) if (image.HasAlpha()) { Header.PixelFormat = BLPPixelFormat.DXT3; } else { // DXT1 for no alpha Header.PixelFormat = BLPPixelFormat.DXT1; } } else if (compressionType == TextureCompressionType.Uncompressed) { // The alpha will be stored as a straight ARGB texture, so set it to 8 Header.AlphaBitDepth = 8; Header.PixelFormat = BLPPixelFormat.PalARGB1555DitherFloydSteinberg; } // What the mip type does is currently unknown, but it's usually set to 1. Header.MipMapType = 1; Header.Resolution = new Resolution((uint)image.Width, (uint)image.Height); // It's now time to compress the image _rawMipMaps = CompressImage(image); // Calculate the offsets and sizes var mipOffset = (uint)(Header.GetSize() + (_palette.Count * 4)); foreach (var rawMipMap in _rawMipMaps) { var mipSize = (uint)rawMipMap.Length; Header.MipMapOffsets.Add(mipOffset); Header.MipMapSizes.Add(mipSize); // Push the offset ahead for the next mipmap mipOffset += mipSize; } }
/// <summary> /// Initializes a new instance of the <see cref="Warcraft.BLP.BLP"/> class. /// This constructor reads a binary BLP file from disk. /// </summary> /// <param name="inData">Data.</param> public BLP(byte[] inData) { using (MemoryStream ms = new MemoryStream(inData)) { using (BinaryReader br = new BinaryReader(ms)) { byte[] fileHeaderBytes; if (PeekFormat(br) == BLPFormat.BLP2) { fileHeaderBytes = br.ReadBytes(148); } else { fileHeaderBytes = br.ReadBytes(156); } this.Header = new BLPHeader(fileHeaderBytes); if (this.Header.CompressionType == TextureCompressionType.JPEG) { this.JPEGHeaderSize = br.ReadUInt32(); this.JPEGHeader = br.ReadBytes((int)this.JPEGHeaderSize); } else if (this.Header.CompressionType == TextureCompressionType.Palettized) { for (int i = 0; i < 256; ++i) { byte b = br.ReadByte(); byte g = br.ReadByte(); byte r = br.ReadByte(); // The alpha in the palette is not used, but is stored for the sake of completion. byte a = br.ReadByte(); Color paletteColor = Color.FromArgb(a, r, g, b); this.Palette.Add(paletteColor); } } else { // Fill up an empty palette - the palette is always present, but we'll be going after offsets anyway for (int i = 0; i < 256; ++i) { Color paletteColor = Color.FromArgb(0, 0, 0, 0); this.Palette.Add(paletteColor); } } // Read the raw mipmap data for (int i = 0; i < this.Header.GetNumMipMaps(); ++i) { br.BaseStream.Position = this.Header.MipMapOffsets[i]; this.RawMipMaps.Add(br.ReadBytes((int) this.Header.MipMapSizes[i])); } } } }
/// <summary> /// Initializes a new instance of the <see cref="Warcraft.BLP.BLP"/> class. /// This constructor creates a BLP file using the specified compression from a bitmap object. /// If the compression type specifed is DXTC, the default pixel format used is DXT1 for opaque textures and DXT3 for the rest. /// </summary> /// <param name="image">Image.</param> /// <param name="compressionType">Compression type.</param> public BLP(Bitmap image, TextureCompressionType compressionType) { // Set up the header this.Header = new BLPHeader { CompressionType = compressionType }; if (compressionType == TextureCompressionType.Palettized) { this.Header.PixelFormat = BLPPixelFormat.Pixel_Palettized; // Determine best alpha bit depth if (image.HasAlpha()) { List<byte> alphaLevels = new List<byte>(); for (int y = 0; y < image.Height; ++y) { for (int x = 0; x < image.Width; ++x) { Color pixel = image.GetPixel(x, y); if (!alphaLevels.Contains(pixel.A)) { alphaLevels.Add(pixel.A); } if (alphaLevels.Count > 16) { break; } } } if (alphaLevels.Count > 16) { // More than 16? Use a full byte this.Header.AlphaBitDepth = 8; } else if (alphaLevels.Count > 2) { // More than 2, but less than or equal to 16? Use half a byte this.Header.AlphaBitDepth = 4; } else { // Just 2? Use a bit instead this.Header.AlphaBitDepth = 1; } } else { // No alpha, so a bit depth of 0. this.Header.AlphaBitDepth = 0; } } else if (compressionType == TextureCompressionType.DXTC) { this.Header.AlphaBitDepth = 8; // Determine best DXTC type (1, 3 or 5) if (image.HasAlpha()) { this.Header.PixelFormat = BLPPixelFormat.Pixel_DXT3; } else { // DXT1 for no alpha this.Header.PixelFormat = BLPPixelFormat.Pixel_DXT1; } } else if (compressionType == TextureCompressionType.Uncompressed) { // The alpha will be stored as a straight ARGB texture, so set it to 8 this.Header.AlphaBitDepth = 8; this.Header.PixelFormat = BLPPixelFormat.Pixel_PalARGB1555DitherFloydSteinberg; } // What the mip type does is currently unknown, but it's usually set to 1. this.Header.MipMapType = 1; this.Header.Resolution = new Resolution((uint)image.Width, (uint)image.Height); // It's now time to compress the image this.RawMipMaps = CompressImage(image); // Calculate the offsets and sizes uint mipOffset = (uint)(this.Header.GetSize() + this.Palette.Count * 4); foreach (byte[] rawMipMap in this.RawMipMaps) { uint mipSize = (uint)rawMipMap.Length; this.Header.MipMapOffsets.Add(mipOffset); this.Header.MipMapSizes.Add(mipSize); // Push the offset ahead for the next mipmap mipOffset += mipSize; } }