SDL 1.2中Blit透明Surface时RGB通道异常问题求助
SDL 1.2中Alpha通道Blit失效,RGB意外绘制的问题解决
问题重现
使用SDL 1.2-master版本开发字体系统(每个字形对应一个Surface)时,将Surface的alpha通道设为0(完全透明)后执行Blit操作,原本预期RGB通道内容不会被绘制到屏幕,但实际RGB颜色仍被显示,导致透明效果失效。相关代码如下:
#include <SDL/SDL.h> #include <stdio.h> #include <stdbool.h> #include <unistd.h> int main(int argc, char** argv) { if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) == -1) { fprintf(stderr, "Couldn't initialize SDL: %s.\n", SDL_GetError()); exit(-1); } SDL_Rect** modes = SDL_ListModes(NULL, SDL_FULLSCREEN|SDL_HWSURFACE); if(!modes) { printf(":(\n"); return 1; } SDL_Surface* screen = SDL_SetVideoMode(modes[0]->w, modes[0]->h, 0, SDL_FULLSCREEN | SDL_HWSURFACE | SDL_DOUBLEBUF); if(!screen) { printf(":(\n"); return 1; } SDL_Surface* surface = SDL_CreateRGBSurface(SDL_HWSURFACE|SDL_SRCALPHA, 32, 32, 32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff); if(!surface) { printf(":((\n"); return 1; } for(int i=0; i!=32; i++) { for(int j=0; j!=32; j++) { // 0 blue // 1 green // 2 red // 3 alpha? ((char*)(surface->pixels))[0+i*4*32+j*4] = 0xff; ((char*)(surface->pixels))[1+i*4*32+j*4] = 0xff; ((char*)(surface->pixels))[2+i*4*32+j*4] = 0xff; ((char*)(surface->pixels))[3+i*4*32+j*4] = 0xff; // SDL_OPAQUE_ALPHA is 0xFF // not an alpha channel } } int ret = SDL_BlitSurface(surface, NULL, screen, NULL); SDL_Flip(screen); sleep(2); for(int i=0; i!=32; i++) { for(int j=0; j!=32; j++) { // 0 blue // 1 green // 2 red // 3 alpha? ((char*)(surface->pixels))[0+i*4*32+j*4] = 0x30; ((char*)(surface->pixels))[1+i*4*32+j*4] = 0x40; ((char*)(surface->pixels))[2+i*4*32+j*4] = 0x50; ((char*)(surface->pixels))[3+i*4*32+j*4] = 0x00;// This surface should be transparent, SDL_TRANSPARENT_ALPHA is 0 // not an alpha channel } } ret = SDL_BlitSurface(surface, NULL, screen, NULL); while(1) { SDL_Flip(screen); //if the color is anything but pure white, alpha channel modification failed. } }
问题原因
- Alpha混合未启用:SDL 1.2中仅在创建Surface时指定
SDL_SRCALPHA并不足以开启alpha混合,需要显式设置alpha模式,确保Blit时系统会根据alpha通道值进行像素混合。 - 像素索引计算错误:直接使用
i*4*32计算行偏移是错误的,SDL Surface的行字节数由pitch字段决定(考虑内存字节对齐),硬编码宽度*像素字节数会导致像素地址偏移错误,可能未正确修改到alpha通道。 - 硬件Surface直接访问风险:使用
SDL_HWSURFACE创建的Surface,其像素内存可能位于显存中,直接访问前未调用SDL_LockSurface锁定,会导致内存访问异常或修改不生效。
修复方案
1. 启用Alpha混合模式
在Blit前调用SDL_SetAlpha,确保源Surface的alpha通道生效:
SDL_SetAlpha(surface, SDL_SRCALPHA, 0);
该函数会启用源Surface的alpha混合,Blit时系统会根据每个像素的alpha值计算最终颜色。
2. 使用Surface Pitch计算像素偏移
用surface->pitch代替硬编码的4*32,确保行偏移正确:
// 计算像素起始地址 Uint8* pixel = (Uint8*)surface->pixels + i * surface->pitch + j * 4; pixel[0] = 0x30; // 蓝 pixel[1] = 0x40; // 绿 pixel[2] = 0x50; // 红 pixel[3] = 0x00; // Alpha
3. 安全访问硬件Surface像素
访问硬件Surface的像素前,必须调用SDL_LockSurface锁定内存,操作完成后调用SDL_UnlockSurface解锁:
if (SDL_LockSurface(surface) < 0) { fprintf(stderr, "Lock surface failed: %s\n", SDL_GetError()); return 1; } // 这里执行像素修改操作 SDL_UnlockSurface(surface);
修改后的完整代码
#include <SDL/SDL.h> #include <stdio.h> #include <unistd.h> int main(int argc, char** argv) { if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) == -1) { fprintf(stderr, "Couldn't initialize SDL: %s.\n", SDL_GetError()); exit(-1); } SDL_Rect** modes = SDL_ListModes(NULL, SDL_FULLSCREEN|SDL_HWSURFACE); if(!modes) { printf(":(\n"); return 1; } SDL_Surface* screen = SDL_SetVideoMode(modes[0]->w, modes[0]->h, 0, SDL_FULLSCREEN | SDL_HWSURFACE | SDL_DOUBLEBUF); if(!screen) { printf(":(\n"); return 1; } SDL_Surface* surface = SDL_CreateRGBSurface(SDL_HWSURFACE|SDL_SRCALPHA, 32, 32, 32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff); if(!surface) { printf(":((\n"); return 1; } // 锁定Surface后修改像素 if (SDL_LockSurface(surface) < 0) { fprintf(stderr, "Lock surface failed: %s\n", SDL_GetError()); return 1; } for(int i=0; i<32; i++) { for(int j=0; j<32; j++) { Uint8* pixel = (Uint8*)surface->pixels + i * surface->pitch + j * 4; pixel[0] = 0xff; // 蓝 pixel[1] = 0xff; // 绿 pixel[2] = 0xff; // 红 pixel[3] = 0xff; // 完全不透明 } } SDL_UnlockSurface(surface); // 启用Alpha混合 SDL_SetAlpha(surface, SDL_SRCALPHA, 0); SDL_BlitSurface(surface, NULL, screen, NULL); SDL_Flip(screen); sleep(2); // 再次修改像素为完全透明 if (SDL_LockSurface(surface) < 0) { fprintf(stderr, "Lock surface failed: %s\n", SDL_GetError()); return 1; } for(int i=0; i<32; i++) { for(int j=0; j<32; j++) { Uint8* pixel = (Uint8*)surface->pixels + i * surface->pitch + j * 4; pixel[0] = 0x30; // 蓝 pixel[1] = 0x40; // 绿 pixel[2] = 0x50; // 红 pixel[3] = 0x00; // 完全透明 } } SDL_UnlockSurface(surface); SDL_BlitSurface(surface, NULL, screen, NULL); while(1) { SDL_Flip(screen); } // 清理资源(示例中未执行,实际开发需添加) SDL_FreeSurface(surface); SDL_Quit(); return 0; }
内容的提问来源于stack exchange,提问作者Nieważne Nieważne
相关产品推荐
相关产品推荐

