The easiest way to combine multiple PNG8 images in .NET.

I am trying to combine a bunch of 8-bit PNG images with a large PNG image in C #. Oddly enough, this seems especially complicated.

Since Graphics does not support indexed color, you cannot use it, so I tried to create an unindexed Bitmap (using Graphics) and convert it to an indexed bitmap. The conversion is fine, but I cannot figure out how to set the palette of the output image. By default, some predefined palette is used, 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 (e.g. System.Windows.Media.Imaging.WriteableBitmap) that can support this?

Re: WriteableBitmap: I cannot find any examples online about how PNG could be combined in this context, or even if it makes sense to try it.

+4
source share
2 answers

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

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 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 caution: PngBitmapEncoder actually seems to reduce bpp from 8 to 4 when possible. When I test 6 colors, for example, the PNG output is only 4-bit. When I use a more color-rich image, it is 8-bit. It seems like the function is for now ... although it would be nice if I had explicit control over it.

0
source

Disclaimer, I work at Atalasoft.

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

To read PNG

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

To convert to 24 bpp

  img = img.GetChangedPixelFormat(newPixelFormat); 

Create an image of the desired size

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

use OverlayCommand to overlay img on 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 post in the forum.

0
source

All Articles