另存为 8 位 PNG

本文关键字:PNG 另存为 | 更新日期: 2023-09-27 18:31:32

我正在创建某些大小的占位符图像,这些图像将用作数据URI

Bitmap bitmap = new Bitmap(16, 10);

我已经做了一些研究,但找不到将此位图保存为最小文件大小的好方法,这就是我想要 8 位 PNG 的原因。

我的问题是:如何将此位图作为 8 位 PNG 保存到文件/字节数组/流中?有什么好的图书馆吗?

另存为 8 位 PNG

可以使用 nQuant 执行此操作(可以使用 nuget 安装,或参阅下面的参考资料)。下面的示例转换磁盘上的映像,并且很容易适应您的需求。

    public static bool TryNQuantify(string inputFilename, string outputFilename)
    {
        var quantizer = new nQuant.WuQuantizer();
        var bitmap = new Bitmap(inputFilename);
        if (bitmap.PixelFormat != System.Drawing.Imaging.PixelFormat.Format32bppArgb)
        {
            ConvertTo32bppAndDisposeOriginal(ref bitmap);
        }
        try
        {
            using (var quantized = quantizer.QuantizeImage(bitmap))
            {
                quantized.Save(outputFilename, System.Drawing.Imaging.ImageFormat.Png);
            }
        }
        catch
        {
            return false;
        }
        finally
        {
            bitmap.Dispose();
        }
        return true;
    }
    private static void ConvertTo32bppAndDisposeOriginal(ref Bitmap img)
    {
        var bmp = new Bitmap(img.Width, img.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
        using (var gr = Graphics.FromImage(bmp))
            gr.DrawImage(img, new Rectangle(0, 0, img.Width, img.Height));
        img.Dispose();
        img = bmp;
    }

有关详细信息,请参阅:

  • https://github.com/mcychan/nQuant.cs
  • https://code.msdn.microsoft.com/windowsdesktop/Convert-32-bit-PNGs-to-81ef8c81
我喜欢

FreeImage项目,它重量轻,易于使用。下面是创建透明 png 的示例。您可以轻松地将其包装在方法中,并设置宽度和高度以及透明度值。

//create a new bit map
FIBITMAP dib = new FIBITMAP();
//allocate a 16x10 bitmap with 8bit depth
dib = FreeImage.Allocate(16, 10, 8);
//set the transpareny
byte[] Transparency = new byte[1];
Transparency[0] = 0x00;
FreeImage.SetTransparencyTable(dib, Transparency);
//save the bitmap
FreeImage.Save(FREE_IMAGE_FORMAT.FIF_PNG, dib, "C:''temp''tp.png", FREE_IMAGE_SAVE_FLAGS.DEFAULT);

如果您只需要一个小的透明图像,为什么要停在 8 位?您可以直接下降到 1 位!反正你只需要一种颜色,它会更小。

事实上,你甚至不需要为此做任何特别的事情。由于新索引位图上的像素将全部默认为 0,这意味着它们引用其第一个调色板颜色,因此您需要做的就是制作一个新的 1bpp 图像,并将第一个调色板颜色设置为透明:

public static Bitmap MakePlaceholderImage(Int32 width, Int32 height)
{
    Bitmap bm = new Bitmap(width, height, PixelFormat.Format1bppIndexed);
    // This colour can't be assigned directly since the .Palette getter actually makes a copy of the palette.
    ColorPalette pal = bm.Palette;
    pal.Entries[0] = Color.Transparent;
    bm.Palette = pal;
    return bm;
}

我对此进行了一些实验,并保存为 png,最终结果似乎始终比使用 8bpp 执行的相同代码的结果小 8 倍。对于 5000x5000 的图像,作为 png 的文件大小仅略高于 3 KiB。

谷歌搜索将我带到了一个 6 年前的线程,我找不到我的解决方案。它也应该有可能6年,因为它使用了.Net Framework的System.Drawing Libraray。

此示例代码应该可以帮助任何想要编写 8 位索引 png 的人。

static class SaveImages
{
    public static void SaveGrayImage(Bitmap srcImg, string name)
    {
        Bitmap grayImg = new Bitmap(srcImg.Width, srcImg.Height, PixelFormat.Format8bppIndexed);
        var pal = grayImg.Palette;
        foreach (int i in Enumerable.Range(0, 256))
            pal.Entries[i] = Color.FromArgb(255, i, i, i);
        grayImg.Palette = pal;
        var data = grayImg.LockBits(new Rectangle(0, 0, srcImg.Width, srcImg.Height), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
        var bytes = new byte[data.Stride * data.Height];
        foreach (int y in Enumerable.Range(0, srcImg.Height))
        {
            foreach (int x in Enumerable.Range(0, srcImg.Width))
            {
                var colour = srcImg.GetPixel(x, y);
                var c = (int)colour.R + (int)colour.G + (int)colour.B;
                c /= 3;
                bytes[data.Stride * y + x] = (byte)c;
            }
        }
        Marshal.Copy(bytes, 0, data.Scan0, bytes.Length);
        grayImg.Save(name, ImageFormat.Png);
    }
}

编辑:使用调色板添加透明度。

1) 使用每像素 8 位格式创建位图:

Bitmap bmp = new Bitmap(20, 20, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);

有关更多格式,请参阅 PixelFormat。

2)绘制到位图...

3)然后将其另存为PNG:

bmp.Save(@"c:'temp'xyz.png", System.Drawing.Imaging.ImageFormat.Png);

创建的文件将具有与bmp相同的像素格式(8 位)