From 0b4b254a53fe4af21f51a52236ae46c148c5d621 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Fri, 26 Sep 2025 11:44:04 -0700 Subject: [PATCH] Added support for textures with palettes Closes https://github.com/libsdl-org/SDL/pull/6192 --- include/SDL3/SDL_render.h | 40 ++- include/SDL3/SDL_surface.h | 2 + src/dynapi/SDL_dynapi.sym | 2 + src/dynapi/SDL_dynapi_overrides.h | 2 + src/dynapi/SDL_dynapi_procs.h | 2 + src/render/SDL_render.c | 278 ++++++++++++----- src/render/SDL_sysrender.h | 7 +- src/render/software/SDL_render_sw.c | 18 ++ test/CMakeLists.txt | 1 + test/testpalette.c | 448 ++++++++++++++++++++++++++++ test/testsymbols.c | 2 + 11 files changed, 728 insertions(+), 74 deletions(-) create mode 100644 test/testpalette.c diff --git a/include/SDL3/SDL_render.h b/include/SDL3/SDL_render.h index 8fe3455207..ee704eb4e9 100644 --- a/include/SDL3/SDL_render.h +++ b/include/SDL3/SDL_render.h @@ -660,6 +660,7 @@ extern SDL_DECLSPEC SDL_Texture * SDLCALL SDL_CreateTextureFromSurface(SDL_Rende * pixels, required * - `SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER`: the height of the texture in * pixels, required + * - `SDL_PROP_TEXTURE_CREATE_PALETTE_POINTER`: an SDL_Palette to use with palettized texture formats. This can be set later with SDL_SetTexturePalette() * - `SDL_PROP_TEXTURE_CREATE_SDR_WHITE_POINT_FLOAT`: for HDR10 and floating * point textures, this defines the value of 100% diffuse white, with higher * values being displayed in the High Dynamic Range headroom. This defaults @@ -759,6 +760,7 @@ extern SDL_DECLSPEC SDL_Texture * SDLCALL SDL_CreateTextureWithProperties(SDL_Re #define SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER "SDL.texture.create.access" #define SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER "SDL.texture.create.width" #define SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER "SDL.texture.create.height" +#define SDL_PROP_TEXTURE_CREATE_PALETTE_POINTER "SDL.texture.create.palette" #define SDL_PROP_TEXTURE_CREATE_SDR_WHITE_POINT_FLOAT "SDL.texture.create.SDR_white_point" #define SDL_PROP_TEXTURE_CREATE_HDR_HEADROOM_FLOAT "SDL.texture.create.HDR_headroom" #define SDL_PROP_TEXTURE_CREATE_D3D11_TEXTURE_POINTER "SDL.texture.create.d3d11.texture" @@ -929,6 +931,42 @@ extern SDL_DECLSPEC SDL_Renderer * SDLCALL SDL_GetRendererFromTexture(SDL_Textur */ extern SDL_DECLSPEC bool SDLCALL SDL_GetTextureSize(SDL_Texture *texture, float *w, float *h); +/** + * Set the palette used by a texture. + * + * Setting the palette keeps an internal reference to the palette, which can be safely destroyed afterwards. + * + * A single palette can be shared with many textures. + * + * \param texture the texture to update. + * \param palette the SDL_Palette structure to use. + * \returns true on success or false on failure; call SDL_GetError() for more + * information. + * + * \threadsafety This function should only be called on the main thread. + * + * \since This function is available since SDL 3.4.0. + * + * \sa SDL_CreatePalette + * \sa SDL_GetTexturePalette + */ +extern SDL_DECLSPEC bool SDLCALL SDL_SetTexturePalette(SDL_Texture *texture, SDL_Palette *palette); + +/** + * Get the palette used by a texture. + * + * \param texture the texture to query. + * \returns a pointer to the palette used by the texture, or NULL if there is + * no palette used. + * + * \threadsafety This function should only be called on the main thread. + * + * \since This function is available since SDL 3.4.0. + * + * \sa SDL_SetTexturePalette + */ +extern SDL_DECLSPEC SDL_Palette * SDLCALL SDL_GetTexturePalette(SDL_Texture *texture); + /** * Set an additional color value multiplied into render copy operations. * @@ -1157,7 +1195,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetTextureBlendMode(SDL_Texture *texture, S * * The default texture scale mode is SDL_SCALEMODE_LINEAR. * - * If the scale mode is not supported, the closest supported mode is chosen. + * If the scale mode is not supported, the closest supported mode is chosen. Palettized textures will use SDL_SCALEMODE_PIXELART instead of SDL_SCALEMODE_LINEAR. * * \param texture the texture to update. * \param scaleMode the SDL_ScaleMode to use for texture scaling. diff --git a/include/SDL3/SDL_surface.h b/include/SDL3/SDL_surface.h index c7354288a7..8d0f5cb8fa 100644 --- a/include/SDL3/SDL_surface.h +++ b/include/SDL3/SDL_surface.h @@ -327,6 +327,8 @@ extern SDL_DECLSPEC SDL_Palette * SDLCALL SDL_CreateSurfacePalette(SDL_Surface * /** * Set the palette used by a surface. * + * Setting the palette keeps an internal reference to the palette, which can be safely destroyed afterwards. + * * A single palette can be shared with many surfaces. * * \param surface the SDL_Surface structure to update. diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index 0e4b0aa6d8..99dd2411f5 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -1257,6 +1257,8 @@ SDL3_0.0.0 { SDL_hid_get_properties; SDL_GetPixelFormatFromGPUTextureFormat; SDL_GetGPUTextureFormatFromPixelFormat; + SDL_SetTexturePalette; + SDL_GetTexturePalette; # extra symbols go here (don't modify this line) local: *; }; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index 010eb7a726..2785c06441 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -1283,3 +1283,5 @@ #define SDL_GetPixelFormatFromGPUTextureFormat SDL_GetPixelFormatFromGPUTextureFormat_REAL #define SDL_GetGPUTextureFormatFromPixelFormat SDL_GetGPUTextureFormatFromPixelFormat_REAL #define JNI_OnLoad JNI_OnLoad_REAL +#define SDL_SetTexturePalette SDL_SetTexturePalette_REAL +#define SDL_GetTexturePalette SDL_GetTexturePalette_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 71a56e4e68..bf8bb35029 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -1291,3 +1291,5 @@ SDL_DYNAPI_PROC(SDL_PropertiesID,SDL_hid_get_properties,(SDL_hid_device *a),(a), SDL_DYNAPI_PROC(SDL_PixelFormat,SDL_GetPixelFormatFromGPUTextureFormat,(SDL_GPUTextureFormat a),(a),return) SDL_DYNAPI_PROC(SDL_GPUTextureFormat,SDL_GetGPUTextureFormatFromPixelFormat,(SDL_PixelFormat a),(a),return) SDL_DYNAPI_PROC(Sint32,JNI_OnLoad,(JavaVM *a, void *b),(a,b),return) +SDL_DYNAPI_PROC(bool,SDL_SetTexturePalette,(SDL_Texture *a,SDL_Palette *b),(a,b),return) +SDL_DYNAPI_PROC(SDL_Palette*,SDL_GetTexturePalette,(SDL_Texture *a),(a),return) diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index 69de7ccd0d..d2d47f3e44 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -697,6 +697,46 @@ static bool QueueCmdFillRects(SDL_Renderer *renderer, const SDL_FRect *rects, co return result; } +static bool UpdateTexturePalette(SDL_Texture *texture) +{ + SDL_Renderer *renderer = texture->renderer; + + if (!SDL_ISPIXELFORMAT_INDEXED(texture->format)) { + return true; + } + + if (!texture->palette) { + return SDL_SetError("Texture doesn't have a palette"); + } + + if (texture->palette_version == texture->palette->version) { + return true; + } + + if (texture->native) { + if (!FlushRenderCommandsIfTextureNeeded(texture->native)) { + return false; + } + + SDL_Surface *surface; + bool result = SDL_LockTextureToSurface(texture->native, NULL, &surface); + if (result) { + result = SDL_BlitSurface(texture->palette_surface, NULL, surface, NULL); + SDL_UnlockTexture(texture->native); + } + if (!result) { + return false; + } + } else { + if (!renderer->UpdateTexturePalette(renderer, texture)) { + return false; + } + } + + texture->palette_version = texture->palette->version; + return true; +} + static bool QueueCmdCopy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, const SDL_FRect *dstrect) { SDL_RenderCommand *cmd = PrepQueueCmdDraw(renderer, SDL_RENDERCMD_COPY, texture); @@ -1408,6 +1448,7 @@ SDL_Texture *SDL_CreateTextureWithProperties(SDL_Renderer *renderer, SDL_Propert SDL_TextureAccess access = (SDL_TextureAccess)SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STATIC); int w = (int)SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, 0); int h = (int)SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, 0); + SDL_Palette *palette = (SDL_Palette *)SDL_GetPointerProperty(props, SDL_PROP_TEXTURE_CREATE_PALETTE_POINTER, NULL); SDL_Colorspace default_colorspace; bool texture_is_fourcc_and_target; @@ -1421,8 +1462,8 @@ SDL_Texture *SDL_CreateTextureWithProperties(SDL_Renderer *renderer, SDL_Propert SDL_SetError("Invalid texture format"); return NULL; } - CHECK_PARAM(SDL_ISPIXELFORMAT_INDEXED(format) && !IsSupportedFormat(renderer, format)) { - SDL_SetError("Palettized textures are not supported"); + CHECK_PARAM(SDL_ISPIXELFORMAT_INDEXED(format) && access == SDL_TEXTUREACCESS_TARGET) { + SDL_SetError("Palettized textures can't be render targets"); return NULL; } CHECK_PARAM(w <= 0 || h <= 0) { @@ -1453,7 +1494,12 @@ SDL_Texture *SDL_CreateTextureWithProperties(SDL_Renderer *renderer, SDL_Propert texture->color.b = 1.0f; texture->color.a = 1.0f; texture->blendMode = SDL_ISPIXELFORMAT_ALPHA(format) ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE; - texture->scaleMode = renderer->scale_mode; + if (renderer->scale_mode == SDL_SCALEMODE_LINEAR && + SDL_ISPIXELFORMAT_INDEXED(format)) { + texture->scaleMode = SDL_SCALEMODE_PIXELART; + } else { + texture->scaleMode = renderer->scale_mode; + } texture->view.pixel_w = w; texture->view.pixel_h = h; texture->view.viewport.w = -1; @@ -1506,7 +1552,12 @@ SDL_Texture *SDL_CreateTextureWithProperties(SDL_Renderer *renderer, SDL_Propert } } SDL_SetNumberProperty(native_props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, closest_format); - SDL_SetNumberProperty(native_props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, texture->access); + if (SDL_ISPIXELFORMAT_INDEXED(texture->format)) { + // We're going to be uploading pixels frequently as the palette changes + SDL_SetNumberProperty(native_props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STREAMING); + } else { + SDL_SetNumberProperty(native_props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, texture->access); + } SDL_SetNumberProperty(native_props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, texture->w); SDL_SetNumberProperty(native_props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, texture->h); @@ -1532,6 +1583,8 @@ SDL_Texture *SDL_CreateTextureWithProperties(SDL_Renderer *renderer, SDL_Propert texture->next = texture->native; renderer->textures = texture; + SDL_SetTextureScaleMode(texture->native, texture->scaleMode); + if (texture->format == SDL_PIXELFORMAT_MJPG) { // We have a custom decode + upload path for this } else if (SDL_ISPIXELFORMAT_FOURCC(texture->format)) { @@ -1544,6 +1597,12 @@ SDL_Texture *SDL_CreateTextureWithProperties(SDL_Renderer *renderer, SDL_Propert SDL_DestroyTexture(texture); return NULL; } + } else if (SDL_ISPIXELFORMAT_INDEXED(texture->format)) { + texture->palette_surface = SDL_CreateSurface(w, h, texture->format); + if (!texture->palette_surface) { + SDL_DestroyTexture(texture); + return NULL; + } } else if (access == SDL_TEXTUREACCESS_STREAMING) { // The pitch is 4 byte aligned texture->pitch = (((w * SDL_BYTESPERPIXEL(format)) + 3) & ~3); @@ -1555,6 +1614,10 @@ SDL_Texture *SDL_CreateTextureWithProperties(SDL_Renderer *renderer, SDL_Propert } } + if (SDL_ISPIXELFORMAT_INDEXED(texture->format) && palette) { + SDL_SetTexturePalette(texture, palette); + } + // Now set the properties for the new texture props = SDL_GetTextureProperties(texture); SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_COLORSPACE_NUMBER, texture->colorspace); @@ -1584,50 +1647,11 @@ SDL_Texture *SDL_CreateTexture(SDL_Renderer *renderer, SDL_PixelFormat format, S static bool SDL_UpdateTextureFromSurface(SDL_Texture *texture, SDL_Rect *rect, SDL_Surface *surface) { - SDL_TextureAccess access; bool direct_update; - SDL_PixelFormat tex_format; - SDL_PropertiesID surface_props; - SDL_PropertiesID tex_props; - SDL_Colorspace surface_colorspace = SDL_COLORSPACE_UNKNOWN; - SDL_Colorspace texture_colorspace = SDL_COLORSPACE_UNKNOWN; - if (texture == NULL || surface == NULL) { - return false; - } - - tex_props = SDL_GetTextureProperties(texture); - if (!tex_props) { - return false; - } - - surface_props = SDL_GetSurfaceProperties(surface); - if (!surface_props) { - return false; - } - - tex_format = (SDL_PixelFormat)SDL_GetNumberProperty(tex_props, SDL_PROP_TEXTURE_FORMAT_NUMBER, 0); - access = (SDL_TextureAccess)SDL_GetNumberProperty(tex_props, SDL_PROP_TEXTURE_ACCESS_NUMBER, 0); - - if (access != SDL_TEXTUREACCESS_STATIC && access != SDL_TEXTUREACCESS_STREAMING) { - return false; - } - - surface_colorspace = SDL_GetSurfaceColorspace(surface); - texture_colorspace = surface_colorspace; - - if (surface_colorspace == SDL_COLORSPACE_SRGB_LINEAR || - SDL_COLORSPACETRANSFER(surface_colorspace) == SDL_TRANSFER_CHARACTERISTICS_PQ) { - if (SDL_ISPIXELFORMAT_FLOAT(tex_format)) { - texture_colorspace = SDL_COLORSPACE_SRGB_LINEAR; - } else if (SDL_ISPIXELFORMAT_10BIT(tex_format)) { - texture_colorspace = SDL_COLORSPACE_HDR10; - } else { - texture_colorspace = SDL_COLORSPACE_SRGB; - } - } - - if (tex_format == surface->format && texture_colorspace == surface_colorspace) { + if (surface->format == texture->format && + surface->palette == texture->palette && + SDL_GetSurfaceColorspace(surface) == texture->colorspace) { if (SDL_ISPIXELFORMAT_ALPHA(surface->format) && SDL_SurfaceHasColorKey(surface)) { /* Surface and Renderer formats are identical. * Intermediate conversion is needed to convert color key to alpha (SDL_ConvertColorkeyToAlpha()). */ @@ -1643,17 +1667,16 @@ static bool SDL_UpdateTextureFromSurface(SDL_Texture *texture, SDL_Rect *rect, S if (direct_update) { if (SDL_MUSTLOCK(surface)) { - SDL_LockSurface(surface); - SDL_UpdateTexture(texture, rect, surface->pixels, surface->pitch); - SDL_UnlockSurface(surface); + if (SDL_LockSurface(surface)) { + SDL_UpdateTexture(texture, rect, surface->pixels, surface->pitch); + SDL_UnlockSurface(surface); + } } else { SDL_UpdateTexture(texture, rect, surface->pixels, surface->pitch); } } else { - SDL_Surface *temp = NULL; - // Set up a destination surface for the texture update - temp = SDL_ConvertSurfaceAndColorspace(surface, tex_format, NULL, texture_colorspace, surface_props); + SDL_Surface *temp = SDL_ConvertSurfaceAndColorspace(surface, texture->format, texture->palette, texture->colorspace, SDL_GetSurfaceProperties(surface)); if (temp) { SDL_UpdateTexture(texture, NULL, temp->pixels, temp->pitch); SDL_DestroySurface(temp); @@ -1709,7 +1732,7 @@ SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *s needAlpha = false; } - // If Palette contains alpha values, promotes to alpha format + // If palette contains alpha values, promotes to alpha format palette = SDL_GetSurfacePalette(surface); if (palette) { bool is_opaque, has_alpha_channel; @@ -1806,6 +1829,9 @@ SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *s SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STATIC); SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, surface->w); SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, surface->h); + if (format == surface->format && palette) { + SDL_SetPointerProperty(props, SDL_PROP_TEXTURE_CREATE_PALETTE_POINTER, palette); + } texture = SDL_CreateTextureWithProperties(renderer, props); SDL_DestroyProperties(props); if (!texture) { @@ -1857,6 +1883,44 @@ bool SDL_GetTextureSize(SDL_Texture *texture, float *w, float *h) return true; } +bool SDL_SetTexturePalette(SDL_Texture *texture, SDL_Palette *palette) +{ + CHECK_TEXTURE_MAGIC(texture, false); + + CHECK_PARAM(!SDL_ISPIXELFORMAT_INDEXED(texture->format)) { + return SDL_SetError("Texture isn't palettized format"); + } + + CHECK_PARAM(palette && palette->ncolors > (1 << SDL_BITSPERPIXEL(texture->format))) { + return SDL_SetError("SDL_SetSurfacePalette() passed a palette that doesn't match the surface format"); + } + + if (palette != texture->palette) { + if (texture->palette) { + SDL_DestroyPalette(texture->palette); + } + + texture->palette = palette; + texture->palette_version = 0; + + if (texture->palette) { + ++texture->palette->refcount; + } + + if (texture->palette_surface) { + SDL_SetSurfacePalette(texture->palette_surface, palette); + } + } + return true; +} + +SDL_Palette *SDL_GetTexturePalette(SDL_Texture *texture) +{ + CHECK_TEXTURE_MAGIC(texture, NULL); + + return texture->palette; +} + bool SDL_SetTextureColorMod(SDL_Texture *texture, Uint8 r, Uint8 g, Uint8 b) { const float fR = (float)r / 255.0f; @@ -2028,9 +2092,13 @@ bool SDL_SetTextureScaleMode(SDL_Texture *texture, SDL_ScaleMode scaleMode) switch (scaleMode) { case SDL_SCALEMODE_NEAREST: - case SDL_SCALEMODE_LINEAR: case SDL_SCALEMODE_PIXELART: break; + case SDL_SCALEMODE_LINEAR: + if (SDL_ISPIXELFORMAT_INDEXED(texture->format)) { + scaleMode = SDL_SCALEMODE_PIXELART; + } + break; default: return SDL_InvalidParamError("scaleMode"); } @@ -2046,7 +2114,7 @@ bool SDL_SetTextureScaleMode(SDL_Texture *texture, SDL_ScaleMode scaleMode) bool SDL_GetTextureScaleMode(SDL_Texture *texture, SDL_ScaleMode *scaleMode) { if (scaleMode) { - *scaleMode = SDL_SCALEMODE_LINEAR; + *scaleMode = SDL_SCALEMODE_INVALID; } CHECK_TEXTURE_MAGIC(texture, false); @@ -2105,15 +2173,27 @@ static bool SDL_UpdateTextureYUV(SDL_Texture *texture, const SDL_Rect *rect, } #endif // SDL_HAVE_YUV -static bool SDL_UpdateTextureNative(SDL_Texture *texture, const SDL_Rect *rect, - const void *pixels, int pitch) +static bool SDL_UpdateTexturePaletteSurface(SDL_Texture *texture, const SDL_Rect *rect, const void *pixels, int pitch) +{ + SDL_Surface *surface = texture->palette_surface; + + const Uint8 *src = (const Uint8 *)pixels; + Uint8 *dst = ((Uint8 *)surface->pixels) + rect->y * surface->pitch + (rect->x * SDL_BITSPERPIXEL(texture->format)) / 8; + int w = ((rect->w * SDL_BITSPERPIXEL(texture->format)) + 7) / 8; + int h = rect->h; + while (h--) { + SDL_memcpy(dst, src, w); + src += pitch; + dst += surface->pitch; + } + texture->palette_version = 0; + return true; +} + +static bool SDL_UpdateTextureNative(SDL_Texture *texture, const SDL_Rect *rect, const void *pixels, int pitch) { SDL_Texture *native = texture->native; - if (!rect->w || !rect->h) { - return true; // nothing to do. - } - if (texture->access == SDL_TEXTUREACCESS_STREAMING) { // We can lock the texture and copy to it void *native_pixels = NULL; @@ -2174,6 +2254,8 @@ bool SDL_UpdateTexture(SDL_Texture *texture, const SDL_Rect *rect, const void *p } else if (texture->yuv) { return SDL_UpdateTextureYUV(texture, &real_rect, pixels, pitch); #endif + } else if (texture->palette_surface) { + return SDL_UpdateTexturePaletteSurface(texture, &real_rect, pixels, pitch); } else if (texture->native) { return SDL_UpdateTextureNative(texture, &real_rect, pixels, pitch); } else { @@ -2426,8 +2508,16 @@ static bool SDL_LockTextureYUV(SDL_Texture *texture, const SDL_Rect *rect, } #endif // SDL_HAVE_YUV -static bool SDL_LockTextureNative(SDL_Texture *texture, const SDL_Rect *rect, - void **pixels, int *pitch) +static bool SDL_LockTexturePaletteSurface(SDL_Texture *texture, const SDL_Rect *rect, void **pixels, int *pitch) +{ + SDL_Surface *surface = texture->palette_surface; + + *pixels = ((Uint8 *)surface->pixels) + rect->y * surface->pitch + (rect->x * SDL_BITSPERPIXEL(texture->format)) / 8; + *pitch = surface->pitch; + return true; +} + +static bool SDL_LockTextureNative(SDL_Texture *texture, const SDL_Rect *rect, void **pixels, int *pitch) { texture->locked_rect = *rect; *pixels = (void *)((Uint8 *)texture->pixels + @@ -2463,7 +2553,9 @@ bool SDL_LockTexture(SDL_Texture *texture, const SDL_Rect *rect, void **pixels, return SDL_LockTextureYUV(texture, rect, pixels, pitch); } else #endif - if (texture->native) { + if (texture->palette_surface) { + return SDL_LockTexturePaletteSurface(texture, rect, pixels, pitch); + } else if (texture->native) { // Calls a real SDL_LockTexture/SDL_UnlockTexture on unlock, flushing then. return SDL_LockTextureNative(texture, rect, pixels, pitch); } else { @@ -2504,6 +2596,9 @@ bool SDL_LockTextureToSurface(SDL_Texture *texture, const SDL_Rect *rect, SDL_Su SDL_UnlockTexture(texture); return false; } + if (texture->palette) { + SDL_SetSurfacePalette(texture->locked_surface, texture->palette); + } *surface = texture->locked_surface; return true; @@ -2531,6 +2626,11 @@ static void SDL_UnlockTextureYUV(SDL_Texture *texture) } #endif // SDL_HAVE_YUV +static void SDL_UnlockTexturePaletteSurface(SDL_Texture *texture) +{ + texture->palette_version = 0; +} + static void SDL_UnlockTextureNative(SDL_Texture *texture) { SDL_Texture *native = texture->native; @@ -2564,15 +2664,19 @@ void SDL_UnlockTexture(SDL_Texture *texture) SDL_UnlockTextureYUV(texture); } else #endif - if (texture->native) { + if (texture->palette_surface) { + SDL_UnlockTexturePaletteSurface(texture); + } else if (texture->native) { SDL_UnlockTextureNative(texture); } else { SDL_Renderer *renderer = texture->renderer; renderer->UnlockTexture(renderer, texture); } - SDL_DestroySurface(texture->locked_surface); - texture->locked_surface = NULL; + if (texture->locked_surface) { + SDL_DestroySurface(texture->locked_surface); + texture->locked_surface = NULL; + } } bool SDL_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture) @@ -3938,6 +4042,10 @@ bool SDL_RenderTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_F dstrect = &full_dstrect; } + if (!UpdateTexturePalette(texture)) { + return false; + } + if (texture->native) { texture = texture->native; } @@ -3983,6 +4091,10 @@ bool SDL_RenderTextureAffine(SDL_Renderer *renderer, SDL_Texture *texture, GetRenderViewportSize(renderer, &real_dstrect); + if (!UpdateTexturePalette(texture)) { + return false; + } + if (texture->native) { texture = texture->native; } @@ -4111,6 +4223,10 @@ bool SDL_RenderTextureRotated(SDL_Renderer *renderer, SDL_Texture *texture, dstrect = &full_dstrect; } + if (!UpdateTexturePalette(texture)) { + return false; + } + if (texture->native) { texture = texture->native; } @@ -4367,6 +4483,10 @@ bool SDL_RenderTextureTiled(SDL_Renderer *renderer, SDL_Texture *texture, const dstrect = &full_dstrect; } + if (!UpdateTexturePalette(texture)) { + return false; + } + if (texture->native) { texture = texture->native; } @@ -5137,8 +5257,14 @@ bool SDL_RenderGeometryRaw(SDL_Renderer *renderer, return true; } - if (texture && texture->native) { - texture = texture->native; + if (texture) { + if (!UpdateTexturePalette(texture)) { + return false; + } + + if (texture->native) { + texture = texture->native; + } } texture_address_mode_u = renderer->texture_address_mode_u; @@ -5381,6 +5507,10 @@ static void SDL_DestroyTextureInternal(SDL_Texture *texture, bool is_destroying) { SDL_Renderer *renderer; + if (texture->palette) { + SDL_SetTexturePalette(texture, NULL); + } + SDL_DestroyProperties(texture->props); renderer = texture->renderer; @@ -5417,8 +5547,14 @@ static void SDL_DestroyTextureInternal(SDL_Texture *texture, bool is_destroying) renderer->DestroyTexture(renderer, texture); - SDL_DestroySurface(texture->locked_surface); - texture->locked_surface = NULL; + if (texture->palette_surface) { + SDL_DestroySurface(texture->palette_surface); + texture->palette_surface = NULL; + } + if (texture->locked_surface) { + SDL_DestroySurface(texture->locked_surface); + texture->locked_surface = NULL; + } SDL_free(texture); } diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h index 96a7dd5eac..660d022d53 100644 --- a/src/render/SDL_sysrender.h +++ b/src/render/SDL_sysrender.h @@ -81,6 +81,7 @@ struct SDL_Texture int refcount; /**< Application reference count, used when freeing texture */ // Private API definition + SDL_Renderer *renderer; SDL_Colorspace colorspace; // The colorspace of the texture float SDR_white_point; // The SDR white point for this content float HDR_headroom; // The HDR headroom needed by this content @@ -89,8 +90,9 @@ struct SDL_Texture SDL_ScaleMode scaleMode; // The texture scale mode SDL_FColor color; // Texture modulation values SDL_RenderViewState view; // Target texture view state - - SDL_Renderer *renderer; + SDL_Palette *palette; + Uint32 palette_version; + SDL_Surface *palette_surface; // Support for formats not supported directly by the renderer SDL_Texture *native; @@ -233,6 +235,7 @@ struct SDL_Renderer void (*InvalidateCachedState)(SDL_Renderer *renderer); bool (*RunCommandQueue)(SDL_Renderer *renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize); + bool (*UpdateTexturePalette)(SDL_Renderer *renderer, SDL_Texture *texture); bool (*UpdateTexture)(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, const void *pixels, int pitch); diff --git a/src/render/software/SDL_render_sw.c b/src/render/software/SDL_render_sw.c index 5700153312..12a5f0e5c4 100644 --- a/src/render/software/SDL_render_sw.c +++ b/src/render/software/SDL_render_sw.c @@ -126,6 +126,20 @@ static bool SW_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_P return true; } +static bool SW_UpdateTexturePalette(SDL_Renderer *renderer, SDL_Texture *texture) +{ + SDL_Surface *surface = (SDL_Surface *)texture->internal; + SDL_Palette *palette = texture->palette; + + if (!surface->palette) { + surface->palette = SDL_CreatePalette(1 << SDL_BITSPERPIXEL(surface->format)); + if (!surface->palette) { + return false; + } + } + return SDL_SetPaletteColors(surface->palette, palette->colors, 0, palette->ncolors); +} + static bool SW_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, const void *pixels, int pitch) { @@ -1115,6 +1129,9 @@ static void SW_SelectBestFormats(SDL_Renderer *renderer, SDL_PixelFormat format) SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_XRGB8888); SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ARGB8888); } + + // Add 8-bit palettized format + SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_INDEX8); } bool SW_CreateRendererForSurface(SDL_Renderer *renderer, SDL_Surface *surface, SDL_PropertiesID create_props) @@ -1142,6 +1159,7 @@ bool SW_CreateRendererForSurface(SDL_Renderer *renderer, SDL_Surface *surface, S renderer->WindowEvent = SW_WindowEvent; renderer->GetOutputSize = SW_GetOutputSize; renderer->CreateTexture = SW_CreateTexture; + renderer->UpdateTexturePalette = SW_UpdateTexturePalette; renderer->UpdateTexture = SW_UpdateTexture; renderer->LockTexture = SW_LockTexture; renderer->UnlockTexture = SW_UnlockTexture; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7aa1526e5d..640d02b869 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -403,6 +403,7 @@ add_sdl_test_executable(testsoftwaretransparent SOURCES testsoftwaretransparent. add_sdl_test_executable(testsprite MAIN_CALLBACKS NEEDS_RESOURCES TESTUTILS SOURCES testsprite.c) add_sdl_test_executable(testspriteminimal SOURCES testspriteminimal.c ${icon_bmp_header} DEPENDS generate-icon_bmp_header) add_sdl_test_executable(testspritesurface SOURCES testspritesurface.c ${icon_bmp_header} DEPENDS generate-icon_bmp_header) +add_sdl_test_executable(testpalette SOURCES testpalette.c) add_sdl_test_executable(teststreaming NEEDS_RESOURCES TESTUTILS SOURCES teststreaming.c) add_sdl_test_executable(testtimer NONINTERACTIVE NONINTERACTIVE_ARGS --no-interactive NONINTERACTIVE_TIMEOUT 60 SOURCES testtimer.c) add_sdl_test_executable(testurl SOURCES testurl.c) diff --git a/test/testpalette.c b/test/testpalette.c new file mode 100644 index 0000000000..f1efd96e9d --- /dev/null +++ b/test/testpalette.c @@ -0,0 +1,448 @@ +/* + 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. +*/ +/* Simple program: Move N sprites around on the screen as fast as possible */ + +#include +#include + +#ifdef SDL_PLATFORM_EMSCRIPTEN +#include +#endif + +#define WINDOW_WIDTH 640 +#define WINDOW_HEIGHT 480 + + +static const SDL_Color Palette[256] = { + { 255, 0, 0, SDL_ALPHA_OPAQUE }, + { 255, 5, 0, SDL_ALPHA_OPAQUE }, + { 255, 11, 0, SDL_ALPHA_OPAQUE }, + { 255, 17, 0, SDL_ALPHA_OPAQUE }, + { 255, 23, 0, SDL_ALPHA_OPAQUE }, + { 255, 29, 0, SDL_ALPHA_OPAQUE }, + { 255, 35, 0, SDL_ALPHA_OPAQUE }, + { 255, 41, 0, SDL_ALPHA_OPAQUE }, + { 255, 47, 0, SDL_ALPHA_OPAQUE }, + { 255, 53, 0, SDL_ALPHA_OPAQUE }, + { 255, 59, 0, SDL_ALPHA_OPAQUE }, + { 255, 65, 0, SDL_ALPHA_OPAQUE }, + { 255, 71, 0, SDL_ALPHA_OPAQUE }, + { 255, 77, 0, SDL_ALPHA_OPAQUE }, + { 255, 83, 0, SDL_ALPHA_OPAQUE }, + { 255, 89, 0, SDL_ALPHA_OPAQUE }, + { 255, 95, 0, SDL_ALPHA_OPAQUE }, + { 255, 101, 0, SDL_ALPHA_OPAQUE }, + { 255, 107, 0, SDL_ALPHA_OPAQUE }, + { 255, 113, 0, SDL_ALPHA_OPAQUE }, + { 255, 119, 0, SDL_ALPHA_OPAQUE }, + { 255, 125, 0, SDL_ALPHA_OPAQUE }, + { 255, 131, 0, SDL_ALPHA_OPAQUE }, + { 255, 137, 0, SDL_ALPHA_OPAQUE }, + { 255, 143, 0, SDL_ALPHA_OPAQUE }, + { 255, 149, 0, SDL_ALPHA_OPAQUE }, + { 255, 155, 0, SDL_ALPHA_OPAQUE }, + { 255, 161, 0, SDL_ALPHA_OPAQUE }, + { 255, 167, 0, SDL_ALPHA_OPAQUE }, + { 255, 173, 0, SDL_ALPHA_OPAQUE }, + { 255, 179, 0, SDL_ALPHA_OPAQUE }, + { 255, 185, 0, SDL_ALPHA_OPAQUE }, + { 255, 191, 0, SDL_ALPHA_OPAQUE }, + { 255, 197, 0, SDL_ALPHA_OPAQUE }, + { 255, 203, 0, SDL_ALPHA_OPAQUE }, + { 255, 209, 0, SDL_ALPHA_OPAQUE }, + { 255, 215, 0, SDL_ALPHA_OPAQUE }, + { 255, 221, 0, SDL_ALPHA_OPAQUE }, + { 255, 227, 0, SDL_ALPHA_OPAQUE }, + { 255, 233, 0, SDL_ALPHA_OPAQUE }, + { 255, 239, 0, SDL_ALPHA_OPAQUE }, + { 255, 245, 0, SDL_ALPHA_OPAQUE }, + { 255, 251, 0, SDL_ALPHA_OPAQUE }, + { 253, 255, 0, SDL_ALPHA_OPAQUE }, + { 247, 255, 0, SDL_ALPHA_OPAQUE }, + { 241, 255, 0, SDL_ALPHA_OPAQUE }, + { 235, 255, 0, SDL_ALPHA_OPAQUE }, + { 229, 255, 0, SDL_ALPHA_OPAQUE }, + { 223, 255, 0, SDL_ALPHA_OPAQUE }, + { 217, 255, 0, SDL_ALPHA_OPAQUE }, + { 211, 255, 0, SDL_ALPHA_OPAQUE }, + { 205, 255, 0, SDL_ALPHA_OPAQUE }, + { 199, 255, 0, SDL_ALPHA_OPAQUE }, + { 193, 255, 0, SDL_ALPHA_OPAQUE }, + { 187, 255, 0, SDL_ALPHA_OPAQUE }, + { 181, 255, 0, SDL_ALPHA_OPAQUE }, + { 175, 255, 0, SDL_ALPHA_OPAQUE }, + { 169, 255, 0, SDL_ALPHA_OPAQUE }, + { 163, 255, 0, SDL_ALPHA_OPAQUE }, + { 157, 255, 0, SDL_ALPHA_OPAQUE }, + { 151, 255, 0, SDL_ALPHA_OPAQUE }, + { 145, 255, 0, SDL_ALPHA_OPAQUE }, + { 139, 255, 0, SDL_ALPHA_OPAQUE }, + { 133, 255, 0, SDL_ALPHA_OPAQUE }, + { 127, 255, 0, SDL_ALPHA_OPAQUE }, + { 121, 255, 0, SDL_ALPHA_OPAQUE }, + { 115, 255, 0, SDL_ALPHA_OPAQUE }, + { 109, 255, 0, SDL_ALPHA_OPAQUE }, + { 103, 255, 0, SDL_ALPHA_OPAQUE }, + { 97, 255, 0, SDL_ALPHA_OPAQUE }, + { 91, 255, 0, SDL_ALPHA_OPAQUE }, + { 85, 255, 0, SDL_ALPHA_OPAQUE }, + { 79, 255, 0, SDL_ALPHA_OPAQUE }, + { 73, 255, 0, SDL_ALPHA_OPAQUE }, + { 67, 255, 0, SDL_ALPHA_OPAQUE }, + { 61, 255, 0, SDL_ALPHA_OPAQUE }, + { 55, 255, 0, SDL_ALPHA_OPAQUE }, + { 49, 255, 0, SDL_ALPHA_OPAQUE }, + { 43, 255, 0, SDL_ALPHA_OPAQUE }, + { 37, 255, 0, SDL_ALPHA_OPAQUE }, + { 31, 255, 0, SDL_ALPHA_OPAQUE }, + { 25, 255, 0, SDL_ALPHA_OPAQUE }, + { 19, 255, 0, SDL_ALPHA_OPAQUE }, + { 13, 255, 0, SDL_ALPHA_OPAQUE }, + { 7, 255, 0, SDL_ALPHA_OPAQUE }, + { 1, 255, 0, SDL_ALPHA_OPAQUE }, + { 0, 255, 3, SDL_ALPHA_OPAQUE }, + { 0, 255, 9, SDL_ALPHA_OPAQUE }, + { 0, 255, 15, SDL_ALPHA_OPAQUE }, + { 0, 255, 21, SDL_ALPHA_OPAQUE }, + { 0, 255, 27, SDL_ALPHA_OPAQUE }, + { 0, 255, 33, SDL_ALPHA_OPAQUE }, + { 0, 255, 39, SDL_ALPHA_OPAQUE }, + { 0, 255, 45, SDL_ALPHA_OPAQUE }, + { 0, 255, 51, SDL_ALPHA_OPAQUE }, + { 0, 255, 57, SDL_ALPHA_OPAQUE }, + { 0, 255, 63, SDL_ALPHA_OPAQUE }, + { 0, 255, 69, SDL_ALPHA_OPAQUE }, + { 0, 255, 75, SDL_ALPHA_OPAQUE }, + { 0, 255, 81, SDL_ALPHA_OPAQUE }, + { 0, 255, 87, SDL_ALPHA_OPAQUE }, + { 0, 255, 93, SDL_ALPHA_OPAQUE }, + { 0, 255, 99, SDL_ALPHA_OPAQUE }, + { 0, 255, 105, SDL_ALPHA_OPAQUE }, + { 0, 255, 111, SDL_ALPHA_OPAQUE }, + { 0, 255, 117, SDL_ALPHA_OPAQUE }, + { 0, 255, 123, SDL_ALPHA_OPAQUE }, + { 0, 255, 129, SDL_ALPHA_OPAQUE }, + { 0, 255, 135, SDL_ALPHA_OPAQUE }, + { 0, 255, 141, SDL_ALPHA_OPAQUE }, + { 0, 255, 147, SDL_ALPHA_OPAQUE }, + { 0, 255, 153, SDL_ALPHA_OPAQUE }, + { 0, 255, 159, SDL_ALPHA_OPAQUE }, + { 0, 255, 165, SDL_ALPHA_OPAQUE }, + { 0, 255, 171, SDL_ALPHA_OPAQUE }, + { 0, 255, 177, SDL_ALPHA_OPAQUE }, + { 0, 255, 183, SDL_ALPHA_OPAQUE }, + { 0, 255, 189, SDL_ALPHA_OPAQUE }, + { 0, 255, 195, SDL_ALPHA_OPAQUE }, + { 0, 255, 201, SDL_ALPHA_OPAQUE }, + { 0, 255, 207, SDL_ALPHA_OPAQUE }, + { 0, 255, 213, SDL_ALPHA_OPAQUE }, + { 0, 255, 219, SDL_ALPHA_OPAQUE }, + { 0, 255, 225, SDL_ALPHA_OPAQUE }, + { 0, 255, 231, SDL_ALPHA_OPAQUE }, + { 0, 255, 237, SDL_ALPHA_OPAQUE }, + { 0, 255, 243, SDL_ALPHA_OPAQUE }, + { 0, 255, 249, SDL_ALPHA_OPAQUE }, + { 0, 255, 255, SDL_ALPHA_OPAQUE }, + { 0, 249, 255, SDL_ALPHA_OPAQUE }, + { 0, 243, 255, SDL_ALPHA_OPAQUE }, + { 0, 237, 255, SDL_ALPHA_OPAQUE }, + { 0, 231, 255, SDL_ALPHA_OPAQUE }, + { 0, 225, 255, SDL_ALPHA_OPAQUE }, + { 0, 219, 255, SDL_ALPHA_OPAQUE }, + { 0, 213, 255, SDL_ALPHA_OPAQUE }, + { 0, 207, 255, SDL_ALPHA_OPAQUE }, + { 0, 201, 255, SDL_ALPHA_OPAQUE }, + { 0, 195, 255, SDL_ALPHA_OPAQUE }, + { 0, 189, 255, SDL_ALPHA_OPAQUE }, + { 0, 183, 255, SDL_ALPHA_OPAQUE }, + { 0, 177, 255, SDL_ALPHA_OPAQUE }, + { 0, 171, 255, SDL_ALPHA_OPAQUE }, + { 0, 165, 255, SDL_ALPHA_OPAQUE }, + { 0, 159, 255, SDL_ALPHA_OPAQUE }, + { 0, 153, 255, SDL_ALPHA_OPAQUE }, + { 0, 147, 255, SDL_ALPHA_OPAQUE }, + { 0, 141, 255, SDL_ALPHA_OPAQUE }, + { 0, 135, 255, SDL_ALPHA_OPAQUE }, + { 0, 129, 255, SDL_ALPHA_OPAQUE }, + { 0, 123, 255, SDL_ALPHA_OPAQUE }, + { 0, 117, 255, SDL_ALPHA_OPAQUE }, + { 0, 111, 255, SDL_ALPHA_OPAQUE }, + { 0, 105, 255, SDL_ALPHA_OPAQUE }, + { 0, 99, 255, SDL_ALPHA_OPAQUE }, + { 0, 93, 255, SDL_ALPHA_OPAQUE }, + { 0, 87, 255, SDL_ALPHA_OPAQUE }, + { 0, 81, 255, SDL_ALPHA_OPAQUE }, + { 0, 75, 255, SDL_ALPHA_OPAQUE }, + { 0, 69, 255, SDL_ALPHA_OPAQUE }, + { 0, 63, 255, SDL_ALPHA_OPAQUE }, + { 0, 57, 255, SDL_ALPHA_OPAQUE }, + { 0, 51, 255, SDL_ALPHA_OPAQUE }, + { 0, 45, 255, SDL_ALPHA_OPAQUE }, + { 0, 39, 255, SDL_ALPHA_OPAQUE }, + { 0, 33, 255, SDL_ALPHA_OPAQUE }, + { 0, 27, 255, SDL_ALPHA_OPAQUE }, + { 0, 21, 255, SDL_ALPHA_OPAQUE }, + { 0, 15, 255, SDL_ALPHA_OPAQUE }, + { 0, 9, 255, SDL_ALPHA_OPAQUE }, + { 0, 3, 255, SDL_ALPHA_OPAQUE }, + { 1, 0, 255, SDL_ALPHA_OPAQUE }, + { 7, 0, 255, SDL_ALPHA_OPAQUE }, + { 13, 0, 255, SDL_ALPHA_OPAQUE }, + { 19, 0, 255, SDL_ALPHA_OPAQUE }, + { 25, 0, 255, SDL_ALPHA_OPAQUE }, + { 31, 0, 255, SDL_ALPHA_OPAQUE }, + { 37, 0, 255, SDL_ALPHA_OPAQUE }, + { 43, 0, 255, SDL_ALPHA_OPAQUE }, + { 49, 0, 255, SDL_ALPHA_OPAQUE }, + { 55, 0, 255, SDL_ALPHA_OPAQUE }, + { 61, 0, 255, SDL_ALPHA_OPAQUE }, + { 67, 0, 255, SDL_ALPHA_OPAQUE }, + { 73, 0, 255, SDL_ALPHA_OPAQUE }, + { 79, 0, 255, SDL_ALPHA_OPAQUE }, + { 85, 0, 255, SDL_ALPHA_OPAQUE }, + { 91, 0, 255, SDL_ALPHA_OPAQUE }, + { 97, 0, 255, SDL_ALPHA_OPAQUE }, + { 103, 0, 255, SDL_ALPHA_OPAQUE }, + { 109, 0, 255, SDL_ALPHA_OPAQUE }, + { 115, 0, 255, SDL_ALPHA_OPAQUE }, + { 121, 0, 255, SDL_ALPHA_OPAQUE }, + { 127, 0, 255, SDL_ALPHA_OPAQUE }, + { 133, 0, 255, SDL_ALPHA_OPAQUE }, + { 139, 0, 255, SDL_ALPHA_OPAQUE }, + { 145, 0, 255, SDL_ALPHA_OPAQUE }, + { 151, 0, 255, SDL_ALPHA_OPAQUE }, + { 157, 0, 255, SDL_ALPHA_OPAQUE }, + { 163, 0, 255, SDL_ALPHA_OPAQUE }, + { 169, 0, 255, SDL_ALPHA_OPAQUE }, + { 175, 0, 255, SDL_ALPHA_OPAQUE }, + { 181, 0, 255, SDL_ALPHA_OPAQUE }, + { 187, 0, 255, SDL_ALPHA_OPAQUE }, + { 193, 0, 255, SDL_ALPHA_OPAQUE }, + { 199, 0, 255, SDL_ALPHA_OPAQUE }, + { 205, 0, 255, SDL_ALPHA_OPAQUE }, + { 211, 0, 255, SDL_ALPHA_OPAQUE }, + { 217, 0, 255, SDL_ALPHA_OPAQUE }, + { 223, 0, 255, SDL_ALPHA_OPAQUE }, + { 229, 0, 255, SDL_ALPHA_OPAQUE }, + { 235, 0, 255, SDL_ALPHA_OPAQUE }, + { 241, 0, 255, SDL_ALPHA_OPAQUE }, + { 247, 0, 255, SDL_ALPHA_OPAQUE }, + { 253, 0, 255, SDL_ALPHA_OPAQUE }, + { 255, 0, 251, SDL_ALPHA_OPAQUE }, + { 255, 0, 245, SDL_ALPHA_OPAQUE }, + { 255, 0, 239, SDL_ALPHA_OPAQUE }, + { 255, 0, 233, SDL_ALPHA_OPAQUE }, + { 255, 0, 227, SDL_ALPHA_OPAQUE }, + { 255, 0, 221, SDL_ALPHA_OPAQUE }, + { 255, 0, 215, SDL_ALPHA_OPAQUE }, + { 255, 0, 209, SDL_ALPHA_OPAQUE }, + { 255, 0, 203, SDL_ALPHA_OPAQUE }, + { 255, 0, 197, SDL_ALPHA_OPAQUE }, + { 255, 0, 191, SDL_ALPHA_OPAQUE }, + { 255, 0, 185, SDL_ALPHA_OPAQUE }, + { 255, 0, 179, SDL_ALPHA_OPAQUE }, + { 255, 0, 173, SDL_ALPHA_OPAQUE }, + { 255, 0, 167, SDL_ALPHA_OPAQUE }, + { 255, 0, 161, SDL_ALPHA_OPAQUE }, + { 255, 0, 155, SDL_ALPHA_OPAQUE }, + { 255, 0, 149, SDL_ALPHA_OPAQUE }, + { 255, 0, 143, SDL_ALPHA_OPAQUE }, + { 255, 0, 137, SDL_ALPHA_OPAQUE }, + { 255, 0, 131, SDL_ALPHA_OPAQUE }, + { 255, 0, 125, SDL_ALPHA_OPAQUE }, + { 255, 0, 119, SDL_ALPHA_OPAQUE }, + { 255, 0, 113, SDL_ALPHA_OPAQUE }, + { 255, 0, 107, SDL_ALPHA_OPAQUE }, + { 255, 0, 101, SDL_ALPHA_OPAQUE }, + { 255, 0, 95, SDL_ALPHA_OPAQUE }, + { 255, 0, 89, SDL_ALPHA_OPAQUE }, + { 255, 0, 83, SDL_ALPHA_OPAQUE }, + { 255, 0, 77, SDL_ALPHA_OPAQUE }, + { 255, 0, 71, SDL_ALPHA_OPAQUE }, + { 255, 0, 65, SDL_ALPHA_OPAQUE }, + { 255, 0, 59, SDL_ALPHA_OPAQUE }, + { 255, 0, 53, SDL_ALPHA_OPAQUE }, + { 255, 0, 47, SDL_ALPHA_OPAQUE }, + { 255, 0, 41, SDL_ALPHA_OPAQUE }, + { 255, 0, 35, SDL_ALPHA_OPAQUE }, + { 255, 0, 29, SDL_ALPHA_OPAQUE }, + { 255, 0, 23, SDL_ALPHA_OPAQUE }, + { 255, 0, 17, SDL_ALPHA_OPAQUE }, + { 255, 0, 11, SDL_ALPHA_OPAQUE }, + { 255, 0, 5, SDL_ALPHA_OPAQUE } +}; + +static SDL_Renderer *renderer; +static SDL_Palette *palette; +static SDL_Texture *texture; +static SDL_Texture *black_texture; +static SDL_Texture *white_texture; +static int palettePos = 0; +static int paletteDir = -1; +static bool done; + +static bool CreateTextures() +{ + Uint8 data[256]; + int i; + + palette = SDL_CreatePalette(256); + if (!palette) { + return false; + } + + for (i = 0; i < SDL_arraysize(data); i++) { + data[i] = i; + } + + texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_INDEX8, SDL_TEXTUREACCESS_STATIC, 256, 1); + if (!texture) { + return false; + } + SDL_UpdateTexture(texture, NULL, data, SDL_arraysize(data)); + SDL_SetTexturePalette(texture, palette); + + black_texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_INDEX8, SDL_TEXTUREACCESS_STATIC, 1, 1); + if (!black_texture) { + return false; + } + SDL_UpdateTexture(black_texture, NULL, data, SDL_arraysize(data)); + SDL_SetTexturePalette(black_texture, palette); + + white_texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_INDEX8, SDL_TEXTUREACCESS_STATIC, 1, 1); + if (!white_texture) { + return false; + } + SDL_UpdateTexture(white_texture, NULL, data, SDL_arraysize(data)); + SDL_SetTexturePalette(white_texture, palette); + + return true; +} + +static void UpdatePalette(int pos) +{ + int paletteSize = (int)SDL_arraysize(Palette); + + if (pos == 0) { + SDL_SetPaletteColors(palette, Palette, 0, paletteSize); + } else { + SDL_SetPaletteColors(palette, Palette + pos, 0, paletteSize - pos); + SDL_SetPaletteColors(palette, Palette, paletteSize - pos, pos); + } +} + +static void loop(void) +{ + SDL_Event event; + SDL_FRect src = { 0.0f, 0.0f, 1.0f, 1.0f }; + SDL_FRect dst1 = { 0.0f, 0.0f, 32.0f, 32.0f }; + SDL_FRect dst2 = { WINDOW_WIDTH - 32.0f, 0.0f, 32.0f, 32.0f }; + const SDL_Color black = { 0, 0, 0, SDL_ALPHA_OPAQUE }; + const SDL_Color white = { 255, 255, 255, SDL_ALPHA_OPAQUE }; + + /* Check for events */ + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_EVENT_KEY_UP: + switch (event.key.key) { + case SDLK_LEFT: + paletteDir = 1; + break; + case SDLK_RIGHT: + paletteDir = -1; + break; + case SDLK_ESCAPE: + done = true; + break; + default: + break; + } + break; + case SDL_EVENT_QUIT: + done = true; + break; + default: + break; + } + } + + SDL_RenderClear(renderer); + + /* Draw the rainbow texture */ + UpdatePalette(palettePos); + palettePos += paletteDir; + if (palettePos < 0) { + palettePos = SDL_arraysize(Palette) - 1; + } else if (palettePos >= SDL_arraysize(Palette)) { + palettePos = 0; + } + SDL_RenderTexture(renderer, texture, NULL, NULL); + + /* Draw one square with black, and one square with white + * This tests changing palette colors within a single frame + */ + SDL_SetPaletteColors(palette, &black, 0, 1); + SDL_RenderTexture(renderer, black_texture, &src, &dst1); + SDL_SetPaletteColors(palette, &white, 0, 1); + SDL_RenderTexture(renderer, white_texture, &src, &dst2); + + SDL_RenderPresent(renderer); + SDL_Delay(10); + +#ifdef SDL_PLATFORM_EMSCRIPTEN + if (done) { + emscripten_cancel_main_loop(); + } +#endif +} + +int main(int argc, char *argv[]) +{ + SDL_Window *window = NULL; + int return_code = -1; + + if (argc > 1) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "USAGE: %s", argv[0]); + return_code = 1; + goto quit; + } + + if (!SDL_CreateWindowAndRenderer("testpalette", WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_RESIZABLE, &window, &renderer)) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_CreateWindowAndRenderer failed: %s", SDL_GetError()); + return_code = 2; + goto quit; + } + + if (!CreateTextures()) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create textures: %s", SDL_GetError()); + return_code = 3; + goto quit; + } + + /* Main render loop */ + done = false; + +#ifdef SDL_PLATFORM_EMSCRIPTEN + emscripten_set_main_loop(loop, 0, 1); +#else + while (!done) { + loop(); + } +#endif + return_code = 0; +quit: + SDL_DestroyPalette(palette); + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); + return return_code; +} diff --git a/test/testsymbols.c b/test/testsymbols.c index 425bba217a..591d93ca7d 100644 --- a/test/testsymbols.c +++ b/test/testsymbols.c @@ -1332,6 +1332,8 @@ const static struct { SDL_SYMBOL_ITEM(SDL_hid_get_properties), SDL_SYMBOL_ITEM(SDL_GetPixelFormatFromGPUTextureFormat), SDL_SYMBOL_ITEM(SDL_GetGPUTextureFormatFromPixelFormat), + SDL_SYMBOL_ITEM(SDL_SetTexturePalette), + SDL_SYMBOL_ITEM(SDL_GetTexturePalette), /* extra symbols go here (don't modify this line) */ { NULL, NULL } };