在 .NET 中组合多个 PNG8 图像的最简单方法

发布于 2024-10-05 16:26:01 字数 350 浏览 4 评论 0原文

我正在尝试在 C# 中将一堆 8 位 PNG 图像组合成一个更大的 PNG 图像。奇怪的是,这似乎特别困难。

由于图形不支持索引颜色,因此您不能使用它,因此我尝试构建非索引位图(使用图形)并将其转换为索引颜色位图。转换很好,但我不知道如何设置输出图像的调色板。它默认为一些预定义的调色板,与我正在寻找的内容几乎没有关系。

那么:

有没有办法控制位图调色板?或者是否有另一种方法(例如 System.Windows.Media.Imaging.WriteableBitmap)可以支持此功能?

回复:WriteableBitmap:我似乎无法在网上找到任何关于如何在这种情况下组合 PNG 的示例,或者即使尝试它有任何意义。

I'm trying to combine a bunch of 8-bit PNG images into a larger PNG image in C#. Oddly enough, this seems to be particularly difficult.

Since Graphics does not support indexed color, you can't use that, so I tried building a non-indexed Bitmap (using Graphics) and converting that to indexed color bitmap. The conversion is fine, but I can't figure out how to set the palette of the output image. It defaults to some pre-defined palette which has little to do with what I'm looking for.

So:

Is there a way to control the bitmap palette? Or is there another method (System.Windows.Media.Imaging.WriteableBitmap, for example) which can support this?

Re: WriteableBitmap: I can't seem to find any examples online of how one would go about combining PNGs in this context, or even if it makes any sense to attempt it.

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

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

发布评论

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

