优化 .NET 中 System.Drawing 的 PNG 输出

发布于 2024-08-18 05:18:49 字数 2426 浏览 4 评论 0原文

我有一个例程,它读取图像,调整其大小并将其放置在新背景上(全新的位图,只是设置了大小)。

这一切都工作得非常完美,但现在我想减小 PNG 文件的大小输出 - 如果我输出 JPEG 文件,我会得到我期望的 ~4K 左右的文件大小,但我的 PNG 文件大小超过 30K。

我知道我永远无法用 PNG 达到 JPEG 水平,但我认为我可以做得更好一点。

当我将输出的 PNG 加载到 Fireworks 中时,我注意到背景和调整大小的图像仍然是单独的层。在 Fireworks 中压平 PNG 可将文件大小减少 5 到 10K。

那么,首先有没有一种方法可以在输出时以编程方式压平 PNG?

其次,还有什么其他人可以推荐来减小 PNG 大小吗?

我使用 PNG 文件,因为我希望将背景保留为透明。

代码:

private static void ResizeImage(String ImageInPath, int MaxWidth, int MaxHeight, String ImageOutPath, Boolean PadImage, Color MyColour)
{
    Bitmap MyImage = new Bitmap(ImageInPath);
    Bitmap MyResizedImage = null;

    int XPosition = 0;
    int YPosition = 0;
    float Ratio = MyImage.Width / (float)MyImage.Height;

    int MyImageHeight = MyImage.Height;
    int MyImageWidth = MyImage.Width;

    if (MyImage.Width > MyImage.Height)
    {
        if (MyImage.Width > MaxWidth)
            MyResizedImage = new Bitmap(MyImage, new Size(MaxWidth, (int)Math.Round(MaxWidth /
            Ratio, 0)));
        YPosition = (MaxHeight / 2) - (MyResizedImage.Height / 2);
    }
    else if (MyImage.Height > MyImage.Width)
    {
        if (MyImage.Height > MaxHeight)
            MyResizedImage = new Bitmap(MyImage, new Size((int)Math.Round(MaxWidth * Ratio,
            0), MaxHeight));
        XPosition = (MaxWidth / 2) - (MyResizedImage.Width / 2);
    }


    if (PadImage)
    {
        Bitmap MyUnderlay = new Bitmap(MaxWidth, MaxHeight);
        var Canvas = Graphics.FromImage(MyUnderlay);
        Canvas.Clear(MyColour);
        Canvas.InterpolationMode = InterpolationMode.HighQualityBicubic;
        Canvas.DrawImage(MyResizedImage, XPosition, YPosition);
        Canvas.Save();
        if (MyColour == Color.Transparent)
        {
            MyUnderlay.Save(ImageOutPath + ".png", ImageFormat.Png);
        }
        else
        {
            MyUnderlay.Save(ImageOutPath, ImageFormat.Jpeg);
        }

        Canvas.Dispose();
        MyUnderlay.Dispose();
    }
    else
    {
        MyResizedImage.Save(ImageOutPath, ImageFormat.Jpeg);
    }

    MyResizedImage.Dispose();
    MyImage.Dispose();
}

I have a routine which reads in an image, resizes it and positions it on a new background (brand new bitmap, just the size is set).

This all works absolutely perfectly, but now I would like to reduce the size of the PNG files it outputs - if I output JPEG files, I get the ~4K or so file size I expect, but my PNG files are more than 30K in size.

I know that I will never get to JPEG levels with PNG, but I think I can do a bit better.

When I load the outputted PNGs into Fireworks, I note that the background and the resized image are still seperate layers. Flattening the PNG in Fireworks reduces the filesize by a good 5 to 10K.

So, firstly is there a way to programmatically flatten the PNG on output?

Secondly, is there anything else anyone can recommend to reduce the PNG size?

I am using PNG files, because I wish to retain the background as transparent.

Code:

