Added support for textures with palettes

Closes https://github.com/libsdl-org/SDL/pull/6192
This commit is contained in:
Sam Lantinga
2025-09-26 11:44:04 -07:00
parent d42bf59c66
commit 0b4b254a53
11 changed files with 728 additions and 74 deletions

View File

@@ -660,6 +660,7 @@ extern SDL_DECLSPEC SDL_Texture * SDLCALL SDL_CreateTextureFromSurface(SDL_Rende
* pixels, required * pixels, required
* - `SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER`: the height of the texture in * - `SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER`: the height of the texture in
* pixels, required * 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 * - `SDL_PROP_TEXTURE_CREATE_SDR_WHITE_POINT_FLOAT`: for HDR10 and floating
* point textures, this defines the value of 100% diffuse white, with higher * point textures, this defines the value of 100% diffuse white, with higher
* values being displayed in the High Dynamic Range headroom. This defaults * 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_ACCESS_NUMBER "SDL.texture.create.access"
#define SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER "SDL.texture.create.width" #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_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_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_HDR_HEADROOM_FLOAT "SDL.texture.create.HDR_headroom"
#define SDL_PROP_TEXTURE_CREATE_D3D11_TEXTURE_POINTER "SDL.texture.create.d3d11.texture" #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); 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. * 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. * 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 texture the texture to update.
* \param scaleMode the SDL_ScaleMode to use for texture scaling. * \param scaleMode the SDL_ScaleMode to use for texture scaling.

View File

@@ -327,6 +327,8 @@ extern SDL_DECLSPEC SDL_Palette * SDLCALL SDL_CreateSurfacePalette(SDL_Surface *
/** /**
* Set the palette used by a 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. * A single palette can be shared with many surfaces.
* *
* \param surface the SDL_Surface structure to update. * \param surface the SDL_Surface structure to update.

View File

@@ -1257,6 +1257,8 @@ SDL3_0.0.0 {
SDL_hid_get_properties; SDL_hid_get_properties;
SDL_GetPixelFormatFromGPUTextureFormat; SDL_GetPixelFormatFromGPUTextureFormat;
SDL_GetGPUTextureFormatFromPixelFormat; SDL_GetGPUTextureFormatFromPixelFormat;
SDL_SetTexturePalette;
SDL_GetTexturePalette;
# extra symbols go here (don't modify this line) # extra symbols go here (don't modify this line)
local: *; local: *;
}; };

View File

@@ -1283,3 +1283,5 @@
#define SDL_GetPixelFormatFromGPUTextureFormat SDL_GetPixelFormatFromGPUTextureFormat_REAL #define SDL_GetPixelFormatFromGPUTextureFormat SDL_GetPixelFormatFromGPUTextureFormat_REAL
#define SDL_GetGPUTextureFormatFromPixelFormat SDL_GetGPUTextureFormatFromPixelFormat_REAL #define SDL_GetGPUTextureFormatFromPixelFormat SDL_GetGPUTextureFormatFromPixelFormat_REAL
#define JNI_OnLoad JNI_OnLoad_REAL #define JNI_OnLoad JNI_OnLoad_REAL
#define SDL_SetTexturePalette SDL_SetTexturePalette_REAL
#define SDL_GetTexturePalette SDL_GetTexturePalette_REAL

View File

@@ -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_PixelFormat,SDL_GetPixelFormatFromGPUTextureFormat,(SDL_GPUTextureFormat a),(a),return)
SDL_DYNAPI_PROC(SDL_GPUTextureFormat,SDL_GetGPUTextureFormatFromPixelFormat,(SDL_PixelFormat 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(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)

View File

@@ -697,6 +697,46 @@ static bool QueueCmdFillRects(SDL_Renderer *renderer, const SDL_FRect *rects, co
return result; 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) 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); 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); 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 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); 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; SDL_Colorspace default_colorspace;
bool texture_is_fourcc_and_target; bool texture_is_fourcc_and_target;
@@ -1421,8 +1462,8 @@ SDL_Texture *SDL_CreateTextureWithProperties(SDL_Renderer *renderer, SDL_Propert
SDL_SetError("Invalid texture format"); SDL_SetError("Invalid texture format");
return NULL; return NULL;
} }
CHECK_PARAM(SDL_ISPIXELFORMAT_INDEXED(format) && !IsSupportedFormat(renderer, format)) { CHECK_PARAM(SDL_ISPIXELFORMAT_INDEXED(format) && access == SDL_TEXTUREACCESS_TARGET) {
SDL_SetError("Palettized textures are not supported"); SDL_SetError("Palettized textures can't be render targets");
return NULL; return NULL;
} }
CHECK_PARAM(w <= 0 || h <= 0) { 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.b = 1.0f;
texture->color.a = 1.0f; texture->color.a = 1.0f;
texture->blendMode = SDL_ISPIXELFORMAT_ALPHA(format) ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE; texture->blendMode = SDL_ISPIXELFORMAT_ALPHA(format) ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE;
if (renderer->scale_mode == SDL_SCALEMODE_LINEAR &&
SDL_ISPIXELFORMAT_INDEXED(format)) {
texture->scaleMode = SDL_SCALEMODE_PIXELART;
} else {
texture->scaleMode = renderer->scale_mode; texture->scaleMode = renderer->scale_mode;
}
texture->view.pixel_w = w; texture->view.pixel_w = w;
texture->view.pixel_h = h; texture->view.pixel_h = h;
texture->view.viewport.w = -1; 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_FORMAT_NUMBER, closest_format);
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_ACCESS_NUMBER, texture->access);
}
SDL_SetNumberProperty(native_props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, texture->w); SDL_SetNumberProperty(native_props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, texture->w);
SDL_SetNumberProperty(native_props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, texture->h); 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; texture->next = texture->native;
renderer->textures = texture; renderer->textures = texture;
SDL_SetTextureScaleMode(texture->native, texture->scaleMode);
if (texture->format == SDL_PIXELFORMAT_MJPG) { if (texture->format == SDL_PIXELFORMAT_MJPG) {
// We have a custom decode + upload path for this // We have a custom decode + upload path for this
} else if (SDL_ISPIXELFORMAT_FOURCC(texture->format)) { } else if (SDL_ISPIXELFORMAT_FOURCC(texture->format)) {
@@ -1544,6 +1597,12 @@ SDL_Texture *SDL_CreateTextureWithProperties(SDL_Renderer *renderer, SDL_Propert
SDL_DestroyTexture(texture); SDL_DestroyTexture(texture);
return NULL; 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) { } else if (access == SDL_TEXTUREACCESS_STREAMING) {
// The pitch is 4 byte aligned // The pitch is 4 byte aligned
texture->pitch = (((w * SDL_BYTESPERPIXEL(format)) + 3) & ~3); 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 // Now set the properties for the new texture
props = SDL_GetTextureProperties(texture); props = SDL_GetTextureProperties(texture);
SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_COLORSPACE_NUMBER, texture->colorspace); 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) static bool SDL_UpdateTextureFromSurface(SDL_Texture *texture, SDL_Rect *rect, SDL_Surface *surface)
{ {
SDL_TextureAccess access;
bool direct_update; 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) { if (surface->format == texture->format &&
return false; surface->palette == texture->palette &&
} SDL_GetSurfaceColorspace(surface) == texture->colorspace) {
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 (SDL_ISPIXELFORMAT_ALPHA(surface->format) && SDL_SurfaceHasColorKey(surface)) { if (SDL_ISPIXELFORMAT_ALPHA(surface->format) && SDL_SurfaceHasColorKey(surface)) {
/* Surface and Renderer formats are identical. /* Surface and Renderer formats are identical.
* Intermediate conversion is needed to convert color key to alpha (SDL_ConvertColorkeyToAlpha()). */ * 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 (direct_update) {
if (SDL_MUSTLOCK(surface)) { if (SDL_MUSTLOCK(surface)) {
SDL_LockSurface(surface); if (SDL_LockSurface(surface)) {
SDL_UpdateTexture(texture, rect, surface->pixels, surface->pitch); SDL_UpdateTexture(texture, rect, surface->pixels, surface->pitch);
SDL_UnlockSurface(surface); SDL_UnlockSurface(surface);
}
} else { } else {
SDL_UpdateTexture(texture, rect, surface->pixels, surface->pitch); SDL_UpdateTexture(texture, rect, surface->pixels, surface->pitch);
} }
} else { } else {
SDL_Surface *temp = NULL;
// Set up a destination surface for the texture update // 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) { if (temp) {
SDL_UpdateTexture(texture, NULL, temp->pixels, temp->pitch); SDL_UpdateTexture(texture, NULL, temp->pixels, temp->pitch);
SDL_DestroySurface(temp); SDL_DestroySurface(temp);
@@ -1709,7 +1732,7 @@ SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *s
needAlpha = false; needAlpha = false;
} }
// If Palette contains alpha values, promotes to alpha format // If palette contains alpha values, promotes to alpha format
palette = SDL_GetSurfacePalette(surface); palette = SDL_GetSurfacePalette(surface);
if (palette) { if (palette) {
bool is_opaque, has_alpha_channel; 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_ACCESS_NUMBER, SDL_TEXTUREACCESS_STATIC);
SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, surface->w); SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, surface->w);
SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, surface->h); 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); texture = SDL_CreateTextureWithProperties(renderer, props);
SDL_DestroyProperties(props); SDL_DestroyProperties(props);
if (!texture) { if (!texture) {
@@ -1857,6 +1883,44 @@ bool SDL_GetTextureSize(SDL_Texture *texture, float *w, float *h)
return true; 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) bool SDL_SetTextureColorMod(SDL_Texture *texture, Uint8 r, Uint8 g, Uint8 b)
{ {
const float fR = (float)r / 255.0f; const float fR = (float)r / 255.0f;
@@ -2028,9 +2092,13 @@ bool SDL_SetTextureScaleMode(SDL_Texture *texture, SDL_ScaleMode scaleMode)
switch (scaleMode) { switch (scaleMode) {
case SDL_SCALEMODE_NEAREST: case SDL_SCALEMODE_NEAREST:
case SDL_SCALEMODE_LINEAR:
case SDL_SCALEMODE_PIXELART: case SDL_SCALEMODE_PIXELART:
break; break;
case SDL_SCALEMODE_LINEAR:
if (SDL_ISPIXELFORMAT_INDEXED(texture->format)) {
scaleMode = SDL_SCALEMODE_PIXELART;
}
break;
default: default:
return SDL_InvalidParamError("scaleMode"); 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) bool SDL_GetTextureScaleMode(SDL_Texture *texture, SDL_ScaleMode *scaleMode)
{ {
if (scaleMode) { if (scaleMode) {
*scaleMode = SDL_SCALEMODE_LINEAR; *scaleMode = SDL_SCALEMODE_INVALID;
} }
CHECK_TEXTURE_MAGIC(texture, false); CHECK_TEXTURE_MAGIC(texture, false);
@@ -2105,15 +2173,27 @@ static bool SDL_UpdateTextureYUV(SDL_Texture *texture, const SDL_Rect *rect,
} }
#endif // SDL_HAVE_YUV #endif // SDL_HAVE_YUV
static bool SDL_UpdateTextureNative(SDL_Texture *texture, const SDL_Rect *rect, static bool SDL_UpdateTexturePaletteSurface(SDL_Texture *texture, const SDL_Rect *rect, const void *pixels, int pitch)
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; SDL_Texture *native = texture->native;
if (!rect->w || !rect->h) {
return true; // nothing to do.
}
if (texture->access == SDL_TEXTUREACCESS_STREAMING) { if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
// We can lock the texture and copy to it // We can lock the texture and copy to it
void *native_pixels = NULL; 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) { } else if (texture->yuv) {
return SDL_UpdateTextureYUV(texture, &real_rect, pixels, pitch); return SDL_UpdateTextureYUV(texture, &real_rect, pixels, pitch);
#endif #endif
} else if (texture->palette_surface) {
return SDL_UpdateTexturePaletteSurface(texture, &real_rect, pixels, pitch);
} else if (texture->native) { } else if (texture->native) {
return SDL_UpdateTextureNative(texture, &real_rect, pixels, pitch); return SDL_UpdateTextureNative(texture, &real_rect, pixels, pitch);
} else { } else {
@@ -2426,8 +2508,16 @@ static bool SDL_LockTextureYUV(SDL_Texture *texture, const SDL_Rect *rect,
} }
#endif // SDL_HAVE_YUV #endif // SDL_HAVE_YUV
static bool SDL_LockTextureNative(SDL_Texture *texture, const SDL_Rect *rect, static bool SDL_LockTexturePaletteSurface(SDL_Texture *texture, const SDL_Rect *rect, void **pixels, int *pitch)
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; texture->locked_rect = *rect;
*pixels = (void *)((Uint8 *)texture->pixels + *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); return SDL_LockTextureYUV(texture, rect, pixels, pitch);
} else } else
#endif #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. // Calls a real SDL_LockTexture/SDL_UnlockTexture on unlock, flushing then.
return SDL_LockTextureNative(texture, rect, pixels, pitch); return SDL_LockTextureNative(texture, rect, pixels, pitch);
} else { } else {
@@ -2504,6 +2596,9 @@ bool SDL_LockTextureToSurface(SDL_Texture *texture, const SDL_Rect *rect, SDL_Su
SDL_UnlockTexture(texture); SDL_UnlockTexture(texture);
return false; return false;
} }
if (texture->palette) {
SDL_SetSurfacePalette(texture->locked_surface, texture->palette);
}
*surface = texture->locked_surface; *surface = texture->locked_surface;
return true; return true;
@@ -2531,6 +2626,11 @@ static void SDL_UnlockTextureYUV(SDL_Texture *texture)
} }
#endif // SDL_HAVE_YUV #endif // SDL_HAVE_YUV
static void SDL_UnlockTexturePaletteSurface(SDL_Texture *texture)
{
texture->palette_version = 0;
}
static void SDL_UnlockTextureNative(SDL_Texture *texture) static void SDL_UnlockTextureNative(SDL_Texture *texture)
{ {
SDL_Texture *native = texture->native; SDL_Texture *native = texture->native;
@@ -2564,15 +2664,19 @@ void SDL_UnlockTexture(SDL_Texture *texture)
SDL_UnlockTextureYUV(texture); SDL_UnlockTextureYUV(texture);
} else } else
#endif #endif
if (texture->native) { if (texture->palette_surface) {
SDL_UnlockTexturePaletteSurface(texture);
} else if (texture->native) {
SDL_UnlockTextureNative(texture); SDL_UnlockTextureNative(texture);
} else { } else {
SDL_Renderer *renderer = texture->renderer; SDL_Renderer *renderer = texture->renderer;
renderer->UnlockTexture(renderer, texture); renderer->UnlockTexture(renderer, texture);
} }
if (texture->locked_surface) {
SDL_DestroySurface(texture->locked_surface); SDL_DestroySurface(texture->locked_surface);
texture->locked_surface = NULL; texture->locked_surface = NULL;
}
} }
bool SDL_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture) 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; dstrect = &full_dstrect;
} }
if (!UpdateTexturePalette(texture)) {
return false;
}
if (texture->native) { if (texture->native) {
texture = texture->native; texture = texture->native;
} }
@@ -3983,6 +4091,10 @@ bool SDL_RenderTextureAffine(SDL_Renderer *renderer, SDL_Texture *texture,
GetRenderViewportSize(renderer, &real_dstrect); GetRenderViewportSize(renderer, &real_dstrect);
if (!UpdateTexturePalette(texture)) {
return false;
}
if (texture->native) { if (texture->native) {
texture = texture->native; texture = texture->native;
} }
@@ -4111,6 +4223,10 @@ bool SDL_RenderTextureRotated(SDL_Renderer *renderer, SDL_Texture *texture,
dstrect = &full_dstrect; dstrect = &full_dstrect;
} }
if (!UpdateTexturePalette(texture)) {
return false;
}
if (texture->native) { if (texture->native) {
texture = texture->native; texture = texture->native;
} }
@@ -4367,6 +4483,10 @@ bool SDL_RenderTextureTiled(SDL_Renderer *renderer, SDL_Texture *texture, const
dstrect = &full_dstrect; dstrect = &full_dstrect;
} }
if (!UpdateTexturePalette(texture)) {
return false;
}
if (texture->native) { if (texture->native) {
texture = texture->native; texture = texture->native;
} }
@@ -5137,9 +5257,15 @@ bool SDL_RenderGeometryRaw(SDL_Renderer *renderer,
return true; return true;
} }
if (texture && texture->native) { if (texture) {
if (!UpdateTexturePalette(texture)) {
return false;
}
if (texture->native) {
texture = texture->native; texture = texture->native;
} }
}
texture_address_mode_u = renderer->texture_address_mode_u; texture_address_mode_u = renderer->texture_address_mode_u;
texture_address_mode_v = renderer->texture_address_mode_v; texture_address_mode_v = renderer->texture_address_mode_v;
@@ -5381,6 +5507,10 @@ static void SDL_DestroyTextureInternal(SDL_Texture *texture, bool is_destroying)
{ {
SDL_Renderer *renderer; SDL_Renderer *renderer;
if (texture->palette) {
SDL_SetTexturePalette(texture, NULL);
}
SDL_DestroyProperties(texture->props); SDL_DestroyProperties(texture->props);
renderer = texture->renderer; renderer = texture->renderer;
@@ -5417,8 +5547,14 @@ static void SDL_DestroyTextureInternal(SDL_Texture *texture, bool is_destroying)
renderer->DestroyTexture(renderer, texture); renderer->DestroyTexture(renderer, texture);
if (texture->palette_surface) {
SDL_DestroySurface(texture->palette_surface);
texture->palette_surface = NULL;
}
if (texture->locked_surface) {
SDL_DestroySurface(texture->locked_surface); SDL_DestroySurface(texture->locked_surface);
texture->locked_surface = NULL; texture->locked_surface = NULL;
}
SDL_free(texture); SDL_free(texture);
} }

