/// <summary> /// Saves the currently loaded image to a Dxt encoded Blp file. /// </summary> /// <param name="filename">The path to the output file.</param> /// <param name="format">Dxt format. See <see cref="LibSquish.DxtFormat"/></param> /// <param name="resizeMethod">Method for possibly required resizing. See <see cref="FixSize"/></param> /// <param name="hasMipmaps">Specifies whether mipmaps are generated or not.</param> /// <param name="compression">Compression method used by LibSquish. Affects quality / speed. See <see cref="LibSquish.ColorCompression"/></param> public void Save(string filename, LibSquish.DxtFormat format, ResizeMethod resizeMethod, bool hasMipmaps, LibSquish.ColorCompression compression = LibSquish.ColorCompression.ClusterFit) { if (bmp != null) { FileStream fs = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.Write); using (BinaryWriter bw = new BinaryWriter(fs)) { Blp2Header header = new Blp2Header(DataType.Uncompressed_DirectX, Encoding.DXT, 0, 0, (byte)(hasMipmaps ? 1 : 0), 0, 0); List <Bitmap> bmps = new List <Bitmap>(); // Add resized image to the output bmps.Add(FixSize(resizeMethod)); header.width = (uint)bmps[0].Width; header.height = (uint)bmps[0].Height; // Add mipmaps if (hasMipmaps) { bmps.AddRange(GenerateMipmaps(bmps[0])); } byte[] compImgData = { }; byte[] colorPalette = new byte[256 * 4]; switch (format) { case LibSquish.DxtFormat.Dxt1: header.alphaEncoding = AlphaEncoding.DXT1; header.alphaDepth = AlphaDepth.Alpha1Bit; break; case LibSquish.DxtFormat.Dxt3: header.alphaEncoding = AlphaEncoding.DXT3; header.alphaDepth = AlphaDepth.Alpha8Bit; break; case LibSquish.DxtFormat.Dxt5: header.alphaEncoding = AlphaEncoding.DXT5; header.alphaDepth = AlphaDepth.Alpha8Bit; break; } for (int i = 0; i < bmps.Count; i++) { // Copy raw image data to byte array BitmapData bmpData = bmps[i].LockBits(new Rectangle(0, 0, bmps[i].Width, bmps[i].Height), ImageLockMode.ReadOnly, bmps[i].PixelFormat); byte[] imgData = new byte[bmpData.Stride * bmps[i].Height]; Marshal.Copy(bmpData.Scan0, imgData, 0, bmpData.Stride * bmps[i].Height); bmps[i].UnlockBits(bmpData); // Set up conversion flags int flags = (int)format | (int)compression | (int)LibSquish.ColorErrorMetric.Perceptual; // Compress to DXT format RemapRGBA(ref imgData); byte[] buff = new byte[LibSquish.GetStorageRequirements((uint)bmps[i].Width, (uint)bmps[i].Height, flags)]; LibSquish.CompressImage(imgData, (uint)bmps[i].Width, (uint)bmps[i].Height, buff, flags); Array.Resize(ref compImgData, compImgData.Length + buff.Length); buff.CopyTo(compImgData, compImgData.Length - buff.Length); // Image data locations header.offsets[i] = (i > 0) ? header.offsets[i - 1] + header.lengths[i - 1] : (uint)(Marshal.SizeOf(typeof(Blp2Header)) + colorPalette.Length); header.lengths[i] = (uint)buff.Length; } bw.Write(header.GetBytes()); bw.Write(colorPalette); bw.Write(compImgData); } } else { throw new BlpConversionException("No image data to convert."); } }
/// <summary> /// Creates a Blp2 object from the specified data stream. /// </summary> /// <param name="stream">A stream that contains the data of a Blp2 or System.Drawing.Image picture.</param> /// <returns>Blp2 object containing the image of the stream.</returns> /// <exception cref=""></exception> public static Blp2 FromStream(Stream stream) { using (BinaryReader br = new BinaryReader(stream)) { Blp2Header header = Blp2Header.FromBinaryReader(br); Blp2 ret = new Blp2(); if (new string(header.fourCC) == "BLP2") { ret.bmp = new Bitmap((int)header.width, (int)header.height, PixelFormat.Format32bppArgb); BitmapData bmpData = ret.bmp.LockBits(new Rectangle(0, 0, ret.bmp.Width, ret.bmp.Height), ImageLockMode.ReadWrite, ret.bmp.PixelFormat); byte[] imgData = new byte[header.lengths[0]]; br.BaseStream.Seek(header.offsets[0], SeekOrigin.Begin); imgData = br.ReadBytes(imgData.Length); // BLP2 conversion if (header.type == DataType.Uncompressed_DirectX && header.encoding == Encoding.RAW1 && (header.alphaDepth == AlphaDepth.NoAlpha || header.alphaDepth == AlphaDepth.Alpha1Bit || header.alphaDepth == AlphaDepth.Alpha8Bit)) { // Uncompressed paletted image ret.format = FileFormat.Raw; // Load palette (4-byte BGRA color values) br.BaseStream.Seek(Marshal.SizeOf(typeof(Blp2Header)), SeekOrigin.Begin); byte[] colorData = br.ReadBytes(256 * 4); Color[] c = new Color[256]; for (int i = 0; i < 1024; i += 4) { c[i / 4] = Color.FromArgb(255, colorData[i], colorData[i + 1], colorData[i + 2]); } // Read indexed bitmap byte[] decompImgData = new byte[header.width * header.height * 4]; for (int i = 0; i < (header.width * header.height); i++) { decompImgData[i * 4] = c[imgData[i]].R; decompImgData[i * 4 + 1] = c[imgData[i]].G; decompImgData[i * 4 + 2] = c[imgData[i]].B; decompImgData[i * 4 + 3] = 255; } // Apply alpha channels if (header.alphaDepth == AlphaDepth.Alpha8Bit) { // 8-bits alpha br.BaseStream.Seek(header.offsets[0] + header.width * header.height, SeekOrigin.Begin); byte[] alphaData = br.ReadBytes((int)(header.width * header.height)); for (int i = 0; i < (header.width * header.height); i++) { decompImgData[i * 4 + 3] = alphaData[i]; } } else if (header.alphaDepth == AlphaDepth.Alpha1Bit) { // 1-bits alpha br.BaseStream.Seek(header.offsets[0] + (header.width * header.height), SeekOrigin.Begin); byte[] alphaData = br.ReadBytes((int)(header.width * header.height / 8)); for (int i = 0; i < (header.width * header.height / 8); i++) { for (int j = 0; j < 8; j++) { decompImgData[i * 32 + ((j + 1) * 4) - 1] = (byte)((alphaData[i] & 1 << j) == 1 << j ? 255 : 0); } } } Marshal.Copy(decompImgData, 0, bmpData.Scan0, decompImgData.Length); } else if (header.type == DataType.Uncompressed_DirectX && header.encoding == Encoding.RAW3 && (header.alphaDepth == AlphaDepth.NoAlpha || header.alphaDepth == AlphaDepth.Alpha1Bit || header.alphaDepth == AlphaDepth.Alpha8Bit)) { // Uncompressed BGRA image ret.format = FileFormat.Raw; Marshal.Copy(imgData, 0, bmpData.Scan0, imgData.Length); } else if (header.type == DataType.Uncompressed_DirectX && header.encoding == Encoding.DXT && (header.alphaEncoding == AlphaEncoding.DXT1 || header.alphaEncoding == AlphaEncoding.DXT3 || header.alphaEncoding == AlphaEncoding.DXT5)) { // DXT1/3/5 ret.format = FileFormat.Dxt; byte[] decompImgData = new byte[header.width * header.height * 4]; LibSquish.DxtFormat flags = 0; switch (header.alphaEncoding) { case AlphaEncoding.DXT1: flags = LibSquish.DxtFormat.Dxt1; break; case AlphaEncoding.DXT3: flags = LibSquish.DxtFormat.Dxt3; break; case AlphaEncoding.DXT5: flags = LibSquish.DxtFormat.Dxt5; break; } LibSquish.DecompressImage(decompImgData, header.width, header.height, imgData, (int)flags); RemapRGBA(ref decompImgData); Marshal.Copy(decompImgData, 0, bmpData.Scan0, decompImgData.Length); } else if (header.type == DataType.JPEG) { ret.format = FileFormat.Jpeg; ret.bmp.UnlockBits(bmpData); throw new NotImplementedException(); } else { ret.bmp.UnlockBits(bmpData); throw new BlpFormatException("The given BLP2 format is not supported."); } ret.bmp.UnlockBits(bmpData); } else { try { // Let System.Drawing.Image handle loading ret.bmp = (Bitmap)Image.FromStream(stream); ret.format = FileFormat.Image; } catch (ArgumentException e) { throw new BlpFormatException("The stream did not contain valid image data.", e.InnerException); } } ret.header = header; return(ret); } }
/// <summary> /// Saves the currently loaded image to a Dxt encoded Blp file. /// </summary> /// <param name="filename">The path to the output file.</param> /// <param name="format">Dxt format. See <see cref="LibSquish.DxtFormat"/></param> /// <param name="resizeMethod">Method for possibly required resizing. See <see cref="FixSize"/></param> /// <param name="hasMipmaps">Specifies whether mipmaps are generated or not.</param> /// <param name="compression">Compression method used by LibSquish. Affects quality / speed. See <see cref="LibSquish.ColorCompression"/></param> public void Save(string filename, LibSquish.DxtFormat format, ResizeMethod resizeMethod, bool hasMipmaps, LibSquish.ColorCompression compression = LibSquish.ColorCompression.ClusterFit) { if (bmp != null) { FileStream fs = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.Write); using (BinaryWriter bw = new BinaryWriter(fs)) { Blp2Header header = new Blp2Header(DataType.Uncompressed_DirectX, Encoding.DXT, 0, 0, (byte)(hasMipmaps ? 1 : 0), 0, 0); List<Bitmap> bmps = new List<Bitmap>(); // Add resized image to the output bmps.Add(FixSize(resizeMethod)); header.width = (uint)bmps[0].Width; header.height = (uint)bmps[0].Height; // Add mipmaps if (hasMipmaps) bmps.AddRange(GenerateMipmaps(bmps[0])); byte[] compImgData = { }; byte[] colorPalette = new byte[256 * 4]; switch (format) { case LibSquish.DxtFormat.Dxt1: header.alphaEncoding = AlphaEncoding.DXT1; header.alphaDepth = AlphaDepth.Alpha1Bit; break; case LibSquish.DxtFormat.Dxt3: header.alphaEncoding = AlphaEncoding.DXT3; header.alphaDepth = AlphaDepth.Alpha8Bit; break; case LibSquish.DxtFormat.Dxt5: header.alphaEncoding = AlphaEncoding.DXT5; header.alphaDepth = AlphaDepth.Alpha8Bit; break; } for (int i = 0; i < bmps.Count; i++) { // Copy raw image data to byte array BitmapData bmpData = bmps[i].LockBits(new Rectangle(0, 0, bmps[i].Width, bmps[i].Height), ImageLockMode.ReadOnly, bmps[i].PixelFormat); byte[] imgData = new byte[bmpData.Stride * bmps[i].Height]; Marshal.Copy(bmpData.Scan0, imgData, 0, bmpData.Stride * bmps[i].Height); bmps[i].UnlockBits(bmpData); // Set up conversion flags int flags = (int)format | (int)compression | (int)LibSquish.ColorErrorMetric.Perceptual; // Compress to DXT format RemapRGBA(ref imgData); byte[] buff = new byte[LibSquish.GetStorageRequirements((uint)bmps[i].Width, (uint)bmps[i].Height, flags)]; LibSquish.CompressImage(imgData, (uint)bmps[i].Width, (uint)bmps[i].Height, buff, flags); Array.Resize(ref compImgData, compImgData.Length + buff.Length); buff.CopyTo(compImgData, compImgData.Length - buff.Length); // Image data locations header.offsets[i] = (i > 0) ? header.offsets[i - 1] + header.lengths[i - 1] : (uint)(Marshal.SizeOf(typeof(Blp2Header)) + colorPalette.Length); header.lengths[i] = (uint)buff.Length; } bw.Write(header.GetBytes()); bw.Write(colorPalette); bw.Write(compImgData); } } else { throw new BlpConversionException("No image data to convert."); } }