From c0a2ae2a4a9826a15df347ad9be61436090bbf8f Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sun, 23 Nov 2025 11:38:35 -0800
Subject: [PATCH] opengles2: fixed swapped colors when using indexed textures
---
src/render/opengles2/SDL_render_gles2.c | 19 +++++-
src/render/opengles2/SDL_shaders_gles2.c | 82 +++++++++++++++++-------
src/render/opengles2/SDL_shaders_gles2.h | 3 +
3 files changed, 79 insertions(+), 25 deletions(-)
diff --git a/src/render/opengles2/SDL_render_gles2.c b/src/render/opengles2/SDL_render_gles2.c
index 2d2d725cb1a0a..74644e7de5a9a 100644
--- a/src/render/opengles2/SDL_render_gles2.c
+++ b/src/render/opengles2/SDL_render_gles2.c
@@ -628,6 +628,7 @@ static bool GLES2_SelectProgram(GLES2_RenderData *data, SDL_Texture *texture, GL
GLES2_ShaderType vtype, ftype;
GLES2_ProgramCacheEntry *program;
GLES2_TextureData *tdata = texture ? (GLES2_TextureData *)texture->internal : NULL;
+ const bool colorswap = (data->drawstate.target && (data->drawstate.target->format == SDL_PIXELFORMAT_BGRA32 || data->drawstate.target->format == SDL_PIXELFORMAT_BGRX32));
const float *shader_params = NULL;
int shader_params_len = 0;
@@ -640,15 +641,27 @@ static bool GLES2_SelectProgram(GLES2_RenderData *data, SDL_Texture *texture, GL
case GLES2_IMAGESOURCE_TEXTURE_INDEX8:
switch (scale_mode) {
case SDL_SCALEMODE_NEAREST:
- ftype = GLES2_SHADER_FRAGMENT_TEXTURE_PALETTE_NEAREST;
+ if (colorswap) {
+ ftype = GLES2_SHADER_FRAGMENT_TEXTURE_PALETTE_NEAREST_COLORSWAP;
+ } else {
+ ftype = GLES2_SHADER_FRAGMENT_TEXTURE_PALETTE_NEAREST;
+ }
break;
case SDL_SCALEMODE_LINEAR:
- ftype = GLES2_SHADER_FRAGMENT_TEXTURE_PALETTE_LINEAR;
+ if (colorswap) {
+ ftype = GLES2_SHADER_FRAGMENT_TEXTURE_PALETTE_LINEAR_COLORSWAP;
+ } else {
+ ftype = GLES2_SHADER_FRAGMENT_TEXTURE_PALETTE_LINEAR;
+ }
shader_params = tdata->texel_size;
shader_params_len = 4 * sizeof(float);
break;
case SDL_SCALEMODE_PIXELART:
- ftype = GLES2_SHADER_FRAGMENT_TEXTURE_PALETTE_PIXELART;
+ if (colorswap) {
+ ftype = GLES2_SHADER_FRAGMENT_TEXTURE_PALETTE_PIXELART_COLORSWAP;
+ } else {
+ ftype = GLES2_SHADER_FRAGMENT_TEXTURE_PALETTE_PIXELART;
+ }
shader_params = tdata->texel_size;
shader_params_len = 4 * sizeof(float);
break;
diff --git a/src/render/opengles2/SDL_shaders_gles2.c b/src/render/opengles2/SDL_shaders_gles2.c
index 6b109227a0054..76786026ff13f 100644
--- a/src/render/opengles2/SDL_shaders_gles2.c
+++ b/src/render/opengles2/SDL_shaders_gles2.c
@@ -167,7 +167,20 @@ static const char GLES2_Fragment_TexturePalette_Nearest[] =
"\n"
"void main()\n"
"{\n"
-" gl_FragColor = SamplePaletteNearest(v_texCoord);\n"
+" mediump vec4 color = SamplePaletteNearest(v_texCoord);\n"
+" gl_FragColor = color;\n"
+" gl_FragColor *= v_color;\n"
+"}\n"
+;
+
+static const char GLES2_Fragment_TexturePalette_Nearest_Colorswap[] =
+ PALETTE_SHADER_PROLOGUE
+ PALETTE_SHADER_FUNCTIONS
+"\n"
+"void main()\n"
+"{\n"
+" mediump vec4 color = SamplePaletteNearest(v_texCoord);\n"
+" gl_FragColor = vec4(color.b, color.g, color.r, color.a);\n"
" gl_FragColor *= v_color;\n"
"}\n"
;
@@ -178,7 +191,20 @@ static const char GLES2_Fragment_TexturePalette_Linear[] =
"\n"
"void main()\n"
"{\n"
-" gl_FragColor = SamplePaletteLinear(v_texCoord);\n"
+" mediump vec4 color = SamplePaletteLinear(v_texCoord);\n"
+" gl_FragColor = color;\n"
+" gl_FragColor *= v_color;\n"
+"}\n"
+;
+
+static const char GLES2_Fragment_TexturePalette_Linear_Colorswap[] =
+ PALETTE_SHADER_PROLOGUE
+ PALETTE_SHADER_FUNCTIONS
+"\n"
+"void main()\n"
+"{\n"
+" mediump vec4 color = SamplePaletteLinear(v_texCoord);\n"
+" gl_FragColor = vec4(color.b, color.g, color.r, color.a);\n"
" gl_FragColor *= v_color;\n"
"}\n"
;
@@ -191,10 +217,28 @@ static const char GLES2_Fragment_TexturePalette_PixelArt[] =
"void main()\n"
"{\n"
#ifdef OPENGLES_300
-" gl_FragColor = SamplePaletteLinear(GetPixelArtUV(v_texCoord));\n"
+" mediump vec4 color = SamplePaletteLinear(GetPixelArtUV(v_texCoord));\n"
#else
-" gl_FragColor = SamplePaletteNearest(v_texCoord);\n"
+" mediump vec4 color = SamplePaletteNearest(v_texCoord);\n"
#endif
+" gl_FragColor = color;\n"
+" gl_FragColor *= v_color;\n"
+"}\n"
+;
+
+static const char GLES2_Fragment_TexturePalette_PixelArt_Colorswap[] =
+ PALETTE_SHADER_PROLOGUE
+ PALETTE_SHADER_FUNCTIONS
+ PIXELART_SHADER_FUNCTIONS
+"\n"
+"void main()\n"
+"{\n"
+#ifdef OPENGLES_300
+" mediump vec4 color = SamplePaletteLinear(GetPixelArtUV(v_texCoord));\n"
+#else
+" mediump vec4 color = SamplePaletteNearest(v_texCoord);\n"
+#endif
+" gl_FragColor = vec4(color.b, color.g, color.r, color.a);\n"
" gl_FragColor *= v_color;\n"
"}\n"
;
@@ -206,10 +250,7 @@ static const char GLES2_Fragment_TextureRGB[] =
"void main()\n"
"{\n"
" mediump vec4 color = texture2D(u_texture, v_texCoord);\n"
-" gl_FragColor = color;\n"
-" gl_FragColor.r = color.b;\n"
-" gl_FragColor.b = color.r;\n"
-" gl_FragColor.a = 1.0;\n"
+" gl_FragColor = vec4(color.b, color.g, color.r, 1.0);\n"
" gl_FragColor *= v_color;\n"
"}\n"
;
@@ -222,10 +263,7 @@ static const char GLES2_Fragment_TextureRGB_PixelArt[] =
"void main()\n"
"{\n"
" mediump vec4 color = GetPixelArtSample(v_texCoord);\n"
-" gl_FragColor = color;\n"
-" gl_FragColor.r = color.b;\n"
-" gl_FragColor.b = color.r;\n"
-" gl_FragColor.a = 1.0;\n"
+" gl_FragColor = vec4(color.b, color.g, color.r, 1.0);\n"
" gl_FragColor *= v_color;\n"
"}\n"
;
@@ -237,8 +275,7 @@ static const char GLES2_Fragment_TextureBGR[] =
"void main()\n"
"{\n"
" mediump vec4 color = texture2D(u_texture, v_texCoord);\n"
-" gl_FragColor = color;\n"
-" gl_FragColor.a = 1.0;\n"
+" gl_FragColor = vec4(color.r, color.g, color.b, 1.0);\n"
" gl_FragColor *= v_color;\n"
"}\n"
;
@@ -251,8 +288,7 @@ static const char GLES2_Fragment_TextureBGR_PixelArt[] =
"void main()\n"
"{\n"
" mediump vec4 color = GetPixelArtSample(v_texCoord);\n"
-" gl_FragColor = color;\n"
-" gl_FragColor.a = 1.0;\n"
+" gl_FragColor = vec4(color.r, color.g, color.b, 1.0);\n"
" gl_FragColor *= v_color;\n"
"}\n"
;
@@ -264,9 +300,7 @@ static const char GLES2_Fragment_TextureARGB[] =
"void main()\n"
"{\n"
" mediump vec4 color = texture2D(u_texture, v_texCoord);\n"
-" gl_FragColor = color;\n"
-" gl_FragColor.r = color.b;\n"
-" gl_FragColor.b = color.r;\n"
+" gl_FragColor = vec4(color.b, color.g, color.r, color.a);\n"
" gl_FragColor *= v_color;\n"
"}\n"
;
@@ -279,9 +313,7 @@ static const char GLES2_Fragment_TextureARGB_PixelArt[] =
"void main()\n"
"{\n"
" mediump vec4 color = GetPixelArtSample(v_texCoord);\n"
-" gl_FragColor = color;\n"
-" gl_FragColor.r = color.b;\n"
-" gl_FragColor.b = color.r;\n"
+" gl_FragColor = vec4(color.b, color.g, color.r, color.a);\n"
" gl_FragColor *= v_color;\n"
"}\n"
;
@@ -524,6 +556,12 @@ const char *GLES2_GetShader(GLES2_ShaderType type)
return GLES2_Fragment_TexturePalette_Linear;
case GLES2_SHADER_FRAGMENT_TEXTURE_PALETTE_PIXELART:
return GLES2_Fragment_TexturePalette_PixelArt;
+ case GLES2_SHADER_FRAGMENT_TEXTURE_PALETTE_NEAREST_COLORSWAP:
+ return GLES2_Fragment_TexturePalette_Nearest_Colorswap;
+ case GLES2_SHADER_FRAGMENT_TEXTURE_PALETTE_LINEAR_COLORSWAP:
+ return GLES2_Fragment_TexturePalette_Linear_Colorswap;
+ case GLES2_SHADER_FRAGMENT_TEXTURE_PALETTE_PIXELART_COLORSWAP:
+ return GLES2_Fragment_TexturePalette_PixelArt_Colorswap;
case GLES2_SHADER_FRAGMENT_TEXTURE_RGB:
return GLES2_Fragment_TextureRGB;
case GLES2_SHADER_FRAGMENT_TEXTURE_RGB_PIXELART:
diff --git a/src/render/opengles2/SDL_shaders_gles2.h b/src/render/opengles2/SDL_shaders_gles2.h
index c29ced6994efa..a0a40a0879456 100644
--- a/src/render/opengles2/SDL_shaders_gles2.h
+++ b/src/render/opengles2/SDL_shaders_gles2.h
@@ -42,6 +42,9 @@ typedef enum
GLES2_SHADER_FRAGMENT_TEXTURE_PALETTE_NEAREST,
GLES2_SHADER_FRAGMENT_TEXTURE_PALETTE_LINEAR,
GLES2_SHADER_FRAGMENT_TEXTURE_PALETTE_PIXELART,
+ GLES2_SHADER_FRAGMENT_TEXTURE_PALETTE_NEAREST_COLORSWAP,
+ GLES2_SHADER_FRAGMENT_TEXTURE_PALETTE_LINEAR_COLORSWAP,
+ GLES2_SHADER_FRAGMENT_TEXTURE_PALETTE_PIXELART_COLORSWAP,
GLES2_SHADER_FRAGMENT_TEXTURE_RGB,
GLES2_SHADER_FRAGMENT_TEXTURE_RGB_PIXELART,
GLES2_SHADER_FRAGMENT_TEXTURE_BGR,