none
How to set pixel into bitmap with Pixel format of Format8bppIndexed ?

    Question

  • I define new bitmap in my code 

    Bitmap image = new Bitmap( width, height, PixelFormat.Format8bppIndexed);

    ==> that mean that this bitmap is gray scale color. 

    Now, i want to define what color will be at place (i, j) so i usually use ( on 24 color bit ) the method 'SetPixel( i, j, Color.FromRGB( ... ) )'

    But in this case that the bitmap is on gray scale i cant do it because i have only one value of the color ... and the application crash all the time when i try to 
    call Color.FromRGB(theOneColorIhave)

    Someone know other way to do set the pixel values - some way that can be use with 8 bit format ? 

    Thanks
    Wednesday, November 03, 2010 2:37 PM

Answers

  • For changing pixels in an image with an indexed format, you need to change the bytes constituting the image. You cannot use SetPixel.

    // For comparison
    Color colorBefore = image.GetPixel(5, 5);
    
    BitmapData data = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
    
    // Copy the bytes from the image into a byte array
    byte[] 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
    
    // Copy the bytes from the byte array into the image
    Marshal.Copy(bytes, 0, data.Scan0, bytes.Length);
    
    image.UnlockBits(data);
    
    // You can check the pixel has been changed
    Color colorAfter = image.GetPixel(5, 5);
    
    Console.WriteLine("Pixel changed from {0} to {1}.", colorBefore, colorAfter);
    
    Wednesday, November 03, 2010 3:22 PM
  • Everything you need to know about indexed GDI+ images in a nutshell:

        private void button1_Click(object sender, EventArgs e)
        {
          
    int width = 256;
          
    int height = 256;
          
    int stride = 4 * ((width * 8 + 31) / 32);
          
    byte[,] b = new byte[height, stride];
          
    GCHandle gch = GCHandle.Alloc(b, GCHandleType.Pinned);
          
    Bitmap bmp = new Bitmap(width,
                                  height,
                                  stride,
                                  
    PixelFormat.Format8bppIndexed,
                                  gch.AddrOfPinnedObject());
          
    ColorPalette pal = bmp.Palette;
          
    for (int i = 0; i < 256; i++)
            pal.Entries[i] = 
    Color.FromArgb(255, i, i, i);
          bmp.Palette = pal;
          
    for (int y = 0; y < height; y++)
            
    for (int x = 0; x < width; x++)
              b[y, x] = (
    byte)y;
          gch.Free();
        }

    8bppIndexed GetPixel and SetPixel functions:

        Color Get8bppIndexedPixel(Bitmap bmp, int x, int y)
        {
          
    BitmapData bmpData = bmp.LockBits(new Rectangle(x, y, 1, 1),
                                            
    ImageLockMode.ReadOnly,
                                            bmp.PixelFormat);
          
    Color c = bmp.Palette.Entries[Marshal.ReadByte(bmpData.Scan0)];
          bmp.UnlockBits(bmpData);
          
    return c;
        }
        
    void Set8bppIndexedPixel(Bitmap bmp, int x, int y, Color c)
        {
          
    int i;
          
    int min = int.MaxValue;
          
    int minI = 0;
          
    for (i = 0; i < 256; i++)
          {
            
    Color cPal = bmp.Palette.Entries[i];
            
    int r = c.R - cPal.R;
            
    int g = c.G - cPal.G;
            
    int b = c.B - cPal.B;
            
    int d = r * r + g * g + b * b;
            
    if (d == 0) break;
            
    if (d < min)
            {
              min = d;
              minI = i;
            }
          }
          
    if (i == 256) i = minI;
          
    BitmapData bmpData = bmp.LockBits(new Rectangle(x, y, 1, 1),
                                            
    ImageLockMode.ReadOnly,
                                            bmp.PixelFormat);
          
    Marshal.WriteByte(bmpData.Scan0, (byte)i);
          bmp.UnlockBits(bmpData);
        }

    Wednesday, November 03, 2010 7:13 PM

