1

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?

Michael Liu
  • 52,147
  • 13
  • 117
  • 150
MaoTseTongue
  • 71
  • 2
  • 8
  • That's because the values are a `byte` type. When you subtract `128` or add `128` the result is the same, as the overflow will wrap back to the original. (`0 + 128 = 128`, `0 - 128 = 256 - 128 = 128`) The actual values in the image are `bytes` (regardless of what type `int` you store them as). – Der Kommissar Jun 02 '15 at 02:26
  • Okay, that explains the problem, thank you. As for a solution, how do I subtract 128? Is there some type of conversion or cast required for the 128? I've never run across this before. Actually, just tried casting (byte) 128 and Convert.ToByte(128), and as I'm sure you already know, niether had any effect. What is the correct proceedure? I'm obviously completely new to this. Thank you – MaoTseTongue Jun 02 '15 at 03:55
  • `rgbValues[counter] -= (rgbValues[counter] > 128 ? 128 : rgbValues[counter]);` That should do what you wish. – Der Kommissar Jun 02 '15 at 11:44

1 Answers1

1

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;  
Michael Liu
  • 52,147
  • 13
  • 117
  • 150
  • I've been away due to work/family issues, I'm sorry about the delay in responding / acknowledging. Thank you very much, this works perfectly! – MaoTseTongue Jun 06 '15 at 04:29
  • I ran into a curious problem: the code doesn't work after resizing an image. Resizing and using GetPixel/SetPixel works fine. But when I resize using LockBits, instead of Reds and Purples, I get a series of diagonal lines through the image. Any ideas as to what would cause that to happen, or should I post a new question if I can't troubleshoot the issue? Thank you – MaoTseTongue Jun 06 '15 at 05:16
  • Okay, my resize function is changing the image from 24 bit to 32 bit, that didn't matter with GetPixel/SetPixel, but it does matter with Lockbits. I need to go back and read up on how to check for 24 bit images vs 32 bit images, or just convert to 32 bit before applying the filter loop. – MaoTseTongue Jun 06 '15 at 06:14
  • You should post a new question since that sounds like a different problem. – Michael Liu Jun 06 '15 at 14:37