c-为什么在更改SDL_Surface的像素时会出现奇怪的伪影



所以我试图编辑SDL_Surface的每个像素以应用灰度公式。但当运行我的代码时,我得到了整个屏幕宽度和表面高度的一个区域,其中填充了奇怪的RGB条纹。

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);
}
}
}

我想这是字节在Uint8Uint32之间来回转换的问题,但我不知道确切的原因。我尝试将Uint8 *pixels作为Uint32 *传递,但它没有修复它,并导致了分段错误。

您有垂直条带,每行都排成一行(相对于阶梯式(。所以,我相信你对3的使用是正确的(即每个像素是3个字节(

这可能是由几件事引起的。

您可以在行地址中偏离一个字节。

您可以包装/截断像素颜色值(即,您需要饱和度数学(。也就是说,(例如(如果计算的灰度值为256,它将换行/截断为0。

我认为有一种更简单的方法可以索引到像素阵列中。

总之,这里有一些重构的代码。它还没有经过测试,但它应该会让你有所了解。

我假设格式是RGB(3字节/像素([与4字节/像素的RGBA相比],pitch是每行字节的数量。

#include <SDL2/SDL.h>
typedef unsigned char Uint8;
typedef unsigned int Uint32;
static inline Uint8
grayof(Uint8 r, Uint8 g, Uint8 b)
{
Uint8 gray;
#if ORIG
gray = 0.21 * r + 0.72 * g + 0.07 * b;
#else
Uint32 acc = 0;
// use scaled integer arithmetic (vs. float)
acc += 210 * (Uint32) r;
acc += 720 * (Uint32) g;
acc += 70 * (Uint32) b;
acc /= 1000;
// saturation math
// (e.g.) prevent pixel value of 256 from wrapping to 1
if (acc > 255)
acc = 255;
gray = acc;
#endif
return gray;
}
void
grayscale32(Uint8 *pixels, SDL_PixelFormat *format,
int width, int height, int pitch)
{
Uint8 *byteptr;
Uint32 *targetPixel;
Uint8 r, g, b, gray;
for (int y = 0;  y < height;  ++y) {
byteptr = &pixels[y * pitch];
targetPixel = (Uint32 *) byteptr;
for (int x = 0;  x < width;  ++x, ++targetPixel) {
SDL_GetRGB(*targetPixel, format, &r, &g, &b);
gray = grayof(r, g, b);
*targetPixel = SDL_MapRGB(format, gray, gray, gray);
}
}
}

如果没有更多信息[或原始图像],很难准确判断格式和几何图形。

虽然可能性较小,但这里有一个替代索引方案:

void
grayscale32(Uint8 *pixels, SDL_PixelFormat *format,
int width, int height, int pitch)
{
Uint8 *byteptr;
Uint32 *targetPixel;
Uint8 r, g, b, gray;
for (int y = 0;  y < height;  ++y) {
byteptr = &pixels[y * pitch];
for (int x = 0;  x < width;  ++x, byteptr += 3) {
targetPixel = (Uint32 *) byteptr;
SDL_GetRGB(*targetPixel, format, &r, &g, &b);
gray = grayof(r, g, b);
*targetPixel = SDL_MapRGB(format, gray, gray, gray);
}
}
}

更新:

我不认为这里真的需要饱和度检查,因为方程中的因子加起来是1,所以即使原始值为rgb(255255255(,我们也会得到255的灰度。

是的,你不需要sat数学是正确的。我很保守[因为我懒得检查因子值:-(]。

尽管使用了grayof函数,第一个仍然给了我条纹,但第二个本身运行得非常好。

好的,这意味着每个像素是3个字节[R/G/B]。我不确定它是否是R/G/B/a格式的4字节,其中a是alpha值。

但是,考虑到存储器格式是3字节/RGB,这就出现了targetPixelUint32 *的问题。

这是因为在执行*targetPixel = ...;时,它存储4字节,但循环增量是3。这意味着给定的存储正在将一个字节流血到下一个像素区域。

这看起来像:

Memory layout:
| 0       3       6       9
| R G B | R G B | R G B | R G B |
Store progression:
| 1 2 3 | 4
| 1 2 3 | 4
| 1 2 3 | 4
| 1 2 3 | 4

因此,有效地,第二个存储是而不是获得原始R

它看起来还可以,但我怀疑结果的灰度值有点偏离

这是一个可能解决问题的版本。如果R和B值似乎相反,则可能需要使用-DALT=1进行编译。尝试两种方式:有/没有。

#include <SDL2/SDL.h>
typedef unsigned char Uint8;
typedef unsigned int Uint32;
#ifndef ALT
#define ALT     0
#endif
enum {
#if ALT
OFF_R = 2,
OFF_G = 1,
OFF_B = 0,
#else
OFF_R = 0,
OFF_G = 1,
OFF_B = 2,
#endif
PIXBYTES = 3
};
static inline Uint8
grayof(Uint8 r, Uint8 g, Uint8 b)
{
Uint8 gray;
#if ORIG
gray = 0.21 * r + 0.72 * g + 0.07 * b;
#else
Uint32 acc = 0;
// use scaled integer arithmetic (vs. float)
acc += 210 * (Uint32) r;
acc += 720 * (Uint32) g;
acc += 70 * (Uint32) b;
acc /= 1000;
// saturation math
// (e.g.) prevent pixel value of 256 from wrapping to 1
#if SAT
if (acc > 255)
acc = 255;
#endif
gray = acc;
#endif
return gray;
}
void
grayscale32(Uint8 *pixels, SDL_PixelFormat *format,
int width, int height, int pitch)
{
Uint8 *byteptr;
Uint8 *bytelim;
Uint8 gray;
for (int y = 0;  y < height;  ++y) {
byteptr = &pixels[y * pitch];
bytelim = &byteptr[width * PIXBYTES];
for (;  byteptr < bytelim;  byteptr += PIXBYTES) {
gray = grayof(byteptr[OFF_R], byteptr[OFF_G], byteptr[OFF_B]);
byteptr[OFF_R] = gray;
byteptr[OFF_G] = gray;
byteptr[OFF_B] = gray;
}
}
}

最新更新