Can't set palette in bitmap

2019-01-23 23:43发布

问题:

I have been writing a PCX decoder and, so far, the PCX image itself parses fine, but I can't work out how to set the palette of a bitmap.

I have created a bitmap like so:

Bitmap bmp = new Bitmap(width,
                        height,
                        stride2,
                        System.Drawing.Imaging.PixelFormat.Format8bppIndexed,
                        pixels);

But I can't seem to set the palette using the following method:

for (int i = 0; i < 256; i += 3)
{
    Color b = new Color();
    b = Color.FromArgb(palette[i], palette[i + 1], palette[i + 2]);
    bmp.Palette.Entries.SetValue(b, i);
}

In this example, I have read through each byte in the palette of the pcx file and stored them in palette[]. from there, I have used this to set the entries in the palette of the bitmap. How do I set the colours?

回答1:

This had me confused too. It seems bitmap.Palette returns a clone of the bitmap's palette. Once you've modified your copy, you need to reset the bitmap's pallete by using bitmap.Palette = palette, e.g.

ColorPalette palette = bitmap.Palette;
Color entries = palette.Entries;
....
entries[i] = new Color(...);
....
bitmap.Palette = palette; // The crucial statement

See http://www.charlespetzold.com/pwcs/PaletteChange.html



回答2:

According to Microsoft Reference Source, Palette property of Image class in .net, internally uses GDI+ Flat APIs for handling palettes. GdipGetImagePalette used for initializing ColorPalette object in get method and GdipSetImagePalette used for writing ColorPalette object data back to device is set method.

Each time in your for loop the line bmp.Palette.Entries.SetValue(b, i); forces the Image to call GdipGetImagePalette and data of bmp.Palette reinitialized and therefore you can see no change has been made to bmp.Palette after the loop.

To solve this problem you must do the following:

  1. Assign new alias to bmp.Palette by assigning it to a variable,
  2. Modify it by new alias (this prevents reloading),
  3. And put it back to the bmp.Palette.

Code:

var newAliasForPalette = bmp.Palette; // Palette loaded from graphic device
for (int i = 0; i < 256; i++)
{
    newAliasForPalette.Entries[i] = myColor[i];
}
bmp.Palette = newAliasForPalette; // Palette data wrote back to the graphic device

In my opinion, replacement of definition of Palette as a property with GetPalette() and SetPalette() methods by Microsoft, will be a great help in avoiding confusion.