Beispiel #1
0
        /// <summary>
        /// Saves the currently loaded image to a uncompressed or paletted Blp file.
        /// </summary>
        /// <param name="filename">The path to the output file.</param>
        /// <param name="format">Raw format. See <see cref="RawFormat"/></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="samplingFactor">(Optional) Sampling factor 1-30. Lower values stand for higher quality to the disadvantage of speed. Does not apply to Raw3 encoding.</param>
        /// <param name="samplingFactor">(Optional) Specifies whether or not to use dithering. Does not apply to Raw3 encoding.</param>
        public void Save(string filename, RawFormat format, ResizeMethod resizeMethod, bool hasMipmaps, byte samplingFactor = 10, bool dither = false)
        {
            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, 0, AlphaDepth.Alpha8Bit, AlphaEncoding.RAW8BIT, (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];

                    if (format == RawFormat.Raw1)
                    {
                        // Uncompressed paletted image
                        if (samplingFactor < 1 || samplingFactor > 30)
                        {
                            throw new ArgumentException("Sampling factor must be between 1 and 30.");
                        }
                        header.encoding = Encoding.RAW1;

                        // Quantize colors to a 256 color palette
                        NeuQuant quant = new NeuQuant(bmps[0]);
                        quant.Quantize();

                        // Set color palette
                        int j = 0;
                        foreach (Color c in quant.Palette)
                        {
                            colorPalette[j]     = c.R;
                            colorPalette[j + 1] = c.G;
                            colorPalette[j + 2] = c.B;
                            colorPalette[j + 3] = 0;
                            j += 4;
                        }

                        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);

                            byte[] alphaData = new byte[imgData.Length / 4];

                            // Index image colors
                            int curOffset = compImgData.Length;
                            Array.Resize(ref compImgData, compImgData.Length + imgData.Length / 4);

                            for (j = 0; j < imgData.Length; j += 4)
                            {
                                Color c = Color.FromArgb(imgData[j], imgData[j + 1], imgData[j + 2]);
                                compImgData[curOffset + j / 4] = (byte)quant.GetPaletteIndex(c);

                                // Floyd-Steinberg dithering
                                if (dither)
                                {
                                    // Quantization error
                                    int diffErrR = c.R - quant.Palette[compImgData[curOffset + j / 4]].R;
                                    int diffErrG = c.G - quant.Palette[compImgData[curOffset + j / 4]].G;
                                    int diffErrB = c.B - quant.Palette[compImgData[curOffset + j / 4]].B;

                                    // Diffuse error, distribute  to neighbour bytes as follows:
                                    //  X      P     7/16
                                    // 3/16   5/16   1/16
                                    if (bmpData.Stride - (j % bmpData.Stride) >= 8)
                                    {
                                        imgData[j + 4] = truncateByte(imgData[j + 4], (diffErrR * 7) >> 4);
                                        imgData[j + 5] = truncateByte(imgData[j + 5], (diffErrG * 7) >> 4);
                                        imgData[j + 6] = truncateByte(imgData[j + 6], (diffErrB * 7) >> 4);
                                    }
                                    else if (j + bmpData.Stride < imgData.Length)
                                    {
                                        imgData[j + bmpData.Stride]     = truncateByte(imgData[j + bmpData.Stride], (diffErrR * 5) >> 4);
                                        imgData[j + bmpData.Stride + 1] = truncateByte(imgData[j + bmpData.Stride + 1], (diffErrG * 5) >> 4);
                                        imgData[j + bmpData.Stride + 2] = truncateByte(imgData[j + bmpData.Stride + 2], (diffErrB * 5) >> 4);

                                        if (bmpData.Stride - (j % bmpData.Stride) >= 8)
                                        {
                                            imgData[j + bmpData.Stride + 4] = truncateByte(imgData[j + bmpData.Stride + 4], (diffErrR * 1) >> 4);
                                            imgData[j + bmpData.Stride + 5] = truncateByte(imgData[j + bmpData.Stride + 5], (diffErrG * 1) >> 4);
                                            imgData[j + bmpData.Stride + 6] = truncateByte(imgData[j + bmpData.Stride + 6], (diffErrB * 1) >> 4);
                                        }
                                        else if (j % bmpData.Stride >= 4)
                                        {
                                            imgData[j + bmpData.Stride - 4] = truncateByte(imgData[j + bmpData.Stride - 4], (diffErrR * 3) >> 4);
                                            imgData[j + bmpData.Stride - 3] = truncateByte(imgData[j + bmpData.Stride - 3], (diffErrG * 3) >> 4);
                                            imgData[j + bmpData.Stride - 2] = truncateByte(imgData[j + bmpData.Stride - 2], (diffErrB * 3) >> 4);
                                        }
                                    }
                                }

                                // Fill alpha array (8 bit)
                                alphaData[j / 4] = imgData[j + 3];
                            }

                            // Append alpha channels
                            Array.Resize(ref compImgData, compImgData.Length + alphaData.Length);
                            alphaData.CopyTo(compImgData, compImgData.Length - alphaData.Length);

                            // Image data locations
                            header.offsets[i] = (uint)(curOffset + Marshal.SizeOf(typeof(Blp2Header)) + colorPalette.Length);
                            header.lengths[i] = (uint)(compImgData.Length - ((i > 0) ? header.offsets[i - 1] : 0));
                        }
                    }
                    else if (format == RawFormat.Raw3)
                    {
                        // Uncompressed 32bpp image
                        header.encoding = Encoding.RAW3;

                        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);

                            Array.Resize(ref compImgData, compImgData.Length + imgData.Length);
                            imgData.CopyTo(compImgData, compImgData.Length - imgData.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)imgData.Length;
                        }
                    }

                    bw.Write(header.GetBytes());
                    bw.Write(colorPalette);
                    bw.Write(compImgData);
                }
            }
            else
            {
                throw new BlpConversionException("No image data to convert.");
            }
        }
