--- a/src/adapters/sdl/platform.cpp +++ b/src/adapters/sdl/platform.cpp @@ -5,6 +5,7 @@ #include "openlf2/detail/sdl/frame_upscaler.hpp" #include "openlf2/detail/sdl/haptics.hpp" #include "openlf2/detail/sdl/setup_screen.hpp" +#include "openlf2/detail/sdl/sprite_alpha.hpp" #ifdef OPENLF2_HAS_WINDOW_ICON #include "window_icon.hpp" // generated from the SVG icon by tools/icons/make_icons.py #endif @@ -667,7 +668,7 @@ #endif if (!loaded) return error(); std::unique_ptr surface( - SDL_ConvertSurface(loaded.get(), png ? SDL_PIXELFORMAT_ARGB8888 : SDL_PIXELFORMAT_XRGB8888)); + detail::sprite_surface_with_alpha(loaded.get(), png, sprite.color_key)); if (!surface) return error(); if (surface->w <= 0 || surface->h <= 0 || surface->w > 8192 || surface->h > 8192) { return fail(ErrorCode::limit, "image dimensions exceed limits"); @@ -680,13 +681,10 @@ textures_.clear(); texture_bytes_ = 0; } - if (sprite.color_key && - !SDL_SetSurfaceColorKey(surface.get(), true, SDL_MapSurfaceRGB(surface.get(), 0, 0, 0))) { - return error(); - } Texture texture(SDL_CreateTextureFromSurface(renderer_.get(), surface.get())); // Sprites are always sampled without filtering; linear scaling happens on the finished frame. - if (!texture || !SDL_SetTextureScaleMode(texture.get(), SDL_SCALEMODE_NEAREST)) return error(); + if (!texture || !detail::set_sprite_texture_blend(texture.get(), png, sprite.color_key) + || !SDL_SetTextureScaleMode(texture.get(), SDL_SCALEMODE_NEAREST)) return error(); textures_.emplace(key, std::move(texture)); texture_bytes_ += texture_bytes; } --- /dev/null +++ b/include/openlf2/detail/sdl/sprite_alpha.hpp @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +#pragma once +#include + +namespace openlf2::detail { +// BMP sprites use exact RGB black as their transparent key. Materialize alpha +// before texture upload: an XRGB color key can otherwise depend on the backend. +// PNG alpha is already authored and must be preserved, including opaque black. +inline SDL_Surface* sprite_surface_with_alpha(SDL_Surface* loaded, bool png, bool color_key) { + SDL_Surface* surface = SDL_ConvertSurface(loaded, + png || color_key ? SDL_PIXELFORMAT_ARGB8888 : SDL_PIXELFORMAT_XRGB8888); + if (!surface) return nullptr; + if (color_key && !png) { + if (!SDL_LockSurface(surface)) { + SDL_DestroySurface(surface); + return nullptr; + } + for (int y = 0; y < surface->h; ++y) { + auto* row = reinterpret_cast(static_cast(surface->pixels) + + y * surface->pitch); + for (int x = 0; x < surface->w; ++x) { + const Uint32 rgb = row[x] & 0x00ffffffu; + row[x] = rgb == 0 ? 0u : rgb | 0xff000000u; + } + } + SDL_UnlockSurface(surface); + } + return surface; +} + +inline bool set_sprite_texture_blend(SDL_Texture* texture, bool png, bool color_key) { + return SDL_SetTextureBlendMode(texture, + png || color_key ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE); +} +} // namespace openlf2::detail