From f3926ce9ab6ba2252eca53d3e19c7b47ee02b0e8 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 20 Oct 2025 08:17:41 -0700
Subject: [PATCH] Made image save functions consistent
---
src/IMG_avif.c | 59 +++++++++++++++++++++++------------
src/IMG_bmp.c | 6 +++-
src/IMG_gif.c | 6 +++-
src/IMG_jpg.c | 54 +++++++++++++++++++++++---------
src/IMG_libpng.c | 36 ++++++++++++---------
src/IMG_tga.c | 67 +++++++++++++++++----------------------
src/IMG_webp.c | 81 +++++++++++++++++++++++-------------------------
7 files changed, 178 insertions(+), 131 deletions(-)
diff --git a/src/IMG_avif.c b/src/IMG_avif.c
index 1face428..36a4cde9 100644
--- a/src/IMG_avif.c
+++ b/src/IMG_avif.c
@@ -731,40 +731,61 @@ SDL_Surface *IMG_LoadAVIF_IO(SDL_IOStream *src)
#endif /* LOAD_AVIF */
-bool IMG_SaveAVIF(SDL_Surface *surface, const char *file, int quality)
-{
- SDL_IOStream *dst = SDL_IOFromFile(file, "wb");
- if (dst) {
- return IMG_SaveAVIF_IO(surface, dst, 1, quality);
- } else {
- return false;
- }
-}
+#if SAVE_AVIF
bool IMG_SaveAVIF_IO(SDL_Surface *surface, SDL_IOStream *dst, bool closeio, int quality)
{
bool result = false;
+ if (!surface) {
+ SDL_InvalidParamError("surface");
+ goto done;
+ }
if (!dst) {
- return SDL_SetError("Passed NULL dst");
+ SDL_InvalidParamError("dst");
+ goto done;
}
-#ifdef SAVE_AVIF
- if (!result) {
- result = IMG_SaveAVIF_IO_libavif(surface, dst, quality);
- }
-#else
- (void) surface;
- (void) quality;
- result = SDL_SetError("SDL_image built without AVIF save support");
-#endif
+ result = IMG_SaveAVIF_IO_libavif(surface, dst, quality);
+done:
if (closeio) {
SDL_CloseIO(dst);
}
return result;
}
+bool IMG_SaveAVIF(SDL_Surface *surface, const char *file, int quality)
+{
+ SDL_IOStream *dst = SDL_IOFromFile(file, "wb");
+ if (dst) {
+ return IMG_SaveAVIF_IO(surface, dst, true, quality);
+ } else {
+ return false;
+ }
+}
+
+#else
+
+bool IMG_SaveAVIF_IO(SDL_Surface *surface, SDL_IOStream *dst, bool closeio, int quality)
+{
+ (void)surface;
+ (void)dst;
+ (void)closeio;
+ (void)quality;
+ return SDL_SetError("SDL_image built without AVIF save support");
+}
+
+bool IMG_SaveAVIF(SDL_Surface *surface, const char *file, int quality)
+{
+ (void)surface;
+ (void)file;
+ (void)quality;
+ return SDL_SetError("SDL_image built without AVIF save support");
+}
+
+#endif // SAVE_AVIF
+
#ifdef LOAD_AVIF
static void SetHDRProperties(SDL_Surface *surface, const avifImage *image)
diff --git a/src/IMG_bmp.c b/src/IMG_bmp.c
index 1436d90a..6fecc069 100644
--- a/src/IMG_bmp.c
+++ b/src/IMG_bmp.c
@@ -733,7 +733,11 @@ bool IMG_SaveBMP_IO(SDL_Surface *surface, SDL_IOStream *dst, bool closeio)
bool IMG_SaveBMP(SDL_Surface *surface, const char *file)
{
SDL_IOStream *dst = SDL_IOFromFile(file, "wb");
- return IMG_SaveBMP_IO(surface, dst, true);
+ if (dst) {
+ return IMG_SaveBMP_IO(surface, dst, true);
+ } else {
+ return false;
+ }
}
#else // !SAVE_BMP
diff --git a/src/IMG_gif.c b/src/IMG_gif.c
index c786ae82..469ac8b5 100644
--- a/src/IMG_gif.c
+++ b/src/IMG_gif.c
@@ -2816,7 +2816,11 @@ bool IMG_SaveGIF_IO(SDL_Surface *surface, SDL_IOStream *dst, bool closeio)
bool IMG_SaveGIF(SDL_Surface *surface, const char *file)
{
SDL_IOStream *dst = SDL_IOFromFile(file, "wb");
- return IMG_SaveGIF_IO(surface, dst, true);
+ if (dst) {
+ return IMG_SaveGIF_IO(surface, dst, true);
+ } else {
+ return false;
+ }
}
#else
diff --git a/src/IMG_jpg.c b/src/IMG_jpg.c
index 6d29f523..69fe543b 100644
--- a/src/IMG_jpg.c
+++ b/src/IMG_jpg.c
@@ -757,15 +757,7 @@ static bool IMG_SaveJPG_IO_tinyjpeg(SDL_Surface *surface, SDL_IOStream *dst, int
#endif /* SAVE_JPG && (defined(LOAD_JPG_DYNAMIC) || !defined(WANT_JPEGLIB)) */
-bool IMG_SaveJPG(SDL_Surface *surface, const char *file, int quality)
-{
- SDL_IOStream *dst = SDL_IOFromFile(file, "wb");
- if (dst) {
- return IMG_SaveJPG_IO(surface, dst, 1, quality);
- } else {
- return false;
- }
-}
+#if SAVE_JPG
bool IMG_SaveJPG_IO(SDL_Surface *surface, SDL_IOStream *dst, bool closeio, int quality)
{
@@ -773,11 +765,15 @@ bool IMG_SaveJPG_IO(SDL_Surface *surface, SDL_IOStream *dst, bool closeio, int q
(void)surface;
(void)quality;
+ if (!surface) {
+ SDL_InvalidParamError("surface");
+ goto done;
+ }
if (!dst) {
- return SDL_SetError("Passed NULL dst");
+ SDL_InvalidParamError("dst");
+ goto done;
}
-#if SAVE_JPG
#ifdef USE_JPEGLIB
if (!result) {
result = IMG_SaveJPG_IO_jpeglib(surface, dst, quality);
@@ -790,12 +786,40 @@ bool IMG_SaveJPG_IO(SDL_Surface *surface, SDL_IOStream *dst, bool closeio, int q
}
#endif
-#else
- result = SDL_SetError("SDL_image built without JPEG save support");
-#endif
-
+done:
if (closeio) {
SDL_CloseIO(dst);
}
return result;
}
+
+bool IMG_SaveJPG(SDL_Surface *surface, const char *file, int quality)
+{
+ SDL_IOStream *dst = SDL_IOFromFile(file, "wb");
+ if (dst) {
+ return IMG_SaveJPG_IO(surface, dst, true, quality);
+ } else {
+ return false;
+ }
+}
+
+#else // !SAVE_JPG
+
+bool IMG_SaveJPG_IO(SDL_Surface *surface, SDL_IOStream *dst, bool closeio, int quality)
+{
+ (void)surface;
+ (void)dst;
+ (void)closeio;
+ (void)quality;
+ return SDL_SetError("SDL_image built without JPG save support");
+}
+
+bool IMG_SaveJPG(SDL_Surface *surface, const char *file, int quality)
+{
+ (void)surface;
+ (void)file;
+ (void)quality;
+ return SDL_SetError("SDL_image built without JPG save support");
+}
+
+#endif // SAVE_JPG
diff --git a/src/IMG_libpng.c b/src/IMG_libpng.c
index 82b138fc..8253c9b8 100644
--- a/src/IMG_libpng.c
+++ b/src/IMG_libpng.c
@@ -666,13 +666,9 @@ static bool LIBPNG_SavePNG_IO_Internal(struct png_save_vars *vars, SDL_Surface *
return true;
}
-#endif // SAVE_PNG
bool IMG_SavePNG_IO(SDL_Surface *surface, SDL_IOStream *dst, bool closeio)
{
-#if !SAVE_PNG
- return false;
-#else
if (!surface || !dst) {
SDL_SetError("Surface or SDL_IOStream is NULL");
return false;
@@ -707,30 +703,42 @@ bool IMG_SavePNG_IO(SDL_Surface *surface, SDL_IOStream *dst, bool closeio)
SDL_SetError("%s", vars.error);
}
- if (closeio && dst) {
+ if (closeio) {
SDL_CloseIO(dst);
}
return result;
-#endif
}
bool IMG_SavePNG(SDL_Surface *surface, const char *file)
{
- if (!surface || !file) {
- SDL_SetError("Surface or file name is NULL");
- return false;
- }
-
SDL_IOStream *dst = SDL_IOFromFile(file, "wb");
- if (!dst) {
- SDL_SetError("Failed to open file %s for writing: %s", file, SDL_GetError());
+ if (dst) {
+ return IMG_SavePNG_IO(surface, dst, true);
+ } else {
return false;
}
+}
+
+#else // !SAVE_PNG
- return IMG_SavePNG_IO(surface, dst, true);
+bool IMG_SavePNG_IO(SDL_Surface *surface, SDL_IOStream *dst, bool closeio)
+{
+ (void)surface;
+ (void)dst;
+ (void)closeio;
+ return SDL_SetError("SDL_image built without PNG save support");
+}
+
+bool IMG_SavePNG(SDL_Surface *surface, const char *file)
+{
+ (void)surface;
+ (void)file;
+ return SDL_SetError("SDL_image built without PNG save support");
}
+#endif // SAVE_PNG
+
typedef struct
{
png_uint_32 num_frames;
diff --git a/src/IMG_tga.c b/src/IMG_tga.c
index 2ed0b9ed..a19636d9 100644
--- a/src/IMG_tga.c
+++ b/src/IMG_tga.c
@@ -358,21 +358,19 @@ SDL_Surface *IMG_LoadTGA_IO(SDL_IOStream *src)
bool IMG_SaveTGA_IO(SDL_Surface *surface, SDL_IOStream *dst, bool closeio)
{
- Sint64 start;
- const char *error = NULL;
+ Sint64 start = -1;
struct TGAheader hdr;
- bool retval = false;
SDL_Palette *surface_palette = NULL;
SDL_Surface *temp_surface = NULL;
+ bool result = false;
- if (!surface || !dst) {
-
- if (closeio && dst) {
- SDL_CloseIO(dst);
- }
-
- SDL_SetError("Invalid parameters to IMG_SaveTGA_IO");
- return false;
+ if (!surface) {
+ SDL_InvalidParamError("surface");
+ goto done;
+ }
+ if (!dst) {
+ SDL_InvalidParamError("dst");
+ goto done;
}
start = SDL_TellIO(dst);
@@ -393,8 +391,7 @@ bool IMG_SaveTGA_IO(SDL_Surface *surface, SDL_IOStream *dst, bool closeio)
const SDL_PixelFormatDetails *pixelFormatDetails = SDL_GetPixelFormatDetails(surface->format);
if (!pixelFormatDetails) {
- error = "Failed to get SDL_PixelFormatDetails for surface format";
- goto error;
+ goto done;
}
surface_palette = SDL_GetSurfacePalette(surface);
@@ -432,13 +429,12 @@ bool IMG_SaveTGA_IO(SDL_Surface *surface, SDL_IOStream *dst, bool closeio)
}
break;
default:
- error = "Unsupported SDL_Surface format for TGA saving. Supported formats are INDEX8, BGR24, RGB24, BGRA32, RGBA32, XRGB8888, XRGB1555, ARGB1555.";
- goto error;
+ SDL_SetError("Unsupported SDL_Surface format for TGA saving. Supported formats are INDEX8, BGR24, RGB24, BGRA32, RGBA32, XRGB8888, XRGB1555, ARGB1555.");
+ goto done;
}
if (SDL_WriteIO(dst, &hdr, sizeof(hdr)) != sizeof(hdr)) {
- error = "Error writing TGA header";
- goto error;
+ goto done;
}
if (hdr.has_cmap && surface_palette) {
@@ -452,8 +448,7 @@ bool IMG_SaveTGA_IO(SDL_Surface *surface, SDL_IOStream *dst, bool closeio)
color_entry[1] = colors[i].g;
color_entry[2] = colors[i].r;
if (SDL_WriteIO(dst, color_entry, 3) != 3) {
- error = "Error writing TGA colormap entry (24-bit)";
- goto error;
+ goto done;
}
break;
case 32:
@@ -462,13 +457,12 @@ bool IMG_SaveTGA_IO(SDL_Surface *surface, SDL_IOStream *dst, bool closeio)
color_entry[2] = colors[i].r;
color_entry[3] = colors[i].a;
if (SDL_WriteIO(dst, color_entry, 4) != 4) {
- error = "Error writing TGA colormap entry (32-bit)";
- goto error;
+ goto done;
}
break;
default:
- error = "Unsupported TGA colormap bit depth for saving";
- goto error;
+ SDL_SetError("Unsupported TGA colormap bit depth for saving");
+ goto done;
}
}
}
@@ -497,8 +491,7 @@ bool IMG_SaveTGA_IO(SDL_Surface *surface, SDL_IOStream *dst, bool closeio)
if (target_format != SDL_PIXELFORMAT_UNKNOWN && target_format != surface->format) {
temp_surface = SDL_ConvertSurface(surface, target_format);
if (!temp_surface) {
- error = "Failed to convert surface format for TGA saving";
- goto error;
+ goto done;
}
pixels_to_write = (Uint8 *)temp_surface->pixels;
pitch_to_write = temp_surface->pitch;
@@ -506,43 +499,39 @@ bool IMG_SaveTGA_IO(SDL_Surface *surface, SDL_IOStream *dst, bool closeio)
if (tempPixelFormatDetails) {
bytes_per_pixel = tempPixelFormatDetails->bytes_per_pixel;
} else {
- error = "Failed to get SDL_PixelFormatDetails for converted surface format";
- goto error;
+ goto done;
}
}
for (int y = 0; y < surface->h; ++y) {
if (SDL_WriteIO(dst, pixels_to_write + y * pitch_to_write, (size_t)surface->w * bytes_per_pixel) != (size_t)(surface->w * bytes_per_pixel)) {
- error = "Error writing TGA pixel data";
- goto error;
+ goto done;
}
}
- retval = true;
+ result = true;
-error:
+done:
if (temp_surface) {
SDL_DestroySurface(temp_surface);
}
- if (!retval) {
+ if (!result && !closeio && start != -1) {
SDL_SeekIO(dst, start, SDL_IO_SEEK_SET);
- SDL_SetError("%s", error);
}
- if (closeio && dst) {
+ if (closeio) {
SDL_CloseIO(dst);
}
- return retval;
+ return result;
}
bool IMG_SaveTGA(SDL_Surface *surface, const char *file)
{
SDL_IOStream *dst = SDL_IOFromFile(file, "wb");
- if (!dst) {
+ if (dst) {
+ return IMG_SaveTGA_IO(surface, dst, true);
+ } else {
return false;
}
- bool retval = IMG_SaveTGA_IO(surface, dst, true);
- SDL_CloseIO(dst);
- return retval;
}
#else
diff --git a/src/IMG_webp.c b/src/IMG_webp.c
index 0ad1982e..62b0d12f 100644
--- a/src/IMG_webp.c
+++ b/src/IMG_webp.c
@@ -701,28 +701,30 @@ bool IMG_SaveWEBP_IO(SDL_Surface *surface, SDL_IOStream *dst, bool closeio, floa
WebPPicture pic;
WebPMemoryWriter writer;
SDL_Surface *converted_surface = NULL;
- bool ret = true;
- const char *error = NULL;
+ bool result = false;
bool pic_initialized = false;
bool memorywriter_initialized = false;
bool converted_surface_locked = false;
Sint64 start = -1;
- if (!surface || !dst) {
- error = "Invalid input surface or destination stream.";
- goto cleanup;
+ if (!surface) {
+ SDL_InvalidParamError("surface");
+ goto done;
+ }
+ if (!dst) {
+ SDL_InvalidParamError("dst");
+ goto done;
}
start = SDL_TellIO(dst);
if (!IMG_InitWEBP()) {
- error = SDL_GetError();
- goto cleanup;
+ goto done;
}
if (!lib.WebPConfigInitInternal(&config, WEBP_PRESET_DEFAULT, quality, WEBP_ENCODER_ABI_VERSION)) {
- error = "Failed to initialize WebPConfig.";
- goto cleanup;
+ SDL_SetError("Failed to initialize WebPConfig");
+ goto done;
}
quality = SDL_clamp(quality, 0.0f, 100.0f);
@@ -734,13 +736,13 @@ bool IMG_SaveWEBP_IO(SDL_Surface *surface, SDL_IOStream *dst, bool closeio, floa
config.method = 4;
if (!lib.WebPValidateConfig(&config)) {
- error = "Invalid WebP configuration.";
- goto cleanup;
+ SDL_SetError("Invalid WebP configuration");
+ goto done;
}
if (!lib.WebPPictureInitInternal(&pic, WEBP_ENCODER_ABI_VERSION)) {
- error = "Failed to initialize WebPPicture.";
- goto cleanup;
+ SDL_SetError("Failed to initialize WebPPicture");
+ goto done;
}
pic_initialized = true;
@@ -750,8 +752,7 @@ bool IMG_SaveWEBP_IO(SDL_Surface *surface, SDL_IOStream *dst, bool closeio, floa
if (surface->format != SDL_PIXELFORMAT_RGBA32) {
converted_surface = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_RGBA32);
if (!converted_surface) {
- error = SDL_GetError();
- goto cleanup;
+ goto done;
}
} else {
converted_surface = surface;
@@ -759,15 +760,14 @@ bool IMG_SaveWEBP_IO(SDL_Surface *surface, SDL_IOStream *dst, bool closeio, floa
if (SDL_MUSTLOCK(converted_surface)) {
if (!SDL_LockSurface(converted_surface)) {
- error = SDL_GetError();
- goto cleanup;
+ goto done;
}
converted_surface_locked = true;
}
if (!lib.WebPPictureImportRGBA(&pic, (const uint8_t *)converted_surface->pixels, converted_surface->pitch)) {
- error = "Failed to import RGBA pixels into WebPPicture.";
- goto cleanup;
+ SDL_SetError("Failed to import RGBA pixels into WebPPicture");
+ goto done;
}
if (converted_surface_locked) {
@@ -781,21 +781,22 @@ bool IMG_SaveWEBP_IO(SDL_Surface *surface, SDL_IOStream *dst, bool closeio, floa
pic.custom_ptr = &writer;
if (!lib.WebPEncode(&config, &pic)) {
- error = GetWebPEncodingErrorStringInternal(pic.error_code);
- goto cleanup;
+ SDL_SetError("Failed to encode WebP: %s", GetWebPEncodingErrorStringInternal(pic.error_code));
+ goto done;
}
if (writer.size > 0) {
if (SDL_WriteIO(dst, writer.mem, writer.size) != writer.size) {
- error = "Failed to write all WebP data to destination.";
- goto cleanup;
+ goto done;
}
} else {
- error = "No WebP data generated.";
- goto cleanup;
+ SDL_SetError("No WebP data generated.");
+ goto done;
}
-cleanup:
+ result = true;
+
+done:
if (converted_surface_locked) {
SDL_UnlockSurface(converted_surface);
}
@@ -812,29 +813,25 @@ bool IMG_SaveWEBP_IO(SDL_Surface *surface, SDL_IOStream *dst, bool closeio, floa
lib.WebPMemoryWriterClear(&writer);
}
- if (error) {
- if (!closeio && start != -1) {
- SDL_SeekIO(dst, start, SDL_IO_SEEK_SET);
- }
- SDL_SetError("%s", error);
- ret = false;
+ if (!result && !closeio && start != -1) {
+ SDL_SeekIO(dst, start, SDL_IO_SEEK_SET);
}
if (closeio) {
SDL_CloseIO(dst);
}
- return ret;
+ return result;
}
bool IMG_SaveWEBP(SDL_Surface *surface, const char *file, float quality)
{
SDL_IOStream *dst = SDL_IOFromFile(file, "wb");
- if (!dst) {
+ if (dst) {
+ return IMG_SaveWEBP_IO(surface, dst, true, quality);
+ } else {
return false;
}
-
- return IMG_SaveWEBP_IO(surface, dst, true, quality);
}
struct IMG_AnimationEncoderContext
@@ -1076,14 +1073,14 @@ bool IMG_isWEBP(SDL_IOStream *src)
SDL_Surface *IMG_LoadWEBP_IO(SDL_IOStream *src)
{
(void)src;
- SDL_SetError("SDL_image was not built with WEBP support");
+ SDL_SetError("SDL_image built without WEBP load support");
return NULL;
}
IMG_Animation *IMG_LoadWEBPAnimation_IO(SDL_IOStream *src)
{
(void)src;
- SDL_SetError("SDL_image was not built with WEBP support");
+ SDL_SetError("SDL_image built without WEBP load support");
return NULL;
}
@@ -1091,7 +1088,7 @@ bool IMG_CreateWEBPAnimationDecoder(IMG_AnimationDecoder *decoder, SDL_Propertie
{
(void)decoder;
(void)props;
- return SDL_SetError("SDL_image was not built with WEBP load support");
+ return SDL_SetError("SDL_image built without WEBP load support");
}
#endif // !LOAD_WEBP
@@ -1104,7 +1101,7 @@ bool IMG_SaveWEBP_IO(SDL_Surface *surface, SDL_IOStream *dst, bool closeio, floa
(void)dst;
(void)closeio;
(void)quality;
- return SDL_SetError("SDL_image was not built with WEBP save support");
+ return SDL_SetError("SDL_image built without WEBP save support");
}
bool IMG_SaveWEBP(SDL_Surface *surface, const char *file, float quality)
@@ -1112,14 +1109,14 @@ bool IMG_SaveWEBP(SDL_Surface *surface, const char *file, float quality)
(void)surface;
(void)file;
(void)quality;
- return SDL_SetError("SDL_image was not built with WEBP save support");
+ return SDL_SetError("SDL_image built without WEBP save support");
}
bool IMG_CreateWEBPAnimationEncoder(IMG_AnimationEncoder *encoder, SDL_PropertiesID props)
{
(void)encoder;
(void)props;
- return SDL_SetError("SDL_image was not built with WEBP save support");
+ return SDL_SetError("SDL_image built without WEBP save support");
}
#endif // !SAVE_WEBP