1051 lines
26 KiB
C
1051 lines
26 KiB
C
/*
|
|
Copyright 1997-2026 Sam Lantinga
|
|
Copyright 2022 Collabora Ltd.
|
|
|
|
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.
|
|
*/
|
|
|
|
#include <SDL3_image/SDL_image.h>
|
|
|
|
#include <SDL3/SDL.h>
|
|
#include <SDL3/SDL_test.h>
|
|
|
|
#if defined(SDL_PLATFORM_OS2) || defined(SDL_PLATFORM_WIN32)
|
|
static const char pathsep[] = "\\";
|
|
#elif defined(SDL_PLATFORM_RISCOS)
|
|
static const char pathsep[] = ".";
|
|
#else
|
|
static const char pathsep[] = "/";
|
|
#endif
|
|
|
|
#if defined(__APPLE__) && !defined(SDL_IMAGE_USE_COMMON_BACKEND)
|
|
# define USING_IMAGEIO 1
|
|
#else
|
|
# define USING_IMAGEIO 0
|
|
#endif
|
|
|
|
typedef enum
|
|
{
|
|
TEST_FILE_DIST,
|
|
TEST_FILE_BUILT
|
|
} TestFileType;
|
|
|
|
static bool
|
|
GetStringBoolean(const char *value, bool default_value)
|
|
{
|
|
if (!value || !*value) {
|
|
return default_value;
|
|
}
|
|
if (*value == '0' || SDL_strcasecmp(value, "false") == 0) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Return the absolute path to a resource file, similar to GLib's
|
|
* g_test_build_filename().
|
|
*
|
|
* If type is TEST_FILE_DIST, look for it in $SDL_TEST_SRCDIR or next
|
|
* to the executable.
|
|
*
|
|
* If type is TEST_FILE_BUILT, look for it in $SDL_TEST_BUILDDIR or next
|
|
* to the executable.
|
|
*
|
|
* Fails and returns NULL if out of memory.
|
|
*/
|
|
static char *
|
|
GetTestFilename(TestFileType type, const char *file)
|
|
{
|
|
const char *base;
|
|
char *path = NULL;
|
|
bool needPathSep = true;
|
|
|
|
if (type == TEST_FILE_DIST) {
|
|
base = SDL_getenv("SDL_TEST_SRCDIR");
|
|
} else {
|
|
base = SDL_getenv("SDL_TEST_BUILDDIR");
|
|
}
|
|
|
|
if (base == NULL) {
|
|
base = SDL_GetBasePath();
|
|
/* SDL_GetBasePath() guarantees a trailing path separator */
|
|
needPathSep = false;
|
|
}
|
|
|
|
if (base != NULL) {
|
|
size_t len = SDL_strlen(base) + SDL_strlen(pathsep) + SDL_strlen(file) + 1;
|
|
|
|
path = SDL_malloc(len);
|
|
if (path == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (needPathSep) {
|
|
SDL_snprintf(path, len, "%s%s%s", base, pathsep, file);
|
|
} else {
|
|
SDL_snprintf(path, len, "%s%s", base, file);
|
|
}
|
|
} else {
|
|
path = SDL_strdup(file);
|
|
if (path == NULL) {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
static SDLTest_CommonState *state;
|
|
|
|
typedef struct
|
|
{
|
|
const char *name;
|
|
const char *sample;
|
|
const char *reference;
|
|
int w;
|
|
int h;
|
|
int tolerance;
|
|
bool canLoad;
|
|
bool canSave;
|
|
bool (SDLCALL * checkFunction)(SDL_IOStream *src);
|
|
SDL_Surface *(SDLCALL * loadFunction)(SDL_IOStream *src);
|
|
} Format;
|
|
|
|
static const Format formats[] =
|
|
{
|
|
{
|
|
"AVIF",
|
|
"sample.avif",
|
|
"sample.bmp",
|
|
23,
|
|
42,
|
|
300,
|
|
#ifdef LOAD_AVIF
|
|
true,
|
|
#else
|
|
false,
|
|
#endif
|
|
#if defined(SAVE_AVIF) && SAVE_AVIF
|
|
true,
|
|
#else
|
|
false,
|
|
#endif
|
|
IMG_isAVIF,
|
|
IMG_LoadAVIF_IO,
|
|
},
|
|
{
|
|
"BMP",
|
|
"sample.bmp",
|
|
"sample.png",
|
|
23,
|
|
42,
|
|
0, /* lossless */
|
|
#ifdef LOAD_BMP
|
|
true,
|
|
#else
|
|
false,
|
|
#endif
|
|
#if defined(SAVE_BMP) && SAVE_BMP
|
|
true,
|
|
#else
|
|
false,
|
|
#endif
|
|
IMG_isBMP,
|
|
IMG_LoadBMP_IO,
|
|
},
|
|
{
|
|
"CUR",
|
|
"sample.cur",
|
|
"sample.bmp",
|
|
23,
|
|
42,
|
|
0, /* lossless */
|
|
#ifdef LOAD_BMP
|
|
true,
|
|
#else
|
|
false,
|
|
#endif
|
|
true, /* can save */
|
|
IMG_isCUR,
|
|
IMG_LoadCUR_IO,
|
|
},
|
|
{
|
|
"GIF",
|
|
"palette.gif",
|
|
"palette.bmp",
|
|
23,
|
|
42,
|
|
10, /* lossless indexed, dithered rgba */
|
|
#if USING_IMAGEIO || defined(LOAD_GIF)
|
|
true,
|
|
#else
|
|
false,
|
|
#endif
|
|
#if defined(SAVE_GIF) && SAVE_GIF
|
|
true,
|
|
#else
|
|
false,
|
|
#endif
|
|
IMG_isGIF,
|
|
IMG_LoadGIF_IO,
|
|
},
|
|
{
|
|
"ICO",
|
|
"sample.ico",
|
|
"sample.bmp",
|
|
23,
|
|
42,
|
|
0, /* lossless */
|
|
#ifdef LOAD_BMP
|
|
true,
|
|
#else
|
|
false,
|
|
#endif
|
|
true, /* can save */
|
|
IMG_isICO,
|
|
IMG_LoadICO_IO,
|
|
},
|
|
{
|
|
"JPG",
|
|
"sample.jpg",
|
|
"sample.bmp",
|
|
23,
|
|
42,
|
|
100,
|
|
#if (USING_IMAGEIO && defined(JPG_USES_IMAGEIO)) || defined(SDL_IMAGE_USE_WIC_BACKEND) || defined(LOAD_JPG)
|
|
true,
|
|
#else
|
|
false,
|
|
#endif
|
|
#if defined(SAVE_JPG) && SAVE_JPG
|
|
true,
|
|
#else
|
|
false,
|
|
#endif
|
|
IMG_isJPG,
|
|
IMG_LoadJPG_IO,
|
|
},
|
|
#if 0 /* Different versions of JXL yield different output images */
|
|
{
|
|
"JXL",
|
|
"sample.jxl",
|
|
"sample.bmp",
|
|
23,
|
|
42,
|
|
300,
|
|
#ifdef LOAD_JXL
|
|
true,
|
|
#else
|
|
false,
|
|
#endif
|
|
false, /* can save */
|
|
IMG_isJXL,
|
|
IMG_LoadJXL_IO,
|
|
},
|
|
#endif
|
|
#if 0
|
|
{
|
|
"LBM",
|
|
"sample.lbm",
|
|
"sample.bmp",
|
|
23,
|
|
42,
|
|
0, /* lossless? */
|
|
#ifdef LOAD_LBM
|
|
true,
|
|
#else
|
|
false,
|
|
#endif
|
|
false, /* can save */
|
|
IMG_isLBM,
|
|
IMG_LoadLBM_IO,
|
|
},
|
|
#endif
|
|
{
|
|
"PCX",
|
|
"sample.pcx",
|
|
"sample.bmp",
|
|
23,
|
|
42,
|
|
0, /* lossless? */
|
|
#ifdef LOAD_PCX
|
|
true,
|
|
#else
|
|
false,
|
|
#endif
|
|
false, /* can save */
|
|
IMG_isPCX,
|
|
IMG_LoadPCX_IO,
|
|
},
|
|
{
|
|
"PNG",
|
|
"sample.png",
|
|
"sample.bmp",
|
|
23,
|
|
42,
|
|
0, /* lossless */
|
|
#if (USING_IMAGEIO && defined(PNG_USES_IMAGEIO)) || defined(SDL_IMAGE_USE_WIC_BACKEND) || defined(LOAD_PNG)
|
|
true,
|
|
#else
|
|
false,
|
|
#endif
|
|
#if defined(SAVE_PNG) && SAVE_PNG
|
|
true,
|
|
#else
|
|
false,
|
|
#endif
|
|
IMG_isPNG,
|
|
IMG_LoadPNG_IO,
|
|
},
|
|
{
|
|
"PNM",
|
|
"sample.pnm",
|
|
"sample.bmp",
|
|
23,
|
|
42,
|
|
0, /* lossless */
|
|
#ifdef LOAD_PNM
|
|
true,
|
|
#else
|
|
false,
|
|
#endif
|
|
false, /* can save */
|
|
IMG_isPNM,
|
|
IMG_LoadPNM_IO,
|
|
},
|
|
{
|
|
"QOI",
|
|
"sample.qoi",
|
|
"sample.bmp",
|
|
23,
|
|
42,
|
|
0, /* lossless */
|
|
#ifdef LOAD_QOI
|
|
true,
|
|
#else
|
|
false,
|
|
#endif
|
|
false, /* can save */
|
|
IMG_isQOI,
|
|
IMG_LoadQOI_IO,
|
|
},
|
|
{
|
|
"SVG",
|
|
"svg.svg",
|
|
"svg.bmp",
|
|
32,
|
|
32,
|
|
100,
|
|
#ifdef LOAD_SVG
|
|
true,
|
|
#else
|
|
false,
|
|
#endif
|
|
false, /* can save */
|
|
IMG_isSVG,
|
|
IMG_LoadSVG_IO,
|
|
},
|
|
{
|
|
"SVG-sized",
|
|
"svg.svg",
|
|
"svg64.bmp",
|
|
64,
|
|
64,
|
|
100,
|
|
#ifdef LOAD_SVG
|
|
true,
|
|
#else
|
|
false,
|
|
#endif
|
|
false, /* can save */
|
|
IMG_isSVG,
|
|
IMG_LoadSVG_IO,
|
|
},
|
|
{
|
|
"SVG-class",
|
|
"svg-class.svg",
|
|
"svg-class.bmp",
|
|
82,
|
|
82,
|
|
0, /* lossless? */
|
|
#ifdef LOAD_SVG
|
|
true,
|
|
#else
|
|
false,
|
|
#endif
|
|
false, /* can save */
|
|
IMG_isSVG,
|
|
IMG_LoadSVG_IO,
|
|
},
|
|
{
|
|
"TGA",
|
|
"sample.tga",
|
|
"sample.bmp",
|
|
23,
|
|
42,
|
|
0, /* lossless? */
|
|
#if USING_IMAGEIO || defined(LOAD_TGA)
|
|
true,
|
|
#else
|
|
false,
|
|
#endif
|
|
#if defined(SAVE_TGA) && SAVE_TGA
|
|
true,
|
|
#else
|
|
false,
|
|
#endif
|
|
NULL,
|
|
IMG_LoadTGA_IO,
|
|
},
|
|
{
|
|
"TIF",
|
|
"sample.tif",
|
|
"sample.bmp",
|
|
23,
|
|
42,
|
|
0, /* lossless */
|
|
#if USING_IMAGEIO || defined(SDL_IMAGE_USE_WIC_BACKEND) || defined(LOAD_TIF)
|
|
true,
|
|
#else
|
|
false,
|
|
#endif
|
|
false, /* can save */
|
|
IMG_isTIF,
|
|
IMG_LoadTIF_IO,
|
|
},
|
|
{
|
|
"WEBP",
|
|
"sample.webp",
|
|
"sample.bmp",
|
|
23,
|
|
42,
|
|
300,
|
|
#ifdef LOAD_WEBP
|
|
true,
|
|
#else
|
|
false,
|
|
#endif
|
|
#if defined(SAVE_WEBP) && SAVE_WEBP
|
|
true,
|
|
#else
|
|
false,
|
|
#endif
|
|
IMG_isWEBP,
|
|
IMG_LoadWEBP_IO,
|
|
},
|
|
{
|
|
"XCF",
|
|
"sample.xcf",
|
|
"sample.bmp",
|
|
23,
|
|
42,
|
|
0, /* lossless */
|
|
#ifdef LOAD_XCF
|
|
true,
|
|
#else
|
|
false,
|
|
#endif
|
|
false, /* can save */
|
|
IMG_isXCF,
|
|
IMG_LoadXCF_IO,
|
|
},
|
|
{
|
|
"XPM",
|
|
"sample.xpm",
|
|
"sample.bmp",
|
|
23,
|
|
42,
|
|
0, /* lossless */
|
|
#ifdef LOAD_XPM
|
|
true,
|
|
#else
|
|
false,
|
|
#endif
|
|
false, /* can save */
|
|
IMG_isXPM,
|
|
IMG_LoadXPM_IO,
|
|
},
|
|
#if 0
|
|
{
|
|
"XV",
|
|
"sample.xv",
|
|
"sample.bmp",
|
|
23,
|
|
42,
|
|
0, /* lossless? */
|
|
#ifdef LOAD_XV
|
|
true,
|
|
#else
|
|
false,
|
|
#endif
|
|
false, /* can save */
|
|
IMG_isXV,
|
|
IMG_LoadXV_IO,
|
|
},
|
|
#endif
|
|
};
|
|
|
|
static bool
|
|
StrHasSuffix(const char *str, const char *suffix)
|
|
{
|
|
size_t str_len = SDL_strlen(str);
|
|
size_t suffix_len = SDL_strlen(suffix);
|
|
|
|
return (str_len >= suffix_len
|
|
&& SDL_strcmp(str + (str_len - suffix_len), suffix) == 0);
|
|
}
|
|
|
|
typedef enum
|
|
{
|
|
LOAD_CONVENIENCE = 0,
|
|
LOAD_IO,
|
|
LOAD_TYPED_IO,
|
|
LOAD_FORMAT_SPECIFIC,
|
|
LOAD_SIZED
|
|
} LoadMode;
|
|
|
|
/* Convert to RGBA for comparison, if necessary */
|
|
static bool
|
|
ConvertToRgba32(SDL_Surface **surface_p)
|
|
{
|
|
if ((*surface_p)->format != SDL_PIXELFORMAT_RGBA32) {
|
|
SDL_Surface *temp;
|
|
|
|
SDL_ClearError();
|
|
temp = SDL_ConvertSurface(*surface_p, SDL_PIXELFORMAT_RGBA32);
|
|
SDLTest_AssertCheck(temp != NULL,
|
|
"Converting to RGBA should succeed (%s)",
|
|
SDL_GetError());
|
|
if (temp == NULL) {
|
|
return false;
|
|
}
|
|
SDL_DestroySurface(*surface_p);
|
|
*surface_p = temp;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void
|
|
DumpPixels(const char *filename, SDL_Surface *surface)
|
|
{
|
|
const SDL_Palette *palette = SDL_GetSurfacePalette(surface);
|
|
const unsigned char *pixels = surface->pixels;
|
|
const unsigned char *p;
|
|
size_t w, h, pitch;
|
|
size_t i, j;
|
|
|
|
SDL_Log("%s:\n", filename);
|
|
|
|
if (palette) {
|
|
size_t n = 0;
|
|
|
|
if (palette->ncolors >= 0) {
|
|
n = (size_t) palette->ncolors;
|
|
}
|
|
|
|
SDL_Log(" Palette:\n");
|
|
for (i = 0; i < n; i++) {
|
|
SDL_Log(" RGBA[0x%02x] = %02x%02x%02x%02x\n",
|
|
(unsigned) i,
|
|
palette->colors[i].r,
|
|
palette->colors[i].g,
|
|
palette->colors[i].b,
|
|
palette->colors[i].a);
|
|
}
|
|
}
|
|
|
|
if (surface->w < 0) {
|
|
SDL_Log(" Invalid width %d\n", surface->w);
|
|
return;
|
|
}
|
|
|
|
if (surface->h < 0) {
|
|
SDL_Log(" Invalid height %d\n", surface->h);
|
|
return;
|
|
}
|
|
|
|
if (surface->pitch < 0) {
|
|
SDL_Log(" Invalid pitch %d\n", surface->pitch);
|
|
return;
|
|
}
|
|
|
|
w = (size_t) surface->w;
|
|
h = (size_t) surface->h;
|
|
pitch = (size_t) surface->pitch;
|
|
|
|
SDL_Log(" Pixels:\n");
|
|
|
|
for (j = 0; j < h; j++) {
|
|
SDL_Log(" ");
|
|
|
|
for (i = 0; i < w; i++) {
|
|
p = pixels + (j * pitch) + (i * SDL_BYTESPERPIXEL(surface->format));
|
|
|
|
switch (SDL_BITSPERPIXEL(surface->format)) {
|
|
case 1:
|
|
case 4:
|
|
case 8:
|
|
SDL_Log("%02x ", *p);
|
|
break;
|
|
|
|
case 12:
|
|
case 15:
|
|
case 16:
|
|
SDL_Log("%02x", *p++);
|
|
SDL_Log("%02x ", *p);
|
|
break;
|
|
|
|
case 24:
|
|
SDL_Log("%02x", *p++);
|
|
SDL_Log("%02x", *p++);
|
|
SDL_Log("%02x ", *p);
|
|
break;
|
|
|
|
case 32:
|
|
SDL_Log("%02x", *p++);
|
|
SDL_Log("%02x", *p++);
|
|
SDL_Log("%02x", *p++);
|
|
SDL_Log("%02x ", *p);
|
|
break;
|
|
}
|
|
}
|
|
|
|
SDL_Log("\n");
|
|
}
|
|
}
|
|
|
|
static void
|
|
FormatLoadTest(const Format *format,
|
|
LoadMode mode)
|
|
{
|
|
SDL_Surface *reference = NULL;
|
|
SDL_Surface *surface = NULL;
|
|
SDL_IOStream *src = NULL;
|
|
char *filename = NULL;
|
|
char *refFilename = NULL;
|
|
int diff;
|
|
|
|
SDL_ClearError();
|
|
filename = GetTestFilename(TEST_FILE_DIST, format->sample);
|
|
if (!SDLTest_AssertCheck(filename != NULL,
|
|
"Building filename should succeed (%s)",
|
|
SDL_GetError())) {
|
|
goto out;
|
|
}
|
|
|
|
SDL_ClearError();
|
|
refFilename = GetTestFilename(TEST_FILE_DIST, format->reference);
|
|
if (!SDLTest_AssertCheck(refFilename != NULL,
|
|
"Building ref filename should succeed (%s)",
|
|
SDL_GetError())) {
|
|
goto out;
|
|
}
|
|
|
|
if (StrHasSuffix(format->reference, ".bmp")) {
|
|
SDL_ClearError();
|
|
SDLTest_AssertPass("About to call SDL_LoadBMP(\"%s\")", refFilename);
|
|
reference = SDL_LoadBMP(refFilename);
|
|
if (!SDLTest_AssertCheck(reference != NULL,
|
|
"Loading reference should succeed (%s)",
|
|
SDL_GetError())) {
|
|
goto out;
|
|
}
|
|
}
|
|
else if (StrHasSuffix (format->reference, ".png")) {
|
|
#ifdef LOAD_PNG
|
|
SDL_ClearError();
|
|
SDLTest_AssertPass("About to call IMG_Load(\"%s\")", refFilename);
|
|
reference = IMG_Load(refFilename);
|
|
if (!SDLTest_AssertCheck(reference != NULL,
|
|
"Loading reference should succeed (%s)",
|
|
SDL_GetError())) {
|
|
goto out;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (mode != LOAD_CONVENIENCE) {
|
|
SDL_ClearError();
|
|
SDLTest_AssertPass("About to call SDL_IOFromFile(\"%s\", \"rb\")", refFilename);
|
|
src = SDL_IOFromFile(filename, "rb");
|
|
SDLTest_AssertCheck(src != NULL,
|
|
"Opening %s should succeed (%s)",
|
|
filename, SDL_GetError());
|
|
if (src == NULL)
|
|
goto out;
|
|
}
|
|
|
|
SDL_ClearError();
|
|
switch (mode) {
|
|
case LOAD_CONVENIENCE:
|
|
SDLTest_AssertPass("About to call IMG_Load(\"%s\")", filename);
|
|
surface = IMG_Load(filename);
|
|
break;
|
|
|
|
case LOAD_IO:
|
|
if (format->checkFunction != NULL) {
|
|
SDL_IOStream *ref_src;
|
|
int check;
|
|
|
|
SDLTest_AssertPass("About to call SDL_IOFromFile(\"%s\", \"rb\")", refFilename);
|
|
ref_src = SDL_IOFromFile(refFilename, "rb");
|
|
SDLTest_AssertCheck(ref_src != NULL,
|
|
"Opening %s should succeed (%s)",
|
|
refFilename, SDL_GetError());
|
|
if (ref_src != NULL) {
|
|
SDLTest_AssertPass("About to call IMG_Is%s(<reference>)", format->name);
|
|
check = format->checkFunction(ref_src);
|
|
SDLTest_AssertCheck(!check,
|
|
"Should not detect %s as %s -> %d",
|
|
refFilename, format->name, check);
|
|
SDL_CloseIO(ref_src);
|
|
}
|
|
}
|
|
|
|
if (format->checkFunction != NULL) {
|
|
SDLTest_AssertPass("About to call IMG_Is%s(<src>)", format->name);
|
|
int check = format->checkFunction(src);
|
|
|
|
SDLTest_AssertCheck(check,
|
|
"Should detect %s as %s -> %d",
|
|
filename, format->name, check);
|
|
}
|
|
|
|
SDL_ClearError();
|
|
SDLTest_AssertPass("About to call IMG_Load_IO(<src>, true)");
|
|
surface = IMG_Load_IO(src, true);
|
|
src = NULL; /* ownership taken */
|
|
break;
|
|
|
|
case LOAD_TYPED_IO:
|
|
SDLTest_AssertPass("About to call IMG_LoadTyped_IO(<src>, true, \"%s\")", format->name);
|
|
surface = IMG_LoadTyped_IO(src, true, format->name);
|
|
src = NULL; /* ownership taken */
|
|
break;
|
|
|
|
case LOAD_FORMAT_SPECIFIC:
|
|
SDLTest_AssertPass("About to call IMG_Load%s_IO(<src>)", format->name);
|
|
surface = format->loadFunction(src);
|
|
break;
|
|
|
|
case LOAD_SIZED:
|
|
if (SDL_strcmp(format->name, "SVG-sized") == 0) {
|
|
surface = IMG_LoadSizedSVG_IO(src, 64, 64);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (!SDLTest_AssertCheck(surface != NULL,
|
|
"Load %s (%s)", filename, SDL_GetError())) {
|
|
goto out;
|
|
}
|
|
|
|
SDLTest_AssertCheck(surface->w == format->w,
|
|
"Expected width %d px, got %d",
|
|
format->w, surface->w);
|
|
SDLTest_AssertCheck(surface->h == format->h,
|
|
"Expected height %d px, got %d",
|
|
format->h, surface->h);
|
|
|
|
if (GetStringBoolean(SDL_getenv("SDL_IMAGE_TEST_DEBUG"), false)) {
|
|
DumpPixels(filename, surface);
|
|
}
|
|
|
|
if (reference != NULL) {
|
|
ConvertToRgba32(&reference);
|
|
ConvertToRgba32(&surface);
|
|
diff = SDLTest_CompareSurfaces(surface, reference, format->tolerance);
|
|
SDLTest_AssertCheck(diff == 0,
|
|
"Surface differed from reference by at most %d in %d pixels",
|
|
format->tolerance, diff);
|
|
if (diff != 0 || GetStringBoolean(SDL_getenv("SDL_IMAGE_TEST_DEBUG"), false)) {
|
|
DumpPixels(filename, surface);
|
|
DumpPixels(refFilename, reference);
|
|
}
|
|
}
|
|
|
|
out:
|
|
if (surface != NULL) {
|
|
SDL_DestroySurface(surface);
|
|
}
|
|
if (reference != NULL) {
|
|
SDL_DestroySurface(reference);
|
|
}
|
|
if (src != NULL) {
|
|
SDL_CloseIO(src);
|
|
}
|
|
if (refFilename != NULL) {
|
|
SDL_free(refFilename);
|
|
}
|
|
if (filename != NULL) {
|
|
SDL_free(filename);
|
|
}
|
|
}
|
|
|
|
static void
|
|
FormatSaveTest(const Format *format,
|
|
bool rw)
|
|
{
|
|
char *refFilename = NULL;
|
|
char filename[64] = { 0 };
|
|
SDL_Surface *reference = NULL;
|
|
SDL_Surface *surface = NULL;
|
|
SDL_Surface *indexed_surface = NULL;
|
|
SDL_IOStream *dest = NULL;
|
|
int diff;
|
|
bool result;
|
|
|
|
SDL_snprintf(filename, sizeof(filename),
|
|
"save%s.%s",
|
|
rw ? "Rwops" : "",
|
|
format->name);
|
|
|
|
SDL_ClearError();
|
|
refFilename = GetTestFilename(TEST_FILE_DIST, "sample.bmp");
|
|
if (!SDLTest_AssertCheck(refFilename != NULL,
|
|
"Building ref filename should succeed (%s)",
|
|
SDL_GetError())) {
|
|
goto out;
|
|
}
|
|
|
|
SDL_ClearError();
|
|
reference = SDL_LoadBMP(refFilename);
|
|
if (!SDLTest_AssertCheck(reference != NULL,
|
|
"Loading reference should succeed (%s)",
|
|
SDL_GetError())) {
|
|
goto out;
|
|
}
|
|
|
|
SDL_ClearError();
|
|
if (rw) {
|
|
dest = SDL_IOFromFile(filename, "wb");
|
|
result = IMG_SaveTyped_IO(reference, dest, false, format->name);
|
|
SDL_CloseIO(dest);
|
|
} else {
|
|
result = IMG_Save(reference, filename);
|
|
}
|
|
|
|
SDLTest_AssertCheck(result, "Save %s (%s)", filename, SDL_GetError());
|
|
|
|
if (format->canLoad) {
|
|
SDL_ClearError();
|
|
SDLTest_AssertPass("About to call IMG_Load(\"%s\")", filename);
|
|
surface = IMG_Load(filename);
|
|
|
|
if (!SDLTest_AssertCheck(surface != NULL,
|
|
"Load %s (%s)", "saved file", SDL_GetError())) {
|
|
goto out;
|
|
}
|
|
|
|
ConvertToRgba32(&reference);
|
|
ConvertToRgba32(&surface);
|
|
|
|
SDLTest_AssertCheck(surface->w == format->w,
|
|
"Expected width %d px, got %d",
|
|
format->w, surface->w);
|
|
SDLTest_AssertCheck(surface->h == format->h,
|
|
"Expected height %d px, got %d",
|
|
format->h, surface->h);
|
|
|
|
diff = SDLTest_CompareSurfaces(surface, reference, format->tolerance);
|
|
SDLTest_AssertCheck(diff == 0,
|
|
"Surface differed from reference by at most %d in %d pixels",
|
|
format->tolerance, diff);
|
|
if (diff != 0 || GetStringBoolean(SDL_getenv("SDL_IMAGE_TEST_DEBUG"), false)) {
|
|
DumpPixels(filename, surface);
|
|
DumpPixels(refFilename, reference);
|
|
}
|
|
}
|
|
|
|
SDLTest_Log("Saving an indexed surface without palette MUST fail");
|
|
indexed_surface = SDL_CreateSurface(32, 32, SDL_PIXELFORMAT_INDEX8);
|
|
SDLTest_AssertCheck(indexed_surface != NULL,
|
|
"SDL_CreateSurface(SDL_PIXELFORMAT_INDEX8) must succeed");
|
|
if (indexed_surface) {
|
|
if (rw) {
|
|
Sint64 position;
|
|
|
|
SDL_IOStream *dyn_io = SDL_IOFromDynamicMem();
|
|
SDLTest_AssertPass("About to call IMG_SaveTyped_IO(\"%s\")", format->name);
|
|
result = IMG_SaveTyped_IO(indexed_surface, dyn_io, false, format->name);
|
|
SDLTest_Assert(!result, "Calling IMG_SaveTyped_IO of an indexed surface without palette should return false (actual=%d)", result);
|
|
position = SDL_TellIO(dyn_io) ;
|
|
SDLTest_AssertCheck(position == 0, "Nothing has been written to IO stream (position=%" SDL_PRIs64 ")", position);
|
|
SDL_CloseIO(dyn_io);
|
|
} else {
|
|
char indexed_filename[64] = { 0 };
|
|
SDL_snprintf(indexed_filename, sizeof(indexed_filename),
|
|
"save%s_index.%s",
|
|
rw ? "Rwops" : "",
|
|
format->name);
|
|
SDL_RemovePath(indexed_filename);
|
|
SDLTest_AssertCheck(!SDL_GetPathInfo(indexed_filename, NULL), "\"%s\" should not exist BEFORE test", indexed_filename);
|
|
SDLTest_AssertPass("About to call IMG_Save(\"%s\")", indexed_filename);
|
|
result = IMG_Save(indexed_surface, indexed_filename);
|
|
SDLTest_Assert(!result, "Calling IMG_Save of an indexed surface without palette should return false (actual=%d)", result);
|
|
SDLTest_AssertCheck(!SDL_GetPathInfo(indexed_filename, NULL), "\"%s\" should not exist AFTER test", indexed_filename);
|
|
}
|
|
}
|
|
|
|
out:
|
|
if (surface != NULL) {
|
|
SDL_DestroySurface(surface);
|
|
}
|
|
if (indexed_surface != NULL) {
|
|
SDL_DestroySurface(indexed_surface);
|
|
}
|
|
if (reference != NULL) {
|
|
SDL_DestroySurface(reference);
|
|
}
|
|
if (refFilename != NULL) {
|
|
SDL_free(refFilename);
|
|
}
|
|
}
|
|
|
|
static void
|
|
FormatTest(const Format *format)
|
|
{
|
|
bool forced;
|
|
char envVar[64] = { 0 };
|
|
|
|
SDL_snprintf(envVar, sizeof(envVar), "SDL_IMAGE_TEST_REQUIRE_LOAD_%s",
|
|
format->name);
|
|
|
|
forced = GetStringBoolean(SDL_getenv(envVar), false);
|
|
if (forced) {
|
|
SDLTest_AssertCheck(format->canLoad,
|
|
"%s loading should be enabled", format->name);
|
|
}
|
|
|
|
if (format->canLoad || forced) {
|
|
SDLTest_Log("Testing ability to load format %s", format->name);
|
|
|
|
if (SDL_strcmp(format->name, "SVG-sized") == 0) {
|
|
FormatLoadTest(format, LOAD_SIZED);
|
|
} else {
|
|
FormatLoadTest(format, LOAD_CONVENIENCE);
|
|
|
|
if (SDL_strcmp(format->name, "TGA") == 0) {
|
|
SDLTest_Log("SKIP: Recognising %s by magic number is not supported", format->name);
|
|
} else {
|
|
FormatLoadTest(format, LOAD_IO);
|
|
}
|
|
|
|
FormatLoadTest(format, LOAD_TYPED_IO);
|
|
|
|
if (format->loadFunction != NULL) {
|
|
FormatLoadTest(format, LOAD_FORMAT_SPECIFIC);
|
|
}
|
|
}
|
|
} else {
|
|
SDLTest_Log("Format %s is not supported", format->name);
|
|
}
|
|
|
|
SDL_snprintf(envVar, sizeof(envVar), "SDL_IMAGE_TEST_REQUIRE_SAVE_%s",
|
|
format->name);
|
|
|
|
forced = GetStringBoolean(SDL_getenv(envVar), false);
|
|
if (forced) {
|
|
SDLTest_AssertCheck(format->canSave,
|
|
"%s saving should be enabled", format->name);
|
|
}
|
|
|
|
if (format->canSave || forced) {
|
|
SDLTest_Log("Testing ability to save format %s", format->name);
|
|
FormatSaveTest(format, false);
|
|
FormatSaveTest(format, true);
|
|
} else {
|
|
SDLTest_Log("Saving format %s is not supported", format->name);
|
|
}
|
|
}
|
|
|
|
static int SDLCALL
|
|
TestFormats(void *arg)
|
|
{
|
|
size_t i;
|
|
(void)arg;
|
|
|
|
for (i = 0; i < SDL_arraysize(formats); i++) {
|
|
FormatTest(&formats[i]);
|
|
}
|
|
|
|
return TEST_COMPLETED;
|
|
}
|
|
|
|
static const SDLTest_TestCaseReference formatsTestCase = {
|
|
TestFormats, "Images", "Load and save various image formats", TEST_ENABLED
|
|
};
|
|
|
|
static const SDLTest_TestCaseReference *testCases[] = {
|
|
&formatsTestCase,
|
|
NULL
|
|
};
|
|
static SDLTest_TestSuiteReference testSuite = {
|
|
"img",
|
|
NULL,
|
|
testCases,
|
|
NULL
|
|
};
|
|
static SDLTest_TestSuiteReference *testSuites[] = {
|
|
&testSuite,
|
|
NULL
|
|
};
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
int result;
|
|
int i, done;
|
|
SDL_Event event;
|
|
SDLTest_TestSuiteRunner *runner;
|
|
|
|
/* Initialize test framework */
|
|
state = SDLTest_CommonCreateState(argv, SDL_INIT_VIDEO);
|
|
if (!state) {
|
|
return 1;
|
|
}
|
|
|
|
runner = SDLTest_CreateTestSuiteRunner(state, testSuites);
|
|
|
|
if (!SDLTest_CommonDefaultArgs(state, argc, argv)) {
|
|
return 1;
|
|
}
|
|
|
|
/* Initialize common state */
|
|
if (!SDLTest_CommonInit(state)) {
|
|
goto failure;
|
|
}
|
|
|
|
/* Create the windows, initialize the renderers */
|
|
for (i = 0; i < state->num_windows; ++i) {
|
|
SDL_Renderer *renderer = state->renderers[i];
|
|
SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF);
|
|
SDL_RenderClear(renderer);
|
|
}
|
|
|
|
/* Call Harness */
|
|
result = SDLTest_ExecuteTestSuiteRunner(runner);
|
|
|
|
/* Empty event queue */
|
|
done = 0;
|
|
for (i=0; i<100; i++) {
|
|
while (SDL_PollEvent(&event)) {
|
|
SDLTest_CommonEvent(state, &event, &done);
|
|
}
|
|
SDL_Delay(10);
|
|
}
|
|
|
|
/* Shutdown everything */
|
|
SDLTest_CommonQuit(state);
|
|
return result;
|
|
failure:
|
|
SDLTest_CommonQuit(state);
|
|
return 1;
|
|
}
|