Konrad
Konrad

Reputation: 7208

Picking the color from bitmap using Lockbits

I'm trying to make color picker using LockBits so when I move the cursor over picturebox it shows color located at cursor position. Approach with GetPixel works however I'm interested how to do this using LockBits.

My try, unfortunately shows white all the time:

void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
    Bitmap bmp = new Bitmap(pictureBox1.Image);
    // we will try to get the pixel using raw data and make color from it
    BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat);

    // default format is 32bpp argb ( 4 bytes per pixel)
    unsafe
    {
        byte* scanline = (byte*)data.Scan0;
        for(int y = 0; y < data.Height; y++)
        {
            // row
            for (int x = 0; x < data.Width; x+=4)
            {
                 int r = scanline[x];
                 int g = scanline[x+1];
                 int b = scanline[x+2];
                 //int a = scanline[x+3];
                 Color color = Color.FromArgb(255, r, g, b);

                 pictureBox2.BackColor = color;

             }
         }
    }

    bmp.UnlockBits(data);

    //Color color = bmp.GetPixel(e.X, e.Y);

}

Upvotes: 0

Views: 1903

Answers (1)

TaW
TaW

Reputation: 54433

Here is the solution..:

unsafe Color getPixel(Bitmap bmp, int x, int y)
{
    BitmapData bmData = bmp.LockBits( new Rectangle(0, 0, bmp.Width, bmp.Height),
                        System.Drawing.Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat);

    // not a complete check, but a start on how to use different pixelformats
    int pixWidth = bmp.PixelFormat == PixelFormat.Format24bppRgb ? 3 :
                   bmp.PixelFormat == PixelFormat.Format32bppArgb ? 4 : 4;

    IntPtr scan0 = bmData.Scan0;
    int stride = bmData.Stride;
    byte* p = (byte*)scan0.ToPointer() + y * stride;
    int px = x * pixWidth;
    byte alpha = (byte) (pixWidth == 4 ? p[px + 3] : 255);
    Color color  = Color.FromArgb(alpha , p[px + 2], p[px + 1], p[px + 0]);
    bmp.UnlockBits(bmData);
    return color;
}

This is how you could call it:

private void panel1_MouseClick(object sender, MouseEventArgs e)
{
    panel2.BackColor = getPixel((Bitmap)panel1.BackgroundImage, e.X, e.Y);
}

Of course you can use any sort of Bitmap source, like Label.Image ot PictureBox.Image...

The color channels are called ARGB but actually ordered BGRA.

Note that stride is the physical width of a bitmap pixel row, including possible etra bytes to fill to a multiple of 4 bytes.

Also note that other solutions using lockbit work without pointers..

As noted in the comments above this is actually slower than using GetPixel, since setting up the unsafe access vector eats up any gain, even when reading out several pixels.

Upvotes: 1

Related Questions