So I am trying to edit every pixels of an SDL_Surface
to apply the grayscaling formula. But when running my code I get an area of the width of my entire screen and the height of the surface filled with weird RGB stripes.
void grayscale32(Uint8 *pixels, SDL_PixelFormat *format, int width, int height,
int pitch) {
Uint32 *targetPixel = NULL;
Uint8 r, g, b, gray;
for (int y = 0; y * pitch < height * width * 3; y++) {
for (int x = 0; x < pitch / 4; x++) {
targetPixel = pixels + y * pitch + x * sizeof(*targetPixel);
SDL_GetRGB(*targetPixel, format, &r, &g, &b);
gray = 0.21 * r + 0.72 * g + 0.07 * b;
*targetPixel = SDL_MapRGB(format, gray, gray, gray);
}
}
}
I suppose it's a matter of Byte when converting between Uint8
and Uint32
back and forth but i don't know exactly why. I tried passing Uint8 *pixels
as a Uint32 *
but it didn't fix it and caused a segmentation fault.
You have vertical banding and each row lines up (vs. stairstepping). So, I believe that your use of
3
is correct (i.e. the pixels are 3 bytes each)This can/could be caused by a few things.
You could be off by one byte in the row address.
You could be wrapping/truncating pixel color values (i.e. you need saturation math). That is, (e.g.) if you have a calculated gray value of 256, it will wrap/truncate to 0.
I think there's a simpler way to index into the pixel array.
Anyway, here's some refactored code. It's not been tested but it should give you something to look at.
I'm assuming the format is RGB (3 bytes / pixel) [vs RGBA with 4 bytes / pixel] and
pitch
is the number of bytes per row.Without more information [or the original image], it's difficult to tell exactly what the format and geometry are.
Although less likely, here's an alternate indexing scheme:
UPDATE:
Yes, you are correct about not needing the sat math. I was being conservative [because I was too lazy to check the factor values :-)].
Okay, this means that each pixel is 3 bytes [R/G/B]. I wasn't sure whether it was 4 bytes with a format of R/G/B/A where A is the alpha value.
But, given that the memory format is 3 byte/RGB this presents an issue with
targetPixel
beingUint32 *
.That's because when doing
*targetPixel = ...;
, it's storing 4 bytes but the loop increment is3
. This means that a given store is bleeding one byte into the next pixel area.This would look something like:
So, effectively the second store is not getting the original
R
valueIt may seem/look okay, but I suspect that the resultant gray values are a bit off.
Here's a version that may fix the problem. You may need to compile with
-DALT=1
if the R and B values seem to be reversed. Try it both ways: with/without.