private static void ResizeImage(String ImageInPath, int MaxWidth, int MaxHeight, String ImageOutPath, Boolean PadImage, Color MyColour)
{
    Bitmap MyImage = new Bitmap(ImageInPath);
    Bitmap MyResizedImage = null;

    int XPosition = 0;
    int YPosition = 0;
    float Ratio = MyImage.Width / (float)MyImage.Height;

    int MyImageHeight = MyImage.Height;
    int MyImageWidth = MyImage.Width;

    if (MyImage.Width > MyImage.Height)
    {
        if (MyImage.Width > MaxWidth)
            MyResizedImage = new Bitmap(MyImage, new Size(MaxWidth, (int)Math.Round(MaxWidth /
            Ratio, 0)));
        YPosition = (MaxHeight / 2) - (MyResizedImage.Height / 2);
    }
    else if (MyImage.Height > MyImage.Width)
    {
        if (MyImage.Height > MaxHeight)
            MyResizedImage = new Bitmap(MyImage, new Size((int)Math.Round(MaxWidth * Ratio,
            0), MaxHeight));
        XPosition = (MaxWidth / 2) - (MyResizedImage.Width / 2);
    }


    if (PadImage)
    {
        Bitmap MyUnderlay = new Bitmap(MaxWidth, MaxHeight);
        var Canvas = Graphics.FromImage(MyUnderlay);
        Canvas.Clear(MyColour);
        Canvas.InterpolationMode = InterpolationMode.HighQualityBicubic;
        Canvas.DrawImage(MyResizedImage, XPosition, YPosition);
        Canvas.Save();
        if (MyColour == Color.Transparent)
        {
            MyUnderlay.Save(ImageOutPath + ".png", ImageFormat.Png);
        }
        else
        {
            MyUnderlay.Save(ImageOutPath, ImageFormat.Jpeg);
        }

        Canvas.Dispose();
        MyUnderlay.Dispose();
    }
    else
    {
        MyResizedImage.Save(ImageOutPath, ImageFormat.Jpeg);
    }

    MyResizedImage.Dispose();
    MyImage.Dispose();
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

听风吹 2024-08-25 05:18:49

超级用户的答案,如何阅读没有 Fireworks 的 Fireworks PNG 多层文件,解释 Fireworks 具有 PNG 扩展名,可以在 PNG 文件中存储多个图层。展平去除多余的层。鉴于这些文件最初不是在 Fireworks 中创建的,理论上“扁平化”应该对它们没有影响。我怀疑尺寸的减小是由于 Firework 对 PNG 文件进行了保存优化。

我用于 PNG 优化的工具是:

  1. 转换为 PNG8:如果颜色很少(比如说屏幕截图)然后我使用 pngnqGIMP 的 索引颜色模式可量化至 256 种颜色。 PNG8 可以小于 PNG24 或 PNG32。有关详细信息,请参阅PNG8 – 明显的获胜者
  2. Optipng,一个快速通用 PNG 优化器。 C# PNG 优化教程详细介绍了如何从 C# 运行 optipng
  3. 最后, pngout 很慢,但通常(80-90% 的时间)设法挤压PNG 比 optpng 进一步下降。不过,首先运行 optipng,因为 optipng 会自动执行 pngout 不会尝试的其他优化。

如果您对优化 PNG 文件感兴趣,PNG 规范 非常简单,而且值得一看尽可能小的透明 PNG。与规范一样,实现也非常简单,例如单个文件,纯 Python 实现 png.py (pypng)< /a>.

The Super User answer, How to read Fireworks PNG multilayer files without Fireworks, explains that Fireworks has PNG extensions to store multiple layers in a PNG file. Flattening removes the extra layers. Given that the files were not created in Fireworks in the first place, in theory "flattening" should have no effect on them. I suspect the reduction in size is due to Firework's save-optimize the PNG file.

The tools I use for PNG optimization are:

  1. Convert to PNG8: If there are few colors (say, screenshots) then I use pngnq or GIMP's Indexed color mode to quantize down to 256 colors. PNG8 can be smaller than PNG24 or PNG32. For details see PNG8 – The Clear Winner.
  2. Optipng, a fast general PNG optimizer. C# PNG Optimization Tutorial has details on how to run optipng from C#.
  3. Finally, pngout is slow but often (80-90% of the time) manages to squeeze the PNG down further than optipng. Run optipng first though, as optipng will automatically do other optimizations that pngout does not attempt.

If you are interested in optimizing PNG files, the PNG specification is surprisingly straightforward, and it is worth taking a look at Smallest possible transparent PNG. Like the specification, implementations are also surprisingly simple, take for example, the single file, pure Python implementation png.py (pypng).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文