Beispiel #2
0
        /// <summary>
        /// Saves the currently loaded image to a uncompressed or paletted Blp file.
        /// </summary>
        /// <param name="filename">The path to the output file.</param>
        /// <param name="format">Raw format. See <see cref="RawFormat"/></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="samplingFactor">(Optional) Sampling factor 1-30. Lower values stand for higher quality to the disadvantage of speed. Does not apply to Raw3 encoding.</param>
        /// <param name="samplingFactor">(Optional) Specifies whether or not to use dithering. Does not apply to Raw3 encoding.</param>
        public void Save(string filename, RawFormat format, ResizeMethod resizeMethod, bool hasMipmaps, byte samplingFactor = 10, bool dither = false)
        {
            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, 0, AlphaDepth.Alpha8Bit, AlphaEncoding.RAW8BIT, (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];

                    if (format == RawFormat.Raw1) {
                        // Uncompressed paletted image
                        if (samplingFactor < 1 || samplingFactor > 30) throw new ArgumentException("Sampling factor must be between 1 and 30.");
                        header.encoding = Encoding.RAW1;

                        // Quantize colors to a 256 color palette
                        NeuQuant quant = new NeuQuant(bmps[0]);
                        quant.Quantize();

                        // Set color palette
                        int j = 0;
                        foreach (Color c in quant.Palette) {
                            colorPalette[j] = c.R;
                            colorPalette[j + 1] = c.G;
                            colorPalette[j + 2] = c.B;
                            colorPalette[j + 3] = 0;
                            j += 4;
                        }

                        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);

                            byte[] alphaData = new byte[imgData.Length / 4];

                            // Index image colors
                            int curOffset = compImgData.Length;
                            Array.Resize(ref compImgData, compImgData.Length + imgData.Length / 4);

                            for (j = 0; j < imgData.Length; j += 4) {
                                Color c = Color.FromArgb(imgData[j], imgData[j + 1], imgData[j + 2]);
                                compImgData[curOffset + j / 4] = (byte)quant.GetPaletteIndex(c);

                                // Floyd-Steinberg dithering
                                if (dither) {
                                    // Quantization error
                                    int diffErrR = c.R - quant.Palette[compImgData[curOffset + j / 4]].R;
                                    int diffErrG = c.G - quant.Palette[compImgData[curOffset + j / 4]].G;
                                    int diffErrB = c.B - quant.Palette[compImgData[curOffset + j / 4]].B;

                                    // Diffuse error, distribute  to neighbour bytes as follows:
                                    //  X      P     7/16
                                    // 3/16   5/16   1/16
                                    if (bmpData.Stride - (j % bmpData.Stride) >= 8) {
                                        imgData[j + 4] = truncateByte(imgData[j + 4], (diffErrR * 7) >> 4);
                                        imgData[j + 5] = truncateByte(imgData[j + 5], (diffErrG * 7) >> 4);
                                        imgData[j + 6] = truncateByte(imgData[j + 6], (diffErrB * 7) >> 4);
                                    } else if (j + bmpData.Stride < imgData.Length) {
                                        imgData[j + bmpData.Stride] = truncateByte(imgData[j + bmpData.Stride], (diffErrR * 5) >> 4);
                                        imgData[j + bmpData.Stride + 1] = truncateByte(imgData[j + bmpData.Stride + 1], (diffErrG * 5) >> 4);
                                        imgData[j + bmpData.Stride + 2] = truncateByte(imgData[j + bmpData.Stride + 2], (diffErrB * 5) >> 4);

                                        if (bmpData.Stride - (j % bmpData.Stride) >= 8) {
                                            imgData[j + bmpData.Stride + 4] = truncateByte(imgData[j + bmpData.Stride + 4], (diffErrR * 1) >> 4);
                                            imgData[j + bmpData.Stride + 5] = truncateByte(imgData[j + bmpData.Stride + 5], (diffErrG * 1) >> 4);
                                            imgData[j + bmpData.Stride + 6] = truncateByte(imgData[j + bmpData.Stride + 6], (diffErrB * 1) >> 4);
                                        } else if (j % bmpData.Stride >= 4) {
                                            imgData[j + bmpData.Stride - 4] = truncateByte(imgData[j + bmpData.Stride - 4], (diffErrR * 3) >> 4);
                                            imgData[j + bmpData.Stride - 3] = truncateByte(imgData[j + bmpData.Stride - 3], (diffErrG * 3) >> 4);
                                            imgData[j + bmpData.Stride - 2] = truncateByte(imgData[j + bmpData.Stride - 2], (diffErrB * 3) >> 4);
                                        }
                                    }
                                }

                                // Fill alpha array (8 bit)
                                alphaData[j / 4] = imgData[j + 3];
                            }

                            // Append alpha channels
                            Array.Resize(ref compImgData, compImgData.Length + alphaData.Length);
                            alphaData.CopyTo(compImgData, compImgData.Length - alphaData.Length);

                            // Image data locations
                            header.offsets[i] = (uint)(curOffset + Marshal.SizeOf(typeof(Blp2Header)) + colorPalette.Length);
                            header.lengths[i] = (uint)(compImgData.Length - ((i > 0) ? header.offsets[i - 1] : 0));
                        }

                    } else if (format == RawFormat.Raw3) {

                        // Uncompressed 32bpp image
                        header.encoding = Encoding.RAW3;

                        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);

                            Array.Resize(ref compImgData, compImgData.Length + imgData.Length);
                            imgData.CopyTo(compImgData, compImgData.Length - imgData.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)imgData.Length;
                        }

                    }

                    bw.Write(header.GetBytes());
                    bw.Write(colorPalette);
                    bw.Write(compImgData);

                }

            } else {
                throw new BlpConversionException("No image data to convert.");
            }
        }