From c1600c4764c39fc15d1e1a4ffabe21919ac237a7 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Thu, 25 Jan 2024 17:56:00 -0800
Subject: [PATCH] Added support for saving AVIF images
---
CHANGES.txt | 2 +
CMakeLists.txt | 6 +-
examples/showimage.c | 8 +-
include/SDL3_image/SDL_image.h | 38 ++++-
src/IMG_avif.c | 269 +++++++++++++++++++++++++++++++--
test/main.c | 22 +--
6 files changed, 310 insertions(+), 35 deletions(-)
diff --git a/CHANGES.txt b/CHANGES.txt
index a01aacdb..c485d4ce 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,2 +1,4 @@
3.0.0:
* Added support for loading HDR AVIF images
+ * Added AVIF save support:
+ IMG_SaveAVIF() and IMG_SaveAVIF_RW()
diff --git a/CMakeLists.txt b/CMakeLists.txt
index fb81ce4f..d46cab69 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -110,6 +110,7 @@ option(SDL3IMAGE_XCF "Support loading XCF images" ON)
option(SDL3IMAGE_XPM "Support loading XPM images" ON)
option(SDL3IMAGE_XV "Support loading XV images" ON)
+cmake_dependent_option(SDL3IMAGE_AVIF_SAVE "Add AVIF save support" ON SDL3IMAGE_AVIF OFF)
cmake_dependent_option(SDL3IMAGE_JPG_SAVE "Add JPEG save support" ON SDL3IMAGE_JPG OFF)
cmake_dependent_option(SDL3IMAGE_PNG_SAVE "Add PNG save support" ON SDL3IMAGE_PNG OFF)
@@ -506,7 +507,10 @@ if(SDL3IMAGE_AVIF)
endif()
endif()
if(SDL3IMAGE_AVIF_ENABLED)
- target_compile_definitions(${sdl3_image_target_name} PRIVATE LOAD_AVIF)
+ target_compile_definitions(${sdl3_image_target_name} PRIVATE
+ LOAD_AVIF
+ SDL_IMAGE_SAVE_AVIF=$<BOOL:${SDL3IMAGE_AVIF_SAVE}>
+ )
if(SDL3IMAGE_AVIF_SHARED)
target_include_directories(${sdl3_image_target_name} PRIVATE
$<TARGET_PROPERTY:avif,INCLUDE_DIRECTORIES>
diff --git a/examples/showimage.c b/examples/showimage.c
index 47853703..81b5edf8 100644
--- a/examples/showimage.c
+++ b/examples/showimage.c
@@ -118,12 +118,16 @@ int main(int argc, char *argv[])
if (surface) {
int result;
const char *ext = SDL_strrchr(saveFile, '.');
- if ( ext && SDL_strcasecmp(ext, ".bmp") == 0 ) {
+ if ( ext && SDL_strcasecmp(ext, ".avif") == 0 ) {
+ result = IMG_SaveAVIF(surface, saveFile, 90);
+ } else if ( ext && SDL_strcasecmp(ext, ".bmp") == 0 ) {
result = SDL_SaveBMP(surface, saveFile);
} else if ( ext && SDL_strcasecmp(ext, ".jpg") == 0 ) {
result = IMG_SaveJPG(surface, saveFile, 90);
- } else {
+ } else if ( ext && SDL_strcasecmp(ext, ".png") == 0 ) {
result = IMG_SavePNG(surface, saveFile);
+ } else {
+ result = SDL_SetError("Unknown save file type");
}
if ( result < 0 ) {
SDL_Log("Couldn't save %s: %s\n", saveFile, SDL_GetError());
diff --git a/include/SDL3_image/SDL_image.h b/include/SDL3_image/SDL_image.h
index 4d058856..a041d636 100644
--- a/include/SDL3_image/SDL_image.h
+++ b/include/SDL3_image/SDL_image.h
@@ -1969,6 +1969,36 @@ extern DECLSPEC SDL_Surface * SDLCALL IMG_ReadXPMFromArray(char **xpm);
*/
extern DECLSPEC SDL_Surface * SDLCALL IMG_ReadXPMFromArrayToRGB888(char **xpm);
+/**
+ * Save an SDL_Surface into a AVIF image file.
+ *
+ * If the file already exists, it will be overwritten.
+ *
+ * \param surface the SDL surface to save
+ * \param file path on the filesystem to write new file to.
+ * \returns 0 if successful, -1 on error
+ *
+ * \since This function is available since SDL_image 3.0.0.
+ *
+ * \sa IMG_SaveAVIF_RW
+ */
+extern DECLSPEC int SDLCALL IMG_SaveAVIF(SDL_Surface *surface, const char *file, int quality);
+
+/**
+ * Save an SDL_Surface into AVIF image data, via an SDL_RWops.
+ *
+ * If you just want to save to a filename, you can use IMG_SaveAVIF() instead.
+ *
+ * \param surface the SDL surface to save
+ * \param dst the SDL_RWops to save the image data to.
+ * \returns 0 if successful, -1 on error.
+ *
+ * \since This function is available since SDL_image 3.0.0.
+ *
+ * \sa IMG_SaveAVIF
+ */
+extern DECLSPEC int SDLCALL IMG_SaveAVIF_RW(SDL_Surface *surface, SDL_RWops *dst, int freedst, int quality);
+
/**
* Save an SDL_Surface into a PNG image file.
*
@@ -1981,8 +2011,6 @@ extern DECLSPEC SDL_Surface * SDLCALL IMG_ReadXPMFromArrayToRGB888(char **xpm);
* \since This function is available since SDL_image 3.0.0.
*
* \sa IMG_SavePNG_RW
- * \sa IMG_SaveJPG
- * \sa IMG_SaveJPG_RW
*/
extern DECLSPEC int SDLCALL IMG_SavePNG(SDL_Surface *surface, const char *file);
@@ -1998,8 +2026,6 @@ extern DECLSPEC int SDLCALL IMG_SavePNG(SDL_Surface *surface, const char *file);
* \since This function is available since SDL_image 3.0.0.
*
* \sa IMG_SavePNG
- * \sa IMG_SaveJPG
- * \sa IMG_SaveJPG_RW
*/
extern DECLSPEC int SDLCALL IMG_SavePNG_RW(SDL_Surface *surface, SDL_RWops *dst, int freedst);
@@ -2017,8 +2043,6 @@ extern DECLSPEC int SDLCALL IMG_SavePNG_RW(SDL_Surface *surface, SDL_RWops *dst,
* \since This function is available since SDL_image 3.0.0.
*
* \sa IMG_SaveJPG_RW
- * \sa IMG_SavePNG
- * \sa IMG_SavePNG_RW
*/
extern DECLSPEC int SDLCALL IMG_SaveJPG(SDL_Surface *surface, const char *file, int quality);
@@ -2034,8 +2058,6 @@ extern DECLSPEC int SDLCALL IMG_SaveJPG(SDL_Surface *surface, const char *file,
* \since This function is available since SDL_image 3.0.0.
*
* \sa IMG_SaveJPG
- * \sa IMG_SavePNG
- * \sa IMG_SavePNG_RW
*/
extern DECLSPEC int SDLCALL IMG_SaveJPG_RW(SDL_Surface *surface, SDL_RWops *dst, int freedst, int quality);
diff --git a/src/IMG_avif.c b/src/IMG_avif.c
index dfa97d35..143e57a9 100644
--- a/src/IMG_avif.c
+++ b/src/IMG_avif.c
@@ -24,6 +24,11 @@
#include <SDL3_image/SDL_image.h>
#include "IMG.h"
+/* We'll have AVIF save support by default */
+#if !defined(SDL_IMAGE_SAVE_AVIF)
+# define SDL_IMAGE_SAVE_AVIF 1
+#endif
+
#ifdef LOAD_AVIF
#include <avif/avif.h>
@@ -33,13 +38,22 @@
static struct {
int loaded;
void *handle;
- avifBool (*avifPeekCompatibleFileType)(const avifROData * input);
avifDecoder * (*avifDecoderCreate)(void);
void (*avifDecoderDestroy)(avifDecoder * decoder);
- void (*avifDecoderSetIO)(avifDecoder * decoder, avifIO * io);
- avifResult (*avifDecoderParse)(avifDecoder * decoder);
avifResult (*avifDecoderNextImage)(avifDecoder * decoder);
+ avifResult (*avifDecoderParse)(avifDecoder * decoder);
+ void (*avifDecoderSetIO)(avifDecoder * decoder, avifIO * io);
+ avifResult (*avifEncoderAddImage)(avifEncoder * encoder, const avifImage * image, uint64_t durationInTimescales, avifAddImageFlags addImageFlags);
+ avifEncoder * (*avifEncoderCreate)(void);
+ void (*avifEncoderDestroy)(avifEncoder * encoder);
+ avifResult (*avifEncoderFinish)(avifEncoder * encoder, avifRWData * output);
+ avifImage * (*avifImageCreate)(uint32_t width, uint32_t height, uint32_t depth, avifPixelFormat yuvFormat);
+ void (*avifImageDestroy)(avifImage * image);
+ avifResult (*avifImageRGBToYUV)(avifImage * image, const avifRGBImage * rgb);
avifResult (*avifImageYUVToRGB)(const avifImage * image, avifRGBImage * rgb);
+ avifBool (*avifPeekCompatibleFileType)(const avifROData * input);
+ void (*avifRGBImageSetDefaults)(avifRGBImage * rgb, const avifImage * image);
+ void (*avifRWDataFree)(avifRWData * raw);
const char * (*avifResultToString)(avifResult res);
} lib;
@@ -66,14 +80,23 @@ int IMG_InitAVIF(void)
return -1;
}
#endif
- FUNCTION_LOADER(avifPeekCompatibleFileType, avifBool (*)(const avifROData * input))
- FUNCTION_LOADER(avifDecoderCreate, avifDecoder *(*)(void))
+ FUNCTION_LOADER(avifDecoderCreate, avifDecoder * (*)(void))
FUNCTION_LOADER(avifDecoderDestroy, void (*)(avifDecoder * decoder))
- FUNCTION_LOADER(avifDecoderSetIO, void (*)(avifDecoder * decoder, avifIO * io))
- FUNCTION_LOADER(avifDecoderParse, avifResult (*)(avifDecoder * decoder))
FUNCTION_LOADER(avifDecoderNextImage, avifResult (*)(avifDecoder * decoder))
+ FUNCTION_LOADER(avifDecoderParse, avifResult (*)(avifDecoder * decoder))
+ FUNCTION_LOADER(avifDecoderSetIO, void (*)(avifDecoder * decoder, avifIO * io))
+ FUNCTION_LOADER(avifEncoderAddImage, avifResult (*)(avifEncoder * encoder, const avifImage * image, uint64_t durationInTimescales, avifAddImageFlags addImageFlags))
+ FUNCTION_LOADER(avifEncoderCreate, avifEncoder * (*)(void))
+ FUNCTION_LOADER(avifEncoderDestroy, void (*)(avifEncoder * encoder))
+ FUNCTION_LOADER(avifEncoderFinish, avifResult (*)(avifEncoder * encoder, avifRWData * output))
+ FUNCTION_LOADER(avifImageCreate, avifImage * (*)(uint32_t width, uint32_t height, uint32_t depth, avifPixelFormat yuvFormat))
+ FUNCTION_LOADER(avifImageDestroy, void (*)(avifImage * image))
+ FUNCTION_LOADER(avifImageRGBToYUV, avifResult (*)(avifImage * image, const avifRGBImage * rgb))
FUNCTION_LOADER(avifImageYUVToRGB, avifResult (*)(const avifImage * image, avifRGBImage * rgb))
- FUNCTION_LOADER(avifResultToString, const char *(*)(avifResult res))
+ FUNCTION_LOADER(avifPeekCompatibleFileType, avifBool (*)(const avifROData * input))
+ FUNCTION_LOADER(avifRGBImageSetDefaults, void (*)(avifRGBImage * rgb, const avifImage * image))
+ FUNCTION_LOADER(avifRWDataFree, void (*)(avifRWData * raw))
+ FUNCTION_LOADER(avifResultToString, const char * (*)(avifResult res))
}
++lib.loaded;
@@ -163,8 +186,9 @@ int IMG_isAVIF(SDL_RWops *src)
Uint8 *data;
size_t size;
- if ( !src )
+ if (!src) {
return 0;
+ }
start = SDL_RWtell(src);
is_AVIF = 0;
@@ -400,12 +424,6 @@ SDL_Surface *IMG_LoadAVIF_RW(SDL_RWops *src)
goto done;
}
- surface = SDL_CreateSurface(image->width, image->height, SDL_PIXELFORMAT_ARGB8888);
- if (!surface) {
- SDL_free(rgb.pixels);
- goto done;
- }
-
surface = SDL_CreateSurface(image->width, image->height, SDL_PIXELFORMAT_XBGR2101010);
if (surface) {
if (ConvertRGB16toXBGR2101010(&rgb, surface) == 0) {
@@ -468,7 +486,195 @@ SDL_Surface *IMG_LoadAVIF_RW(SDL_RWops *src)
return surface;
}
+static int IMG_SaveAVIF_RW_libavif(SDL_Surface *surface, SDL_RWops *dst, int quality)
+{
+ avifImage *image = NULL;
+ avifRGBImage rgb;
+ avifEncoder *encoder = NULL;
+ avifRWData avifOutput = AVIF_DATA_EMPTY;
+ avifResult avifResult;
+ const Uint32 surface_format = surface->format->format;
+ SDL_ColorPrimaries colorPrimaries;
+ SDL_TransferCharacteristics transferCharacteristics;
+ Uint16 maxCLL, maxFALL;
+ SDL_PropertiesID props;
+ int result = -1;
+
+ if (!IMG_Init(IMG_INIT_AVIF)) {
+ return -1;
+ }
+
+ /* Get the colorspace and light level properties, if any */
+ props = SDL_GetSurfaceProperties(surface);
+ colorPrimaries = (SDL_ColorPrimaries)SDL_GetNumberProperty(props, SDL_PROPERTY_SURFACE_COLOR_PRIMARIES_NUMBER, SDL_COLOR_PRIMARIES_BT709);
+ transferCharacteristics = (SDL_TransferCharacteristics)SDL_GetNumberProperty(props, SDL_PROPERTY_SURFACE_TRANSFER_CHARACTERISTICS_NUMBER, SDL_TRANSFER_CHARACTERISTICS_SRGB);
+ maxCLL = (Uint16)SDL_GetNumberProperty(props, SDL_PROPERTY_SURFACE_MAXCLL_NUMBER, 0);
+ maxFALL = (Uint16)SDL_GetNumberProperty(props, SDL_PROPERTY_SURFACE_MAXFALL_NUMBER, 0);
+
+ image = lib.avifImageCreate(surface->w, surface->h, 10, AVIF_PIXEL_FORMAT_YUV444);
+ if (!image) {
+ IMG_SetError("Couldn't create AVIF YUV image");
+ goto done;
+ }
+ image->yuvRange = AVIF_RANGE_FULL;
+ image->colorPrimaries = (avifColorPrimaries)colorPrimaries;
+ image->transferCharacteristics = (avifTransferCharacteristics)transferCharacteristics;
+ image->clli.maxCLL = maxCLL;
+ image->clli.maxPALL = maxFALL;
+
+ SDL_zero(rgb);
+ lib.avifRGBImageSetDefaults(&rgb, image);
+
+ if (SDL_ISPIXELFORMAT_10BIT(surface_format)) {
+ const Uint16 expand_alpha[] = {
+ 0, 0x155, 0x2aa, 0x3ff
+ };
+ int width, height;
+ Uint16 *dst16;
+ Uint32 *src;
+ int srcskip;
+
+ /* We are not actually using YUV, but storing raw GBR (yes not RGB) data */
+ image->yuvFormat = AVIF_PIXEL_FORMAT_YUV444;
+ image->depth = 10;
+ image->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_IDENTITY;
+
+ if (SDL_PIXELORDER(surface_format) == SDL_PACKEDORDER_XRGB ||
+ SDL_PIXELORDER(surface_format) == SDL_PACKEDORDER_ARGB) {
+ rgb.format = AVIF_RGB_FORMAT_RGBA;
+ } else {
+ rgb.format = AVIF_RGB_FORMAT_BGRA;
+ }
+ rgb.ignoreAlpha = SDL_ISPIXELFORMAT_ALPHA(surface_format) ? SDL_FALSE : SDL_TRUE;
+ rgb.depth = 10;
+ rgb.rowBytes = (uint32_t)image->width * 4 * sizeof(Uint16);
+ rgb.pixels = (uint8_t *)SDL_malloc(image->height * rgb.rowBytes);
+ if (!rgb.pixels) {
+ goto done;
+ }
+
+ src = (Uint32 *)surface->pixels;
+ srcskip = surface->pitch - (surface->w * sizeof(Uint32));
+ dst16 = (Uint16 *)rgb.pixels;
+ height = image->height;
+ while (height--) {
+ width = image->width;
+ while (width--) {
+ Uint32 pixel = *src++;
+
+ *dst16++ = (pixel >> 20) & 0x3FF;
+ *dst16++ = (pixel >> 10) & 0x3FF;
+ *dst16++ = (pixel >> 0) & 0x3FF;
+ *dst16++ = expand_alpha[(pixel >> 30) & 0x3];
+ }
+ src = (Uint32 *)(((Uint8 *)src) + srcskip);
+ }
+
+ avifResult = lib.avifImageRGBToYUV(image, &rgb);
+ if (avifResult != AVIF_RESULT_OK) {
+ IMG_SetError("Couldn't convert to YUV: %s", lib.avifResultToString(avifResult));
+ goto done;
+ }
+
+ } else {
+ SDL_Surface *temp = NULL;
+
+ rgb.depth = 8;
+ switch (surface_format) {
+ case SDL_PIXELFORMAT_RGBX32:
+ case SDL_PIXELFORMAT_RGBA32:
+ rgb.format = AVIF_RGB_FORMAT_RGBA;
+ temp = surface;
+ break;
+ case SDL_PIXELFORMAT_XRGB32:
+ case SDL_PIXELFORMAT_ARGB32:
+ rgb.format = AVIF_RGB_FORMAT_ARGB;
+ temp = surface;
+ break;
+ case SDL_PIXELFORMAT_BGRX32:
+ case SDL_PIXELFORMAT_BGRA32:
+ rgb.format = AVIF_RGB_FORMAT_BGRA;
+ temp = surface;
+ break;
+ case SDL_PIXELFORMAT_XBGR32:
+ case SDL_PIXELFORMAT_ABGR32:
+ rgb.format = AVIF_RGB_FORMAT_ABGR;
+ temp = surface;
+ break;
+ default:
+ /* Need to convert to a format libavif understands */
+ rgb.format = AVIF_RGB_FORMAT_RGBA;
+ if (SDL_ISPIXELFORMAT_ALPHA(surface_format)) {
+ temp = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_RGBA32);
+ } else {
+ temp = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_RGBX32);
+ }
+ if (!temp) {
+ goto done;
+ }
+ break;
+ }
+ rgb.ignoreAlpha = SDL_ISPIXELFORMAT_ALPHA(surface_format) ? SDL_FALSE : SDL_TRUE;
+ rgb.pixels = (uint8_t *)temp->pixels;
+ rgb.rowBytes = (uint32_t)temp->pitch;
+
+ /* Convert to YUV */
+ avifResult = lib.avifImageRGBToYUV(image, &rgb);
+
+ /* Do any cleanup */
+ if (temp != surface) {
+ SDL_free(temp);
+ }
+ rgb.pixels = NULL;
+
+ /* Check the result of the conversion */
+ if (avifResult != AVIF_RESULT_OK) {
+ IMG_SetError("Couldn't convert to YUV: %s", lib.avifResultToString(avifResult));
+ goto done;
+ }
+ }
+
+ encoder = lib.avifEncoderCreate();
+ encoder->quality = quality;
+ encoder->qualityAlpha = AVIF_QUALITY_LOSSLESS;
+ encoder->speed = AVIF_SPEED_FASTEST;
+
+ avifResult = lib.avifEncoderAddImage(encoder, image, 1, AVIF_ADD_IMAGE_FLAG_SINGLE);
+ if (avifResult != AVIF_RESULT_OK) {
+ IMG_SetError("Failed to add image to avif encoder: %s", lib.avifResultToString(avifResult));
+ goto done;
+ }
+
+ avifResult = lib.avifEncoderFinish(encoder, &avifOutput);
+ if (avifResult != AVIF_RESULT_OK) {
+ IMG_SetError("Failed to finish encoder: %s", lib.avifResultToString(avifResult));
+ goto done;
+ }
+
+ if (SDL_RWwrite(dst, avifOutput.data, avifOutput.size) == avifOutput.size) {
+ result = 0;
+ }
+
+done:
+ if (rgb.pixels) {
+ SDL_free(rgb.pixels);
+ }
+ if (image) {
+ lib.avifImageDestroy(image);
+ }
+ if (encoder) {
+ lib.avifEncoderDestroy(encoder);
+ }
+ lib.avifRWDataFree(&avifOutput);
+
+ return result;
+}
+
#else
+
+/* We don't have any way to save AVIF files */
+#undef SDL_IMAGE_SAVE_AVIF
+
#if defined(_MSC_VER) && _MSC_VER >= 1300
#pragma warning(disable : 4100) /* warning C4100: 'op' : unreferenced formal parameter */
#endif
@@ -498,3 +704,36 @@ SDL_Surface *IMG_LoadAVIF_RW(SDL_RWops *src)
}
#endif /* LOAD_AVIF */
+
+int IMG_SaveAVIF(SDL_Surface *surface, const char *file, int quality)
+{
+ SDL_RWops *dst = SDL_RWFromFile(file, "wb");
+ if (dst) {
+ return IMG_SaveAVIF_RW(surface, dst, 1, quality);
+ } else {
+ return -1;
+ }
+}
+
+int IMG_SaveAVIF_RW(SDL_Surface *surface, SDL_RWops *dst, int freedst, int quality)
+{
+ int result = -1;
+
+ if (!dst) {
+ return IMG_SetError("Passed NULL dst");
+ }
+
+#if SDL_IMAGE_SAVE_AVIF
+ if (result < 0) {
+ result = IMG_SaveAVIF_RW_libavif(surface, dst, quality);
+ }
+
+#else
+ result = IMG_SetError("SDL_image built without AVIF save support");
+#endif
+
+ if (freedst) {
+ SDL_RWclose(dst);
+ }
+ return result;
+}
diff --git a/test/main.c b/test/main.c
index 9acb9a53..89c55fbe 100644
--- a/test/main.c
+++ b/test/main.c
@@ -150,7 +150,7 @@ static const Format formats[] =
#else
SDL_FALSE,
#endif
- SDL_FALSE, /* can save */
+ SDL_IMAGE_SAVE_AVIF,
IMG_isAVIF,
IMG_LoadAVIF_RW,
},
@@ -305,11 +305,7 @@ static const Format formats[] =
#else
SDL_FALSE,
#endif
-#ifdef SDL_IMAGE_SAVE_PNG
- SDL_IMAGE_SAVE_PNG ? SDL_TRUE : SDL_FALSE,
-#else
- SDL_FALSE,
-#endif
+ SDL_IMAGE_SAVE_PNG,
IMG_isPNG,
IMG_LoadPNG_RW,
},
@@ -838,13 +834,13 @@ FormatSaveTest(const Format *format,
format->initFlag, initResult);
}
- if (SDL_strcmp (format->name, "PNG") == 0) {
+ if (SDL_strcmp (format->name, "AVIF") == 0) {
if (rw) {
dest = SDL_RWFromFile(filename, "wb");
- result = IMG_SavePNG_RW(reference, dest, SDL_FALSE);
+ result = IMG_SaveAVIF_RW(reference, dest, SDL_FALSE, 90);
SDL_RWclose(dest);
} else {
- result = IMG_SavePNG(reference, filename);
+ result = IMG_SaveAVIF(reference, filename, 90);
}
} else if (SDL_strcmp(format->name, "JPG") == 0) {
if (rw) {
@@ -854,6 +850,14 @@ FormatSaveTest(const Format *format,
} else {
result = IMG_SaveJPG(reference, filename, 90);
}
+ } else if (SDL_strcmp (format->name, "PNG") == 0) {
+ if (rw) {
+ dest = SDL_RWFromFile(filename, "wb");
+ result = IMG_SavePNG_RW(reference, dest, SDL_FALSE);
+ SDL_RWclose(dest);
+ } else {
+ result = IMG_SavePNG(reference, filename);
+ }
} else {
SDLTest_AssertCheck(SDL_FALSE, "How do I save %s?", format->name);
goto out;