Regression [SDL_image 2.8.0]: can not blit from a png file with palette and transparency
hexagonrecursion opened this issue · 4 comments
SDL_image >= 2.8.0 loads png files with a palette as a surface with a palette (SDL_PIXELFORMAT_INDEX8). If one or more of the colours in the palette has transparency, the blend mode is set to SDL_BLENDMODE_BLEND, the ColorKey is not set and AlphaMod is not set. This causes issues because SDL_BlitSurface from a surface with this exact combination of options is not supported in SDL.
I consider this a regression because previously we could IMG_Load() those files and use them as a source for blitting, but now we can't.
To show that this exact combination of options is not supported:
g++ -lSDL2 main.cpp && ./a.out
Prints:
INFO: Oh no! error: Blit combination not supported
main.cpp:
#include <cstdlib>
#include <SDL2/SDL.h>
void expect(bool cond) {
if(!cond) {
SDL_Log("Something unexpectedly went wroung while setting up the test: %s\n", SDL_GetError());
std::exit(1);
}
}
int main(int, char**) {
// Setup
expect(SDL_Init(SDL_INIT_VIDEO) == 0);
SDL_Surface *src = SDL_CreateRGBSurfaceWithFormat(0,1,1,8,SDL_PIXELFORMAT_INDEX8); expect(src);
expect(SDL_SetSurfaceBlendMode(src, SDL_BLENDMODE_BLEND) == 0);
SDL_Surface *dst = SDL_CreateRGBSurfaceWithFormat(0,1,1,32,SDL_PIXELFORMAT_RGBA32); expect(dst);
// Check
if(SDL_BlitSurface(src, nullptr, dst, nullptr) == 0) {
SDL_Log("OK\n");
} else {
SDL_Log("Oh no! error: %s\n", SDL_GetError());
}
return 0;
}
To show that png files with a palette that contains colours with transparency trigger this:
g++ -lSDL2 -lSDL2_image main.cpp && ./a.out
Prints:
INFO: format: SDL_PIXELFORMAT_INDEX8; has color key: no; blend mode: SDL_BLENDMODE_BLEND; modulate alpha: 255
INFO: Oh no! error: Blit combination not supported
main.cpp:
#include <cstdlib>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
void expect(bool cond) {
if(!cond) {
SDL_Log("Something unexpectedly went wroung while setting up the test: %s\n", SDL_GetError());
std::exit(1);
}
}
const char *blendModeStr(SDL_BlendMode blendMode) {
switch(blendMode) {
case SDL_BLENDMODE_NONE: return "SDL_BLENDMODE_NONE";
case SDL_BLENDMODE_BLEND: return "SDL_BLENDMODE_BLEND";
case SDL_BLENDMODE_ADD: return "SDL_BLENDMODE_ADD";
case SDL_BLENDMODE_MOD: return "SDL_BLENDMODE_MOD";
case SDL_BLENDMODE_MUL: return "SDL_BLENDMODE_MUL";
}
return "unknown";
}
int main(int, char**) {
expect(SDL_Init(SDL_INIT_VIDEO) == 0);
expect(IMG_Init(IMG_INIT_PNG) == IMG_INIT_PNG);
SDL_Surface *src = IMG_Load("bug.png"); expect(src);
SDL_Surface *dst = SDL_CreateRGBSurfaceWithFormat(0,1,1,32,SDL_PIXELFORMAT_RGBA32); expect(dst);
SDL_BlendMode blendMode;
expect(SDL_GetSurfaceBlendMode(src, &blendMode) == 0);
Uint8 alpha;
expect(SDL_GetSurfaceAlphaMod(src, &alpha) == 0);
SDL_Log(
"format: %s; has color key: %s; blend mode: %s; modulate alpha: %d\n",
SDL_GetPixelFormatName(src->format->format),
(SDL_HasColorKey(src) ? "yes" : "no"),
blendModeStr(blendMode),
alpha
);
if(SDL_BlitSurface(src, nullptr, dst, nullptr) == 0) {
SDL_Log("OK\n");
} else {
SDL_Log("Oh no! error: %s\n", SDL_GetError());
}
return 0;
}
Here is the png file I used to test this:
bug.png
This code in SDL_blit_1.c is missing case SDL_COPY_BLEND
:
switch (surface->map->info.flags & ~SDL_COPY_RLE_MASK) {
case 0:
return one_blit[which];
case SDL_COPY_COLORKEY:
return one_blitkey[which];
case SDL_COPY_COLORKEY | SDL_COPY_BLEND: /* this is not super-robust but handles a specific case we found sdl12-compat. */
return (surface->map->info.a == 255) ? one_blitkey[which] : (SDL_BlitFunc)NULL;
case SDL_COPY_MODULATE_ALPHA | SDL_COPY_BLEND:
/* Supporting 8bpp->8bpp alpha is doable but requires lots of
tables which consume space and takes time to precompute,
so is better left to the user */
return which >= 2 ? Blit1toNAlpha : (SDL_BlitFunc)NULL;
case SDL_COPY_COLORKEY | SDL_COPY_MODULATE_ALPHA | SDL_COPY_BLEND:
return which >= 2 ? Blit1toNAlphaKey : (SDL_BlitFunc)NULL;
}
Version information:
- Fedora release 39
- gcc (GCC) 13.2.1 20231205 (Red Hat 13.2.1-6)
- SDL2 == 2.28.5
- SDL2_image == 2.8.1
Thanks for the report!
The showimage test program will also demonstrate this issue with the following patch:
diff --git a/examples/showimage.c b/examples/showimage.c
index d544869..faa597c 100644
--- a/examples/showimage.c
+++ b/examples/showimage.c
@@ -104,12 +104,24 @@ int main(int argc, char *argv[])
continue;
}
+#if 1
+ SDL_Surface *surface = IMG_Load(argv[i]);
+ if (surface) {
+ SDL_Surface *tmp = SDL_CreateSurface(surface->w, surface->h, SDL_PIXELFORMAT_RGBA32);
+ if (SDL_BlitSurface(surface, NULL, tmp, NULL) < 0) {
+ SDL_Log("SDL_BlitSurface() failed: %s\n", SDL_GetError());
+ return(2);
+ }
+ texture = SDL_CreateTextureFromSurface(renderer, tmp);
+ }
+#else
/* Open the image file */
texture = IMG_LoadTexture(renderer, argv[i]);
if (!texture) {
SDL_Log("Couldn't load %s: %s\n", argv[i], SDL_GetError());
continue;
}
+#endif
SDL_QueryTexture(texture, NULL, NULL, &w, &h);
/* Save the image file, if desired */
This will be fixed for the SDL 2.8.2 release. Thanks for the excellent bug report!
Did you mean SDL_image 2.8.2? Because SDL is already at 2.28.5
Yes, sorry, I meant SDL_image 2.8.2.