问题
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.
回答1:
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.
回答2:
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.
来源:https://stackoverflow.com/questions/4356973/easiest-way-to-combine-several-png8-images-in-net