将文件保存为8位PNG格式

4

我正在创建特定尺寸的占位图像,将用作数据URI

Bitmap bitmap = new Bitmap(16, 10);

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

我的问题是:如何将此位图保存为文件/字节数组/流成为一个8位PNG?有哪些好的库可以使用?


Leadtools拥有一个很好的库来处理位图。 - Omar.Alani
但是你为什么需要它呢?它们会是看不见的按钮,之后你会在它们下面放置图片吗? - Vajura
哦,所以你是动态放置<img>对象?你不能在其中没有实际图像的情况下强制设置大小吗? - Vajura
不行,因为它必须是响应式的。宽度=100%; 高度=自动; - OMGKurtNilsen
啊,抱歉,我想象错了。那么,如果这是你唯一的解决方案,对于任何宽度/高度的透明PNG,你可以使用Photoshop将其设置为绝对最小的大小。 - Vajura
显示剩余5条评论
5个回答

2
你可以使用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;
    }

更多信息请参见:


1
这对我来说很有效,但需要注意的是,当我处理高度大于4550像素的图像时,它无法生成图像,输出结果是一个空的灰色图像。 - BornToCode

0

我喜欢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);

我尝试使用FreeImage。通过nuget添加了它,但在我的项目中找不到它。 - OMGKurtNilsen
试着用老式的方法。从http://freeimage.sourceforge.net/download.html下载,将freeimagenet.dll添加为引用,并将freeimage.dll复制到您的构建文件夹中。 - Kevin

0

我通过谷歌搜索找到了一个6年前的帖子,但是我找不到我的解决方案。这个问题在6年前应该已经有解决方法了,因为它使用了.Net Framework的System.Drawing库。

以下示例代码可以帮助任何想要编写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);
    }
}

编辑:要增加透明度,请尝试使用调色板。


0

如果你只需要一个小的透明图像,为什么要止步于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。


-1

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

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

请参阅像素格式以获取更多格式信息。

2) 绘制到位图...

3) 然后将其保存为PNG:

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

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


似乎这不支持透明度,并且还会创建非常大的文件大小。 - OMGKurtNilsen
1
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - joe
这里列出的所有选项都不支持透明度。透明度对于我的占位图像非常重要。 - OMGKurtNilsen
1
PNG和GIF都支持带索引的图像透明度。您必须确保位图的调色板中包含透明颜色。 - Berin Loritsch
在这个解释中,第二步会有问题;标准函数无法绘制8位图像。您需要使用LockBits访问/提取后备数组以进行操作。 - Nyerguds

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接