diff --git a/docs/README-migration.md b/docs/README-migration.md index 82910915e8..fe767ba96c 100644 --- a/docs/README-migration.md +++ b/docs/README-migration.md @@ -1644,7 +1644,20 @@ The `format` member of SDL_Surface is now an enumerated pixel format value. You The userdata member of SDL_Surface has been replaced with a more general properties interface, which can be queried with SDL_GetSurfaceProperties() -Indexed format surfaces no longer have a palette by default. Surfaces without a palette will copy the pixels untranslated between surfaces. You should use SDL_CreatePalette() to create a palette and call SDL_SetSurfacePalette() to associate it with the final indexed surface before copying it to color pixels. +Indexed format surfaces no longer have a palette by default. Surfaces without a palette will copy the pixels untranslated between surfaces. + +Code that used to look like this: +```c + SDL_Surface *surface = SDL_CreateRGBSurfaceWithFormat(0, 32, 32, 8, SDL_PIXELFORMAT_INDEX8); + SDL_Palette *palette = surface->format->palette; + ... +``` +should be changed to: +```c + SDL_Surface *surface = SDL_CreateSurface(32, 32, SDL_PIXELFORMAT_INDEX8); + SDL_Palette *palette = SDL_CreateSurfacePalette(surface); + ... +``` Removed the unused 'flags' parameter from SDL_ConvertSurface. diff --git a/include/SDL3/SDL_surface.h b/include/SDL3/SDL_surface.h index fd5096ac9f..2f0ee8c510 100644 --- a/include/SDL3/SDL_surface.h +++ b/include/SDL3/SDL_surface.h @@ -254,6 +254,24 @@ extern SDL_DECLSPEC int SDLCALL SDL_SetSurfaceColorspace(SDL_Surface *surface, S */ extern SDL_DECLSPEC SDL_Colorspace SDLCALL SDL_GetSurfaceColorspace(SDL_Surface *surface); +/** + * Create a palette and associate it with a surface. + * + * This function creates a palette compatible with the provided surface. The palette is then returned for you to modify, and the surface will automatically use the new palette in future operations. You do not need to destroy the returned palette, it will be freed when the reference count reaches 0, usually when the surface is destroyed. + * + * Bitmap surfaces (with format SDL_PIXELFORMAT_INDEX1LSB or SDL_PIXELFORMAT_INDEX1MSB) will have the palette initialized with 0 as white and 1 as black. Other surfaces will get a palette initialized with white in every entry. + * + * If this function is called for a surface that already has a palette, a new palette will be created to replace it. + * + * \param surface the SDL_Surface structure to update. + * \returns a new SDL_Palette structure on success or NULL on failure (e.g. if the surface didn't have an index format); call SDL_GetError() for more information. + * + * \since This function is available since SDL 3.0.0. + * + * \sa SDL_SetPaletteColors + */ +extern SDL_DECLSPEC SDL_Palette * SDLCALL SDL_CreateSurfacePalette(SDL_Surface *surface); + /** * Set the palette used by a surface. * diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index f0742330d2..e1e0e3432d 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -79,6 +79,7 @@ SDL3_0.0.0 { SDL_CreateStorageDirectory; SDL_CreateSurface; SDL_CreateSurfaceFrom; + SDL_CreateSurfacePalette; SDL_CreateSystemCursor; SDL_CreateTLS; SDL_CreateTexture; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index 7d15e9819d..80eb264dd0 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -104,6 +104,7 @@ #define SDL_CreateStorageDirectory SDL_CreateStorageDirectory_REAL #define SDL_CreateSurface SDL_CreateSurface_REAL #define SDL_CreateSurfaceFrom SDL_CreateSurfaceFrom_REAL +#define SDL_CreateSurfacePalette SDL_CreateSurfacePalette_REAL #define SDL_CreateSystemCursor SDL_CreateSystemCursor_REAL #define SDL_CreateTLS SDL_CreateTLS_REAL #define SDL_CreateTexture SDL_CreateTexture_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 27a35a97fe..63034e6b9a 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -124,6 +124,7 @@ SDL_DYNAPI_PROC(SDL_Renderer*,SDL_CreateSoftwareRenderer,(SDL_Surface *a),(a),re SDL_DYNAPI_PROC(int,SDL_CreateStorageDirectory,(SDL_Storage *a, const char *b),(a,b),return) SDL_DYNAPI_PROC(SDL_Surface*,SDL_CreateSurface,(int a, int b, SDL_PixelFormat c),(a,b,c),return) SDL_DYNAPI_PROC(SDL_Surface*,SDL_CreateSurfaceFrom,(int a, int b, SDL_PixelFormat c, void *d, int e),(a,b,c,d,e),return) +SDL_DYNAPI_PROC(SDL_Palette*,SDL_CreateSurfacePalette,(SDL_Surface *a),(a),return) SDL_DYNAPI_PROC(SDL_Cursor*,SDL_CreateSystemCursor,(SDL_SystemCursor a),(a),return) SDL_DYNAPI_PROC(SDL_TLSID,SDL_CreateTLS,(void),(),return) SDL_DYNAPI_PROC(SDL_Texture*,SDL_CreateTexture,(SDL_Renderer *a, SDL_PixelFormat b, int c, int d, int e),(a,b,c,d,e),return) diff --git a/src/video/SDL_bmp.c b/src/video/SDL_bmp.c index b726b20382..1f52862033 100644 --- a/src/video/SDL_bmp.c +++ b/src/video/SDL_bmp.c @@ -439,8 +439,10 @@ SDL_Surface *SDL_LoadBMP_IO(SDL_IOStream *src, SDL_bool closeio) /* Load the palette, if any */ if (SDL_ISPIXELFORMAT_INDEXED(surface->format)) { - int max_colors = (1 << SDL_BITSPERPIXEL(surface->format)); - SDL_Palette *palette; + SDL_Palette *palette = SDL_CreateSurfacePalette(surface); + if (!palette) { + goto done; + } if (SDL_SeekIO(src, fp_offset + 14 + biSize, SDL_IO_SEEK_SET) < 0) { SDL_SetError("Error seeking in datastream"); @@ -456,21 +458,17 @@ SDL_Surface *SDL_LoadBMP_IO(SDL_IOStream *src, SDL_bool closeio) biClrUsed = 1 << biBitCount; } - if (biClrUsed > (Uint32)max_colors) { + if (biClrUsed > (Uint32)palette->ncolors) { biClrUsed = 1 << biBitCount; /* try forcing it? */ - if (biClrUsed > (Uint32)max_colors) { + if (biClrUsed > (Uint32)palette->ncolors) { SDL_SetError("Unsupported or incorrect biClrUsed field"); goto done; } } - - palette = SDL_CreatePalette(biClrUsed); - if (!palette) { - goto done; - } + palette->ncolors = biClrUsed; if (biSize == 12) { - for (i = 0; i < (int)biClrUsed; ++i) { + for (i = 0; i < palette->ncolors; ++i) { if (!SDL_ReadU8(src, &palette->colors[i].b) || !SDL_ReadU8(src, &palette->colors[i].g) || !SDL_ReadU8(src, &palette->colors[i].r)) { @@ -479,7 +477,7 @@ SDL_Surface *SDL_LoadBMP_IO(SDL_IOStream *src, SDL_bool closeio) palette->colors[i].a = SDL_ALPHA_OPAQUE; } } else { - for (i = 0; i < (int)biClrUsed; ++i) { + for (i = 0; i < palette->ncolors; ++i) { if (!SDL_ReadU8(src, &palette->colors[i].b) || !SDL_ReadU8(src, &palette->colors[i].g) || !SDL_ReadU8(src, &palette->colors[i].r) || @@ -494,9 +492,6 @@ SDL_Surface *SDL_LoadBMP_IO(SDL_IOStream *src, SDL_bool closeio) palette->colors[i].a = SDL_ALPHA_OPAQUE; } } - - SDL_SetSurfacePalette(surface, palette); - SDL_DestroyPalette(palette); } /* Read the surface pixels. Note that the bmp image is upside down */ diff --git a/src/video/SDL_surface.c b/src/video/SDL_surface.c index ab24c3466a..1d8a0326ef 100644 --- a/src/video/SDL_surface.c +++ b/src/video/SDL_surface.c @@ -367,6 +367,46 @@ float SDL_GetSurfaceHDRHeadroom(SDL_Surface *surface, SDL_Colorspace colorspace) return 1.0f; } +SDL_Palette *SDL_CreateSurfacePalette(SDL_Surface *surface) +{ + SDL_Palette *palette; + + if (!SDL_SurfaceValid(surface)) { + SDL_InvalidParamError("surface"); + return NULL; + } + + if (!SDL_ISPIXELFORMAT_INDEXED(surface->format)) { + SDL_SetError("The surface is not indexed format"); + return NULL; + } + + palette = SDL_CreatePalette((1 << SDL_BITSPERPIXEL(surface->format))); + if (!palette) { + return NULL; + } + + if (palette->ncolors == 2) { + /* Create a black and white bitmap palette */ + palette->colors[0].r = 0xFF; + palette->colors[0].g = 0xFF; + palette->colors[0].b = 0xFF; + palette->colors[1].r = 0x00; + palette->colors[1].g = 0x00; + palette->colors[1].b = 0x00; + } + + if (SDL_SetSurfacePalette(surface, palette) < 0) { + SDL_DestroyPalette(palette); + return NULL; + } + + /* The surface has retained the palette, we can remove the reference here */ + SDL_assert(palette->refcount == 2); + SDL_DestroyPalette(palette); + return palette; +} + int SDL_SetSurfacePalette(SDL_Surface *surface, SDL_Palette *palette) { if (!SDL_SurfaceValid(surface)) { diff --git a/test/testautomation_surface.c b/test/testautomation_surface.c index 878c3a5096..0d9a81afc5 100644 --- a/test/testautomation_surface.c +++ b/test/testautomation_surface.c @@ -863,9 +863,11 @@ static int surface_testPalette(void *arg) source = SDL_CreateSurface(1, 1, SDL_PIXELFORMAT_INDEX8); SDLTest_AssertCheck(source != NULL, "SDL_CreateSurface()"); + SDLTest_AssertCheck(SDL_GetSurfacePalette(source) == NULL, "SDL_GetSurfacePalette(source)"); surface = SDL_CreateSurface(1, 1, SDL_PIXELFORMAT_INDEX8); SDLTest_AssertCheck(surface != NULL, "SDL_CreateSurface()"); + SDLTest_AssertCheck(SDL_GetSurfacePalette(surface) == NULL, "SDL_GetSurfacePalette(surface)"); pixels = (Uint8 *)surface->pixels; SDLTest_AssertCheck(*pixels == 0, "Expected *pixels == 0 got %u", *pixels); diff --git a/test/testmanymouse.c b/test/testmanymouse.c index 6f9e418ab3..03c672ce3e 100644 --- a/test/testmanymouse.c +++ b/test/testmanymouse.c @@ -157,7 +157,7 @@ static SDL_Texture *CreateTexture(const char *image[], SDL_Renderer *renderer) SDL_memcpy((Uint8 *)surface->pixels + row * surface->pitch, image[4 + row], surface->w); } - palette = SDL_CreatePalette(256); + palette = SDL_CreateSurfacePalette(surface); if (!palette) { SDL_DestroySurface(surface); return NULL; @@ -171,8 +171,6 @@ static SDL_Texture *CreateTexture(const char *image[], SDL_Renderer *renderer) palette->colors['X'].r = 0x00; palette->colors['X'].g = 0x00; palette->colors['X'].b = 0x00; - SDL_SetSurfacePalette(surface, palette); - SDL_DestroyPalette(palette); SDL_SetSurfaceColorKey(surface, SDL_TRUE, ' ');