评论(2

可可 2024-10-12 16:26:01

事实证明,我能够构建一个非索引位图并使用 PngBitmapEncoder 进行转换,如下所示:

    byte[] ConvertTo8bpp(Bitmap sourceBitmap)
    {
        // generate a custom palette for the bitmap (I already had a list of colors
        // from a previous operation
        Dictionary<System.Drawing.Color, byte> colorDict = new Dictionary<System.Drawing.Color, byte>(); // lookup table for conversion to indexed color
        List<System.Windows.Media.Color> colorList = new List<System.Windows.Media.Color>(); // list for palette creation
        byte index = 0;
        unchecked
        {
            foreach (var cc in ColorsFromPreviousOperation)
            {
                colorDict[cc] = index++;
                colorList.Add(cc.ToMediaColor());
            }
        }
        System.Windows.Media.Imaging.BitmapPalette bmpPal = new System.Windows.Media.Imaging.BitmapPalette(colorList);

        // create the byte array of raw image data
        int width = sourceBitmap.Width;
        int height = sourceBitmap.Height;
        int stride = sourceBitmap.Width;
        byte[] imageData = new byte[width * height];

        for (int x = 0; x < width; ++x)
            for (int y = 0; y < height; ++y)
            {
                var pixelColor = sourceBitmap.GetPixel(x, y);
                imageData[x + (stride * y)] = colorDict[pixelColor];
            }

        // generate the image source
        var bsource = BitmapSource.Create(width, height, 96, 96, PixelFormats.Indexed8, bmpPal, imageData, stride);

        // encode the image
        PngBitmapEncoder encoder = new PngBitmapEncoder();
        encoder.Interlace = PngInterlaceOption.Off;
        encoder.Frames.Add(BitmapFrame.Create(bsource));

        MemoryStream outputStream = new MemoryStream();
        encoder.Save(outputStream);

        return outputStream.ToArray();
    }

加上辅助扩展方法:

    public static System.Windows.Media.Color ToMediaColor(this System.Drawing.Color color)
    {
        return new System.Windows.Media.Color()
        {
            A = color.A,
            R = color.R,
            G = color.G,
            B = color.B
        };
    }

请注意:PngBitmapEncoder 实际上似乎可以将 bpp 计数从 8 减少到 4。例如,当我使用 6 种颜色进行测试时,输出 PNG 仅是 4 位。当我使用色彩更丰富的图像时,它是 8 位的。到目前为止看起来像是一个功能......不过如果我能明确地控制它那就太好了。

It turns out that I was able to build out a non-indexed bitmap and convert using PngBitmapEncoder like so:

    byte[] ConvertTo8bpp(Bitmap sourceBitmap)
    {
        // generate a custom palette for the bitmap (I already had a list of colors
        // from a previous operation
        Dictionary<System.Drawing.Color, byte> colorDict = new Dictionary<System.Drawing.Color, byte>(); // lookup table for conversion to indexed color
        List<System.Windows.Media.Color> colorList = new List<System.Windows.Media.Color>(); // list for palette creation
        byte index = 0;
        unchecked
        {
            foreach (var cc in ColorsFromPreviousOperation)
            {
                colorDict[cc] = index++;
                colorList.Add(cc.ToMediaColor());
            }
        }
        System.Windows.Media.Imaging.BitmapPalette bmpPal = new System.Windows.Media.Imaging.BitmapPalette(colorList);

        // create the byte array of raw image data
        int width = sourceBitmap.Width;
        int height = sourceBitmap.Height;
        int stride = sourceBitmap.Width;
        byte[] imageData = new byte[width * height];

        for (int x = 0; x < width; ++x)
            for (int y = 0; y < height; ++y)
            {
                var pixelColor = sourceBitmap.GetPixel(x, y);
                imageData[x + (stride * y)] = colorDict[pixelColor];
            }

        // generate the image source
        var bsource = BitmapSource.Create(width, height, 96, 96, PixelFormats.Indexed8, bmpPal, imageData, stride);

        // encode the image
        PngBitmapEncoder encoder = new PngBitmapEncoder();
        encoder.Interlace = PngInterlaceOption.Off;
        encoder.Frames.Add(BitmapFrame.Create(bsource));

        MemoryStream outputStream = new MemoryStream();
        encoder.Save(outputStream);

        return outputStream.ToArray();
    }

Plus the helper extension method:

    public static System.Windows.Media.Color ToMediaColor(this System.Drawing.Color color)
    {
        return new System.Windows.Media.Color()
        {
            A = color.A,
            R = color.R,
            G = color.G,
            B = color.B
        };
    }

Note for the wary: the PngBitmapEncoder actually seems to reduce the bpp count from 8 to 4 when it can. When I test with 6 colors, for example, the output PNG is only 4-bit. When I use a more color-rich image, it's 8-bit. Looks like a feature so far... though it would be nice if I had explicit control over it.

还如梦归 2024-10-12 16:26:01

免责声明,我在 Atalasoft 工作。

我们的产品 DotImage Photo 是免费的,并且可以做到这一点。

读取 PNG

 AtalaImage img = new AtalaImage("image.png");

转换为 24 bpp

 img = img.GetChangedPixelFormat(newPixelFormat);

创建所需尺寸的图像

 AtalaImage img2 = new AtalaImage(width, height, color);

使用 OverlayCommand 将 img 覆盖到 img2 上

 OverlayCommand cmd = new OverlayCommand(img);
 cmd.Apply(img2, point);

保存

 img2.Save("new.png", new PngEncoder(), null);

如果您需要帮助,请对此答案发表评论或发表论坛条目。

Disclaimer, I work for Atalasoft.

Our product, DotImage Photo, is free and can do this.

To read a PNG

 AtalaImage img = new AtalaImage("image.png");

To convert to 24 bpp

 img = img.GetChangedPixelFormat(newPixelFormat);

Create an image of the size you want

 AtalaImage img2 = new AtalaImage(width, height, color);

use OverlayCommand to overlay img onto img2

 OverlayCommand cmd = new OverlayCommand(img);
 cmd.Apply(img2, point);

To save

 img2.Save("new.png", new PngEncoder(), null);

If you need help either comment on this answer or make a forum entry.

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