All replies

  • For changing pixels in an image with an indexed format, you need to change the bytes constituting the image. You cannot use SetPixel.

    // For comparison
    Color colorBefore = image.GetPixel(5, 5);
    
    BitmapData data = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
    
    // Copy the bytes from the image into a byte array
    byte[] 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
    
    // Copy the bytes from the byte array into the image
    Marshal.Copy(bytes, 0, data.Scan0, bytes.Length);
    
    image.UnlockBits(data);
    
    // You can check the pixel has been changed
    Color colorAfter = image.GetPixel(5, 5);
    
    Console.WriteLine("Pixel changed from {0} to {1}.", colorBefore, colorAfter);
    
    Wednesday, November 03, 2010 3:22 PM
  • I define new bitmap in my code 

    Bitmap image = new Bitmap( width, height, PixelFormat.Format8bppIndexed);

    ==> that mean that this bitmap is gray scale color. 
    How did you make the gray scale palette?  The default palette for Format8bppIndexed isn't gray scale.
    Wednesday, November 03, 2010 6:46 PM
  • Everything you need to know about indexed GDI+ images in a nutshell:

        private void button1_Click(object sender, EventArgs e)
        {
          
    int width = 256;
          
    int height = 256;
          
    int stride = 4 * ((width * 8 + 31) / 32);
          
    byte[,] b = new byte[height, stride];
          
    GCHandle gch = GCHandle.Alloc(b, GCHandleType.Pinned);
          
    Bitmap bmp = new Bitmap(width,
                                  height,
                                  stride,
                                  
    PixelFormat.Format8bppIndexed,
                                  gch.AddrOfPinnedObject());
          
    ColorPalette pal = bmp.Palette;
          
    for (int i = 0; i < 256; i++)
            pal.Entries[i] = 
    Color.FromArgb(255, i, i, i);
          bmp.Palette = pal;
          
    for (int y = 0; y < height; y++)
            
    for (int x = 0; x < width; x++)
              b[y, x] = (
    byte)y;
          gch.Free();
        }

    8bppIndexed GetPixel and SetPixel functions:

        Color Get8bppIndexedPixel(Bitmap bmp, int x, int y)
        {
          
    BitmapData bmpData = bmp.LockBits(new Rectangle(x, y, 1, 1),
                                            
    ImageLockMode.ReadOnly,
                                            bmp.PixelFormat);
          
    Color c = bmp.Palette.Entries[Marshal.ReadByte(bmpData.Scan0)];
          bmp.UnlockBits(bmpData);
          
    return c;
        }
        
    void Set8bppIndexedPixel(Bitmap bmp, int x, int y, Color c)
        {
          
    int i;
          
    int min = int.MaxValue;
          
    int minI = 0;
          
    for (i = 0; i < 256; i++)
          {
            
    Color cPal = bmp.Palette.Entries[i];
            
    int r = c.R - cPal.R;
            
    int g = c.G - cPal.G;
            
    int b = c.B - cPal.B;
            
    int d = r * r + g * g + b * b;
            
    if (d == 0) break;
            
    if (d < min)
            {
              min = d;
              minI = i;
            }
          }
          
    if (i == 256) i = minI;
          
    BitmapData bmpData = bmp.LockBits(new Rectangle(x, y, 1, 1),
                                            
    ImageLockMode.ReadOnly,
                                            bmp.PixelFormat);
          
    Marshal.WriteByte(bmpData.Scan0, (byte)i);
          bmp.UnlockBits(bmpData);
        }

    Wednesday, November 03, 2010 7:13 PM
  • Hi Louis

    I want to have the bytes of an image in a simple 2-D array or 3-D array.

    What should I do?

    Thanks


    New Ideas, Superior Solutions Www.SalamGroup.Net
    Thursday, March 10, 2011 5:16 AM
  • For a 2D array, you need a loop.

      byte[,] b2d = new byte[data.Height, data.Width];
      for (int i = 0; i < data.Height; i++)
      {
        for (int j = 0; j < data.Width; j++)
        {
          b2d[i, j] = Marshal.ReadByte(data.Scan0, i * data.Stride + j);
        }
      }
    

    For a 3D array, you'd need a 3rd criterion beyond x and y.

    Thursday, March 10, 2011 10:18 AM