Search code examples
c#image-processingrgbgetpixellockbits

How do I correct this LockBits math error?


Using GetPixel/SetPixel, I was using the following to power an image filter that emphasizes reds and purples:

for (int y = 0; y < bmpMain.Height; y++)
    for (int x = 0; x < bmpMain.Width; x++)
    {
        bmpMain.GetPixel(x, y);
        Color c = bmpMain.GetPixel(x, y);
        int myRed = c.R, myGreen = c.G, myBlue = c.B;
        myGreen -= 128;
        if (myGreen < 0) myGreen = 0;
        bmpMain.SetPixel(x, y, Color.FromArgb(255, myRed, myGreen, myBlue));
    }

Using LockBits, I've replaced that with the following:

for (int counter = 1; counter < rgbValues.Length; counter += 3)
{
    rgbValues[counter] -= 128;
    if (rgbValues[counter] < 0) rgbValues[counter] = 0;  
}

But instead of the green pixel value being subtracted by 128, 128 is being added to the green value.

If I do this:

for (int counter = 1; counter < rgbValues.Length; counter += 3)
{
    rgbValues[counter] += 128;
    if (rgbValues[counter] < 0) rgbValues[counter] = 0;  
}

128 is also added to the green value. The resulting image is identical to the image where I subtracted 128.

So, how do I get what should be simplistic math to work properly inside LockBits?


Solution

  • Assuming that rgbValues is a byte array, the statement

    rgbValues[counter] -= 128;
    

    is equivalent to

    rgbValues[counter] = (byte)(rgbValues[counter] - 128);
    

    So if rgbValues[counter] equals zero, it gets set to (byte)(-128). The problem is that, unlike int, the byte data type is unsigned and cannot represent negative values. As EBrown notes, the subtraction overflows and wraps back to 128.

    One way to fix the code is to introduce an intermediate variable of type int so that you can safely accommodate negative values:

    int myGreen = rgbValues[counter];
    myGreen -= 128;
    if (myGreen < 0) myGreen = 0;  
    rgbValues[counter] = (byte)myGreen;
    

    Another way is to rewrite the code and avoid negative values in the first place:

    rgbValues[counter] = rgbValues[counter] > 128
        ? (byte)(rgbValues[counter] - 128)
        : (byte)0;