From f2d2cd8152838ac007bad6882797486265cf1605 Mon Sep 17 00:00:00 2001
From: Qiang <89919117+larks-x@users.noreply.github.com>
Date: Thu, 4 Sep 2025 22:41:45 +0800
Subject: [PATCH] fix: Blending rendering anomaly when using the software
renderer with SDL_WINDOW_TRANSPARENT on Windows. (#13866)
---
VisualC/SDL.sln | 11 +
.../testsoftwaretransparent.vcxproj | 209 ++++++++++++++++++
src/video/windows/SDL_windowsframebuffer.c | 17 +-
test/testsoftwaretransparent.c | 155 +++++++++++++
4 files changed, 388 insertions(+), 4 deletions(-)
create mode 100644 VisualC/tests/testsoftwaretransparent/testsoftwaretransparent.vcxproj
create mode 100644 test/testsoftwaretransparent.c
diff --git a/VisualC/SDL.sln b/VisualC/SDL.sln
index b5cf786ae2..d6e894049e 100644
--- a/VisualC/SDL.sln
+++ b/VisualC/SDL.sln
@@ -131,6 +131,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "01-joystick-polling", "exam
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "02-joystick-events", "examples\input\02-joystick-events\02-joystick-events.vcxproj", "{FCBDF2B2-1129-49AE-9406-3F219E65CA89}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testsoftwaretransparent", "tests\testsoftwaretransparent\testsoftwaretransparent.vcxproj", "{D91C45E2-274E-4C0F-89C7-9986F9A7E85A}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
@@ -579,6 +581,14 @@ Global
{FCBDF2B2-1129-49AE-9406-3F219E65CA89}.Release|Win32.Build.0 = Release|Win32
{FCBDF2B2-1129-49AE-9406-3F219E65CA89}.Release|x64.ActiveCfg = Release|x64
{FCBDF2B2-1129-49AE-9406-3F219E65CA89}.Release|x64.Build.0 = Release|x64
+ {D91C45E2-274E-4C0F-89C7-9986F9A7E85A}.Debug|Win32.ActiveCfg = Debug|Win32
+ {D91C45E2-274E-4C0F-89C7-9986F9A7E85A}.Debug|Win32.Build.0 = Debug|Win32
+ {D91C45E2-274E-4C0F-89C7-9986F9A7E85A}.Debug|x64.ActiveCfg = Debug|x64
+ {D91C45E2-274E-4C0F-89C7-9986F9A7E85A}.Debug|x64.Build.0 = Debug|x64
+ {D91C45E2-274E-4C0F-89C7-9986F9A7E85A}.Release|Win32.ActiveCfg = Release|Win32
+ {D91C45E2-274E-4C0F-89C7-9986F9A7E85A}.Release|Win32.Build.0 = Release|Win32
+ {D91C45E2-274E-4C0F-89C7-9986F9A7E85A}.Release|x64.ActiveCfg = Release|x64
+ {D91C45E2-274E-4C0F-89C7-9986F9A7E85A}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -644,6 +654,7 @@ Global
{3DB9B219-769E-43AC-8B8B-319DB6045DCF} = {D1BF59F6-22DC-493B-BDEB-451A50DA793D}
{B3852DB7-E925-4026-8B9D-D2272EFEFF3C} = {8DEAE483-FDE7-463F-9FD5-F597BBAED1F9}
{FCBDF2B2-1129-49AE-9406-3F219E65CA89} = {8DEAE483-FDE7-463F-9FD5-F597BBAED1F9}
+ {D91C45E2-274E-4C0F-89C7-9986F9A7E85A} = {D69D5741-611F-4E14-8541-1FEE94F50B5A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C320C9F2-1A8F-41D7-B02B-6338F872BCAD}
diff --git a/VisualC/tests/testsoftwaretransparent/testsoftwaretransparent.vcxproj b/VisualC/tests/testsoftwaretransparent/testsoftwaretransparent.vcxproj
new file mode 100644
index 0000000000..1970a3b334
--- /dev/null
+++ b/VisualC/tests/testsoftwaretransparent/testsoftwaretransparent.vcxproj
@@ -0,0 +1,209 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ Win32
+
+
+ Release
+ x64
+
+
+
+ {D91C45E2-274E-4C0F-89C7-9986F9A7E85A}
+ testsoftwaretransparent
+ 10.0
+
+
+
+ Application
+ $(DefaultPlatformToolset)
+
+
+ Application
+ $(DefaultPlatformToolset)
+
+
+ Application
+ $(DefaultPlatformToolset)
+
+
+ Application
+ $(DefaultPlatformToolset)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_ProjectFileVersion>10.0.40219.1
+ $(SolutionDir)$(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\
+ $(SolutionDir)$(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\
+ $(SolutionDir)$(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\
+ $(SolutionDir)$(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\
+ AllRules.ruleset
+
+
+ AllRules.ruleset
+
+
+ AllRules.ruleset
+
+
+ AllRules.ruleset
+
+
+
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ true
+ true
+ Win32
+ .\Release/testsoftwaretransparent.tlb
+
+
+ %(AdditionalOptions) /utf-8
+ $(SolutionDir)/../include;%(AdditionalIncludeDirectories)
+ %(AdditionalUsingDirectories)
+ WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)
+ MultiThreadedDLL
+ Level3
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ 0x0409
+
+
+ Windows
+
+
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ true
+ true
+ X64
+ .\Release/testsoftwaretransparent.tlb
+
+
+ %(AdditionalOptions) /utf-8
+ $(SolutionDir)/../include;%(AdditionalIncludeDirectories)
+ %(AdditionalUsingDirectories)
+ WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)
+ MultiThreadedDLL
+ Level3
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ 0x0409
+
+
+ Windows
+
+
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ true
+ true
+ Win32
+ .\Debug/testsoftwaretransparent.tlb
+
+
+ %(AdditionalOptions) /utf-8
+ Disabled
+ $(SolutionDir)/../include;%(AdditionalIncludeDirectories)
+ %(AdditionalUsingDirectories)
+ WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)
+ MultiThreadedDLL
+ Level3
+ OldStyle
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ 0x0409
+
+
+ true
+ Windows
+
+
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ true
+ true
+ X64
+ .\Debug/testsoftwaretransparent.tlb
+
+
+ %(AdditionalOptions) /utf-8
+ Disabled
+ $(SolutionDir)/../include;%(AdditionalIncludeDirectories)
+ %(AdditionalUsingDirectories)
+ WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)
+ MultiThreadedDebugDLL
+ Level3
+ OldStyle
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ 0x0409
+
+
+ true
+ Windows
+
+
+
+
+ $(TreatWarningsAsError)
+
+
+
+
+ {81ce8daf-ebb2-4761-8e45-b71abcca8c68}
+ false
+ false
+ true
+
+
+ {da956fd3-e143-46f2-9fe5-c77bebc56b1a}
+ false
+ false
+ true
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/video/windows/SDL_windowsframebuffer.c b/src/video/windows/SDL_windowsframebuffer.c
index f2bbd590a9..d9af714bfc 100644
--- a/src/video/windows/SDL_windowsframebuffer.c
+++ b/src/video/windows/SDL_windowsframebuffer.c
@@ -59,6 +59,9 @@ bool WIN_CreateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window, SDL
GetDIBits(data->hdc, hbm, 0, 0, NULL, info, DIB_RGB_COLORS);
DeleteObject(hbm);
+ // Check if a transparent channel is required
+ bool need_alpha = (window->flags & SDL_WINDOW_TRANSPARENT) != 0;
+
*format = SDL_PIXELFORMAT_UNKNOWN;
if (info->bmiHeader.biCompression == BI_BITFIELDS) {
int bpp;
@@ -68,16 +71,22 @@ bool WIN_CreateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window, SDL
masks = (Uint32 *)((Uint8 *)info + info->bmiHeader.biSize);
*format = SDL_GetPixelFormatForMasks(bpp, masks[0], masks[1], masks[2], 0);
}
- if (*format == SDL_PIXELFORMAT_UNKNOWN) {
- // We'll use RGB format for now
- *format = SDL_PIXELFORMAT_XRGB8888;
+ if (*format == SDL_PIXELFORMAT_UNKNOWN || need_alpha) {
+ // We'll use RGB or BGRA32 format for now
+ *format = need_alpha ? SDL_PIXELFORMAT_BGRA32 : SDL_PIXELFORMAT_XRGB8888;
// Create a new one
SDL_memset(info, 0, size);
info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
info->bmiHeader.biPlanes = 1;
info->bmiHeader.biBitCount = 32;
- info->bmiHeader.biCompression = BI_RGB;
+ info->bmiHeader.biCompression = need_alpha ? BI_BITFIELDS : BI_RGB;
+
+ if (need_alpha) {
+ int tmpbpp;
+ Uint32 *bgr32masks = (Uint32 *)((Uint8 *)info + info->bmiHeader.biSize);
+ SDL_GetMasksForPixelFormat(SDL_PIXELFORMAT_BGRA32, &tmpbpp, &bgr32masks[0], &bgr32masks[1], &bgr32masks[2], &bgr32masks[3]);
+ }
}
// Fill in the size information
diff --git a/test/testsoftwaretransparent.c b/test/testsoftwaretransparent.c
new file mode 100644
index 0000000000..bd22d09f5a
--- /dev/null
+++ b/test/testsoftwaretransparent.c
@@ -0,0 +1,155 @@
+/*
+ Copyright (C) 1997-2025 Sam Lantinga
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely.
+*/
+#include
+#include
+
+#include "glass.h"
+
+
+int main(int argc, char *argv[])
+{
+ const char *image_file = NULL;
+ SDL_Window *window = NULL;
+ SDL_Renderer *renderer = NULL;
+ SDL_Texture *texture = NULL;
+ SDL_WindowFlags flags;
+ bool done = false;
+ SDL_Event event;
+
+ int return_code = 1;
+
+
+ int windowWidth = 800;
+ int windowHeight = 600;
+ SDL_FRect destRect;
+ destRect.x = 0;
+ destRect.y = 0;
+ destRect.w = 100;
+ destRect.h = 100;
+
+ SDL_FRect destRect2;
+ destRect2.x = 700;
+ destRect2.y = 0;
+ destRect2.w = 100;
+ destRect2.h = 100;
+
+ SDL_FRect destRect3;
+ destRect3.x = 0;
+ destRect3.y = 500;
+ destRect3.w = 100;
+ destRect3.h = 100;
+
+ SDL_FRect destRect4;
+ destRect4.x = 700;
+ destRect4.y = 500;
+ destRect4.w = 100;
+ destRect4.h = 100;
+
+ SDL_FRect destRect5;
+ destRect5.x = 350;
+ destRect5.y = 250;
+ destRect5.w = 100;
+ destRect5.h = 100;
+
+
+ /* Create the window hidden */
+ flags = (SDL_WINDOW_HIDDEN | SDL_WINDOW_TRANSPARENT);
+ //flags |= SDL_WINDOW_BORDERLESS;
+
+ window = SDL_CreateWindow("SDL Software Renderer Transparent Test", windowWidth, windowHeight, flags);
+ if (!window) {
+ SDL_Log("Couldn't create transparent window: %s", SDL_GetError());
+ goto quit;
+ }
+
+ /* Create a software renderer and set the blend mode */
+ renderer = SDL_CreateRenderer(window, SDL_SOFTWARE_RENDERER);
+ if (!renderer) {
+ SDL_Log("Couldn't create renderer: %s", SDL_GetError());
+ goto quit;
+ }
+
+ if (!SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND)) {
+ SDL_Log("Couldn't set renderer blend mode: %s\n", SDL_GetError());
+ return false;
+ }
+
+ /* Create texture and set the blend mode */
+ // texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_TARGET, windowWidth, windowHeight);
+ //texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, windowWidth, windowHeight);
+ // texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_BGRA32, SDL_TEXTUREACCESS_TARGET, windowWidth, windowHeight);
+ texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_BGRA8888, SDL_TEXTUREACCESS_TARGET, windowWidth, windowHeight);
+ if (texture == NULL) {
+ SDL_Log("Couldn't create texture: %s\n", SDL_GetError());
+ return false;
+ }
+
+ if (!SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND_PREMULTIPLIED)) {
+ SDL_Log("Couldn't set texture blend mode: %s\n", SDL_GetError());
+ return false;
+ }
+
+ /* Show */
+ SDL_ShowWindow(window);
+
+ /* We're ready to go! */
+ while (!done) {
+ while (SDL_PollEvent(&event)) {
+ switch (event.type) {
+ case SDL_EVENT_KEY_DOWN:
+ if (event.key.key == SDLK_ESCAPE) {
+ done = true;
+ }
+ break;
+ case SDL_EVENT_QUIT:
+ done = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+
+ /* Draw opaque red squares at the four corners of the form, and draw a red square with an alpha value of 180 in the center of the form */
+
+ SDL_SetRenderTarget(renderer, texture);
+ SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
+ SDL_RenderClear(renderer);
+
+ SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
+ SDL_RenderFillRect(renderer, &destRect);
+ SDL_RenderFillRect(renderer, &destRect2);
+ SDL_RenderFillRect(renderer, &destRect3);
+ SDL_RenderFillRect(renderer, &destRect4);
+ SDL_SetRenderDrawColor(renderer, 255, 0, 0, 180);
+ SDL_RenderFillRect(renderer, &destRect5);
+
+ SDL_SetRenderTarget(renderer, NULL);
+ SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
+ SDL_RenderClear(renderer);
+ SDL_RenderTexture(renderer, texture, NULL, NULL);
+
+ /* Show everything on the screen and wait a bit */
+ SDL_RenderPresent(renderer);
+ SDL_Delay(100);
+ }
+
+ /* Success! */
+ return_code = 0;
+
+quit:
+ SDL_DestroyTexture(texture);
+ SDL_DestroyRenderer(renderer);
+ SDL_DestroyWindow(window);
+ SDL_Quit();
+ return return_code;
+}