View File

@@ -81,6 +81,7 @@ struct SDL_Texture
int refcount; /**< Application reference count, used when freeing texture */ int refcount; /**< Application reference count, used when freeing texture */
// Private API definition // Private API definition
SDL_Renderer *renderer;
SDL_Colorspace colorspace; // The colorspace of the texture SDL_Colorspace colorspace; // The colorspace of the texture
float SDR_white_point; // The SDR white point for this content float SDR_white_point; // The SDR white point for this content
float HDR_headroom; // The HDR headroom needed by 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_ScaleMode scaleMode; // The texture scale mode
SDL_FColor color; // Texture modulation values SDL_FColor color; // Texture modulation values
SDL_RenderViewState view; // Target texture view state SDL_RenderViewState view; // Target texture view state
SDL_Palette *palette;
SDL_Renderer *renderer; Uint32 palette_version;
SDL_Surface *palette_surface;
// Support for formats not supported directly by the renderer // Support for formats not supported directly by the renderer
SDL_Texture *native; SDL_Texture *native;
@@ -233,6 +235,7 @@ struct SDL_Renderer
void (*InvalidateCachedState)(SDL_Renderer *renderer); void (*InvalidateCachedState)(SDL_Renderer *renderer);
bool (*RunCommandQueue)(SDL_Renderer *renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize); 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, bool (*UpdateTexture)(SDL_Renderer *renderer, SDL_Texture *texture,
const SDL_Rect *rect, const void *pixels, const SDL_Rect *rect, const void *pixels,
int pitch); int pitch);

