This class represents a file header for a binary BLP image. Its primary function is to map the rest of the data in a meaningful way, and describe the image format the BLP image is stored as.
Example #1
0
        /// <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]));
                    }
                }
            }
        }
Example #2
0
        /// <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]));
                    }
                }
            }
        }
Example #3
0
        /// <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;
            }
        }
Example #4
0
        /// <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]));
                    }
                }
            }
        }
Example #5
0
        /// <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;
            }
        }