1

I'm making a tool that does some graphical editing to some bitmaps. I have some 8bppIndexed bitmaps with an existing color palette that I have to convert to 24bppRgb to allow me to draw on it and edit colors of pixels based on some options the user selected. Bitmap graphic is the original 8bpp .bmp file:

//Get Bitmap from file
Bitmap graphic = new Bitmap(source);

//Get palette and pixel format
ColorPalette pal = graphic.Palette;
PixelFormat pix = graphic.PixelFormat; //Format8bppIndexed

//Create datagraphic in 24bppRgb to allow for editing
PixelFormat datapix = PixelFormat.Format24bppRgb;
Bitmap datagraphic = graphic.Clone(new Rectangle(0, 0, graphic.Width, graphic.Height), datapix);

After drawing and editing Bitmap datagraphic, I'm trying to convert it back to 8bppIndexed with the same color palette as Bitmap graphic. I can do that like this:

//Convert back to graphic pixel format
datagraphic = datagraphic.Clone(new Rectangle(0, 0, datagraphic.Width, datagraphic.Height), pix); //Format8bppIndexed

//Apply color palette
datagraphic.Palette = pal;

However, after cloning datagraphic with the same pixel format as graphic, it creates an entirely new color palette. The colors in datagraphic are then all incorrect after applying ColorPalette pal. They only match by their index number between the two palettes. Is there another way of doing this that preserves the colors?

And yes, the bitmaps need to be 8bppIndexed with the custom palette. I'm trying to avoid the need to go through Photoshop to change the color index of all the end result data graphics with the correct palette.

I should say that I'm still fairly new to C#. I hope I was clear as to what I'm trying to do. I appreciate any help here. Thanks in advance.

Tevious
  • 13
  • 4
  • var bmp = new Bitmap(img); That gets you a 32bpp bitmap from the 8bpp *img*. Easy peasy. Converting that back to 8bpp is rocket science that you should always, always avoid. You could save it in the GIF format. – Hans Passant Nov 25 '17 at 14:30
  • Converting the bitmap back to 8bpp with the same color palette is what I'm after, though. Unfortunately, I cannot find any way to get the colors to match the original palette. Cloning the bitmap to 8bpp creates it's own color palette and the color indexes are different, so when I apply the correct palette the colors are at the wrong index color. I suppose I'll have to leave it at 24bpp and just convert the bitmaps back to 8bpp with the correct palette in Photoshop using dithering. – Tevious Nov 26 '17 at 01:47
  • If your image indeed contains 256 colours or less, you can just go over all pixels, rebuild your palette, and then reduce the image to that palette. The original order of the colours will be completely ruined though. There's no way around that except supplying the original colour palette to the conversion method. – Nyerguds Jan 22 '18 at 21:42
  • That said, if you just need to build an 8-bit image from a high colour image and a colour palette... I know how to do _that_. – Nyerguds Jan 22 '18 at 21:44
  • What I'm trying to do is convert a high color image to an 8-bit image using a custom color palette so the colors match the nearest color index in the custom palette. Basically, the same process as Photoshop if you go to Image > Mode > Indexed Color and load a custom palette to the image. – Tevious Jan 23 '18 at 23:08
  • @Tevious Did you try the solution given in my answer? If it helped you, please do [accept the solution](https://stackoverflow.com/help/someone-answers). – Nyerguds Feb 14 '18 at 20:55
  • Sorry, I've just been on vacation for the last couple weeks and had been busy with other work since I originally asked the question. I haven't had the time yet to look into it. I appreciate your answer and will try it out as soon as possible. – Tevious Feb 15 '18 at 22:21
  • @Nyerguds Just want to thank you for your answer. I finally got around to trying your solution today and also added a load color palette option to go with it. This will really help out with our little mod tool (for an old game). Again, apologies for the late response! – Tevious Mar 08 '18 at 13:39
  • No problem. I wrote most of my image manipulation code [for a modding suite of my own](http://www.oldgamesitalia.net/forum/index.php?showtopic=24162), to help out a group of Italian translation enthusiasts interested in old games. Feel free to check out the code; it's all released under WTFPL :) – Nyerguds Mar 08 '18 at 15:08
  • Just curious... are you dealing with actual proprietary in-house graphics formats, then, or just with paletted images of common types? – Nyerguds Mar 08 '18 at 15:13
  • The game uses paletted bitmap graphics that are then assembled into a proprietary format that includes positional information and can contain multiple bitmaps (for animations). – Tevious Mar 09 '18 at 21:39

1 Answers1

0

Converting an 8-bit image to 24bpp is trivial: you just paint it on a new image of the same dimensions and the preferred pixel format.

public static Bitmap PaintOn24bpp(Image image)
{
    Bitmap bp = new Bitmap(image.Width, image.Height, PixelFormat.Format24bppRgb);
    using (Graphics gr = Graphics.FromImage(bp))
        gr.DrawImage(image, new Rectangle(0, 0, bp.Width, bp.Height));
    return bp;
}

This works with any source image no matter its pixel format, though for images containing transparency you may want to preprocess them to fill any transparent areas, because I don't know if painting with transparency taken into account will work if the image you're drawing on doesn't support alpha.

The reverse, to convert a high-colour image to indexed, is a lot trickier, but if you have the palette, it is certainly possible. The main problem in this case is that using Graphics doesn't work, since indexed graphics don't have coloured pixels; they have a coloured palette, and pixels that just reference those colours.

Editing 8-bit data generally boils down to building a byte array containing the image data, and converting that to an image with the palette slapped onto it. The actual conversion is done by making a new 8bpp Bitmap, opening its backing byte array with the LockBits function, and copying your new data into it with Marshal.Copy.

After that, the final missing link is a method to match your image's coloured pixels to their closest palette match, so you can then take the array of resulting matches and bake it into an 8bpp image. This is generally done by calculating the Pythagorean distance (in 3D RGB colour space) of your colour to each of the palette colours, and then taking the index of the colour for which that distance is the smallest.

The whole process, from start to end, is explained in detail in this answer:

A: How to convert a colored image to a image that has only two predefined colors?

This deals with 2 colours, but the method for dealing with 2 or with 256 colours is completely identical, since the end result in both cases was an 8bpp image.

It could be optimised for dealing with 24bpp images directly instead of converting the input to 32bpp, but that's probably not worth the effort, especially since the method posted there works with absolutely any input.

Nyerguds
  • 5,360
  • 1
  • 31
  • 63