为什么更改SDL_Surface的像素时会出现怪异的伪影?

问题描述

因此,我尝试编辑SDL_Surface的每个像素以应用灰度公式。但是,在运行代码时,我得到的是整个屏幕的宽度和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);
        }
    }
}

我想在Uint8Uint32之间来回转换时是一个字节的问题,但是我不知道为什么。我尝试将Uint8 *pixels作为Uint32 *传递,但是它没有解决问题,并导致了分段错误

解决方法

您有垂直条带,并且每一行都排成一行(与楼梯拼合)。因此,我相信您对3的使用是正确的(即每个像素为3个字节)

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

您可能在行地址中少了一个字节。

您可能要包装/截断像素颜色值(即您需要饱和度数学)。也就是说,例如,如果您计算出的灰色灰度值为256,则它将自动将其包装/截断为0。

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

无论如何,这是一些重构的代码。它尚未经过测试,但应该可以给您一些帮助。

我假设格式为RGB(3个字节/像素)[vs RGBA具有4个字节/像素],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,b);

            *targetPixel = SDL_MapRGB(format,gray,gray);
        }
    }
}

没有更多信息[或原始图像],很难确切说明格式和几何形状。

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

void
grayscale32(Uint8 *pixels,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,gray);
        }
    }
}

更新:

由于方程中的因子加起来为1,因此我认为实际上并不需要进行饱和度检查,因此即使原始值为rgb(255,255,255),我们的灰度也为255。

是的,您不需要坐式数学是正确的。我很保守[因为我懒得检查因子值:-)]。

尽管使用grayof函数,第一个仍然给了我条纹,但是第二个单独工作得很好。

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

但是,鉴于内存格式为3字节/ RGB,这会带来一个问题,targetPixelUint32 *

那是因为在执行*targetPixel = ...;时,它存储了 4 个字节,但是循环增量为3。这意味着给定的存储区会将一个字节渗入 next 像素区域。

这看起来像:

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

因此,第二个商店实际上是 not 不能获得原始的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_B = 2,#endif

    PIXBYTES = 3
};

static inline Uint8
grayof(Uint8 r,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,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;
        }
    }
}