Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why i'm getting exception when using SetPixel: SetPixel is not supported for images with indexed pixel formats?

Tags:

c#

.net

winforms

In the top of a class i'm doing:

private static Bitmap bmp2 = new Bitmap(@"C:\Temp\New folder (17)\radar001486.GIF");

Then inside a method i'm doing:

private void test()
   {
    int current_list_length = pointtocolor.Count;
                for (int kk=0;kk<current_list_length;kk++)
                {

                    PointF pt = pointtocolor[kk];
                    e.FillEllipse(cloudColors[cloudColorIndex], pt.X * (float)currentFactor, pt.Y * (float)currentFactor, radius, radius);
                    bmp2.SetPixel((int)pt.X * (int)currentFactor, (int)pt.Y * (int)currentFactor, Color.Yellow);

                }
                bmp2.Save(@"c:\temp\yellowbmpcolor.bmp");
   }

Once it's getting inside the loop it's making the exception on the line:

bmp2.SetPixel((int)pt.X * (int)currentFactor, (int)pt.Y * (int)currentFactor, Color.Yellow);

If i will change the instance of bmp2 from:

private static Bitmap bmp2 = new Bitmap(@"C:\Temp\New folder (17)\radar001486.GIF");

To

private static Bitmap bmp2 = new Bitmap(512,512);

Then it will work but i want to SetPixel the pixels over the original radar001486.GIF and not on a new empty Bitmap.

like image 354
James Aharon Avatar asked Sep 22 '14 23:09

James Aharon


2 Answers

The problem is that you are using a GIF as it has indexed pixels. Try converting it to a png if you can; or if you can't, turn it into a non-indexed image using:

public Bitmap CreateNonIndexedImage(Image src)
{
    Bitmap newBmp = new Bitmap(src.Width, src.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

    using (Graphics gfx = Graphics.FromImage(newBmp)) {
        gfx.DrawImage(src, 0, 0);
    }

    return newBmp;
}

Note: if you have the option to do so (i.e. non-downloaded images, or you have access to the server), definitely convert those images to PNG instead.

like image 194
Kanadaj Avatar answered Oct 22 '22 10:10

Kanadaj


The image you are trying to change is an indexed GIF. This means that the image doesn't contain a series of pixels with their respective color values (as your new Bitmap does); rather, it contains a color palette combined with a series of pixels with their index values into the palette. The pixel format of your image loaded from disk is probably something like Format8bppIndexed.

You cannot use SetPixel on this kind of image, because SetPixel wants to set the R, G, and B values for the pixel directly. That's not how an indexed image works.

To change this kind of image you have a couple of options:

  • Your best bet is to use WPF, which has a GifBitmapEncoder and GifBitmapDecoder. This lets you decode the GIF data into something WPF can draw on, then convert it back. Since this uses DirectX and not GDI+ it doesn't have the limitations of things like SetPixel. I'd really, really suggest you go this route if you can, but if not:

  • Use GDI+ to Convert the image into a non-indexed type of image, change it, and convert it back. This is generally a terrible idea: GDI+ and indexed formats don't get along, and that includes encoding a bitmap as an indexed GIF. The image quality is likely to be terrible.

  • Edit the byte data directly. For this, you need to extract the GIF data into an array and set the pixels to the correct indexed values. The trick here is figuring out the right index value; or, if there happens to be an empty one in the palette, you can just add another one. You'll need to dig deep into the GIF format to figure this out, though it's likely to be the most efficient in terms of both space and speed without reducing the image quality. Once you know what index value to write, you do something like this:

    var data = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), 
        ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
    
    var bytes = new byte[data.Height * data.Stride];
    Marshal.Copy(data.Scan0, bytes, 0, bytes.Length);
    
    bytes[5 * data.Stride + 5] = 1; // Set the pixel at (5, 5) to the color #1
    
    Marshal.Copy(bytes, 0, data.Scan0, bytes.Length);
    
    image.UnlockBits(data);
    
like image 43
Michael Edenfield Avatar answered Oct 22 '22 10:10

Michael Edenfield