所以我试图编辑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);
}
}
}
我想这是字节在Uint8
和Uint32
之间来回转换的问题,但我不知道确切的原因。我尝试将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,这就出现了targetPixel
是Uint32 *
的问题。
这是因为在执行*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;
}
}
}