View File

@@ -126,6 +126,20 @@ static bool SW_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_P
return true; 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, static bool SW_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
const SDL_Rect *rect, const void *pixels, int pitch) 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_XRGB8888);
SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ARGB8888); 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) 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->WindowEvent = SW_WindowEvent;
renderer->GetOutputSize = SW_GetOutputSize; renderer->GetOutputSize = SW_GetOutputSize;
renderer->CreateTexture = SW_CreateTexture; renderer->CreateTexture = SW_CreateTexture;
renderer->UpdateTexturePalette = SW_UpdateTexturePalette;
renderer->UpdateTexture = SW_UpdateTexture; renderer->UpdateTexture = SW_UpdateTexture;
renderer->LockTexture = SW_LockTexture; renderer->LockTexture = SW_LockTexture;
renderer->UnlockTexture = SW_UnlockTexture; renderer->UnlockTexture = SW_UnlockTexture;

View File

@@ -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(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(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(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(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(testtimer NONINTERACTIVE NONINTERACTIVE_ARGS --no-interactive NONINTERACTIVE_TIMEOUT 60 SOURCES testtimer.c)
add_sdl_test_executable(testurl SOURCES testurl.c) add_sdl_test_executable(testurl SOURCES testurl.c)

448
test/testpalette.c Normal file
View File

@@ -0,0 +1,448 @@
/*
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
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 <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#ifdef SDL_PLATFORM_EMSCRIPTEN
#include <emscripten/emscripten.h>
#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;
}

View File

@@ -1332,6 +1332,8 @@ const static struct {
SDL_SYMBOL_ITEM(SDL_hid_get_properties), SDL_SYMBOL_ITEM(SDL_hid_get_properties),
SDL_SYMBOL_ITEM(SDL_GetPixelFormatFromGPUTextureFormat), SDL_SYMBOL_ITEM(SDL_GetPixelFormatFromGPUTextureFormat),
SDL_SYMBOL_ITEM(SDL_GetGPUTextureFormatFromPixelFormat), SDL_SYMBOL_ITEM(SDL_GetGPUTextureFormatFromPixelFormat),
SDL_SYMBOL_ITEM(SDL_SetTexturePalette),
SDL_SYMBOL_ITEM(SDL_GetTexturePalette),
/* extra symbols go here (don't modify this line) */ /* extra symbols go here (don't modify this line) */
{ NULL, NULL } { NULL, NULL }
}; };