Question

I am getting error:

"A Graphics object cannot be created from an image that has an indexed pixel format."

in function:

public static void AdjustImage(ImageAttributes imageAttributes, Image image)
{
        Rectangle rect = new Rectangle(0, 0, image.Width, image.Height);

        Graphics g = Graphics.FromImage(image);       
        g.InterpolationMode = InterpolationMode.HighQualityBicubic;
        g.DrawImage(image, rect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, imageAttributes);
        g.Dispose();
}

I would like to ask you, how can I fix it?

Was it helpful?

Solution

Refering to this, it can be solved by creating a blank bitmap with the same dimensions and the correct PixelFormat and the draw on that bitmap.

// The original bitmap with the wrong pixel format. 
// You can check the pixel format with originalBmp.PixelFormat
Bitmap originalBmp = new (Bitmap)Image.FromFile("YourFileName.gif");

// Create a blank bitmap with the same dimensions
Bitmap tempBitmap = new Bitmap(originalBmp.Width, originalBmp.Height);

// From this bitmap, the graphics can be obtained, because it has the right PixelFormat
using(Graphics g = Graphics.FromImage(tempBitmap))
{
    // Draw the original bitmap onto the graphics of the new bitmap
    g.DrawImage(originalBmp, 0, 0);
    // Use g to do whatever you like
    g.DrawLine(...);
}

// Use tempBitmap as you would have used originalBmp
return tempBitmap;

OTHER TIPS

The simplest way is to create a new image like this:

Bitmap EditableImg = new Bitmap(IndexedImg);

It creates a new image exactly like the original was with all its contents.

Overall, if you want to work with indexed images and actually preserve their colour depth and palette, this will always mean writing explicit checks and special code for them. Graphics simply can't work with them, because it manipulates colours, and the actual pixels of indexed images contain no colours, just indices.

For anyone still seeing this all these years later... the valid way to paint an image onto an existing (8-bit) indexed image is this:

  • Go over all the pixels of the image you want to paste and, for each colour, find the closest match on the target image's colour palette, and save its index into a byte array.
  • Open the backing bytes array of the indexed image using LockBits, and paste your matched bytes onto it, at the desired location, by looping over the relevant indices using the height and image stride.

It's not an easy task, but it's certainly possible. If the pasted image is also indexed, and contains more than 256 pixels, you can speed up the process by doing the colour matching on the palette instead of on the actual image data, then getting the backing bytes from the other indexed image, and remapping them using the created mapping.

Note that all of this only applies to eight bit. If your image is four-bit or one-bit, the simplest way to handle it is to convert it to 8-bit first so you can handle it as one byte per pixel, and convert it back afterwards.

For more information on that, see How can I work with 1-bit and 4-bit images?

Though the accepted answer works, it creates a new 32bpp ARGB image from the indexed bitmap.

To manipulate indexed bitmaps directly you can use this library (alert: shameless self promotion). Its GetReadWriteBitmapData extension allows creating a writable managed accessor even for indexed pixel formats.

And then you can use one of the DrawInto methods that can be used similarly to Graphics.DrawImage. Of course, as the target bitmap is indexed, the drawing operation must quantize the pixels using the target palette colors but there are a sort of overloads that can use dithering to preserve more image details.

Usage example (see more examples in the links above):

using (IReadWriteBitmapData indexedTarget = myIndexedBitmap.GetReadWriteBitmapData())
using (IReadableBitmapData source = someTrueColorBitmap.GetReadableBitmapData())
{
    // or DrawIntoAsync if you want to use async-await
    source.DrawInto(indexedTarget, targetRect, OrderedDitherer.Bayer8x8);
}

Image examples:

All images below had been created with PixelFormat.Format8bppIndexed format with the default palette, and a 256x256 icon and an alpha gradient rainbow were drawn on top of each other. Note that blending is used as much as possible with the available palette.

Image Description
8bpp alpha gradient drawn on icon without dithering No dithering
8bpp alpha gradient drawn on icon with ordered dithering Ordered Bayer8x8 dithering
8bpp alpha gradient drawn on icon with error diffusion dithering Floyd-Steinberg error diffusion dithering

Disclaimer: Of course, the library has also some limitations compared to Graphics, for example there are no shape-drawing methods. But in worst case you still can use the accepted answer, and then call the ConvertPixelFormat method in the end if you need to produce an indexed result.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top