Просмотр исходного кода

Support loading JPEG images through SDL_LoadSurface()

Cameron Cawley 4 дней назад
Родитель
Сommit
c6b232f5d4

+ 54 - 6
include/SDL3/SDL_surface.h

@@ -29,10 +29,10 @@
  * provides a reasonable toolbox for transforming the data, including copying
  * provides a reasonable toolbox for transforming the data, including copying
  * between surfaces, filling rectangles in the image data, etc.
  * between surfaces, filling rectangles in the image data, etc.
  *
  *
- * There is also a simple .bmp loader, SDL_LoadBMP(), and a simple .png
- * loader, SDL_LoadPNG(). SDL itself does not provide loaders for other file
- * formats, but there are several excellent external libraries that do,
- * including its own satellite library,
+ * There is also a simple .bmp loader, SDL_LoadBMP(), a simple .png loader,
+ * SDL_LoadPNG(), and a simple .jpg loader, SDL_LoadJPG(). SDL itself does not
+ * provide loaders for other file formats, but there are several excellent
+ * external libraries that do, including its own satellite library,
  * [SDL_image](https://wiki.libsdl.org/SDL3_image)
  * [SDL_image](https://wiki.libsdl.org/SDL3_image)
  * .
  * .
  *
  *
@@ -510,7 +510,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_LockSurface(SDL_Surface *surface);
 extern SDL_DECLSPEC void SDLCALL SDL_UnlockSurface(SDL_Surface *surface);
 extern SDL_DECLSPEC void SDLCALL SDL_UnlockSurface(SDL_Surface *surface);
 
 
 /**
 /**
- * Load a BMP or PNG image from a seekable SDL data stream.
+ * Load a BMP, PNG or JPEG image from a seekable SDL data stream.
  *
  *
  * The new surface should be freed with SDL_DestroySurface(). Not doing so
  * The new surface should be freed with SDL_DestroySurface(). Not doing so
  * will result in a memory leak.
  * will result in a memory leak.
@@ -531,7 +531,7 @@ extern SDL_DECLSPEC void SDLCALL SDL_UnlockSurface(SDL_Surface *surface);
 extern SDL_DECLSPEC SDL_Surface * SDLCALL SDL_LoadSurface_IO(SDL_IOStream *src, bool closeio);
 extern SDL_DECLSPEC SDL_Surface * SDLCALL SDL_LoadSurface_IO(SDL_IOStream *src, bool closeio);
 
 
 /**
 /**
- * Load a BMP or PNG image from a file.
+ * Load a BMP, PNG or JPEG image from a file.
  *
  *
  * The new surface should be freed with SDL_DestroySurface(). Not doing so
  * The new surface should be freed with SDL_DestroySurface(). Not doing so
  * will result in a memory leak.
  * will result in a memory leak.
@@ -729,6 +729,54 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SavePNG_IO(SDL_Surface *surface, SDL_IOStre
  */
  */
 extern SDL_DECLSPEC bool SDLCALL SDL_SavePNG(SDL_Surface *surface, const char *file);
 extern SDL_DECLSPEC bool SDLCALL SDL_SavePNG(SDL_Surface *surface, const char *file);
 
 
+/**
+ * Load a JPEG image from a seekable SDL data stream.
+ *
+ * This is intended as a convenience function for loading images from trusted
+ * sources. If you want to load arbitrary images you should use libjpeg or
+ * another image loading library designed with security in mind.
+ *
+ * The new surface should be freed with SDL_DestroySurface(). Not doing so
+ * will result in a memory leak.
+ *
+ * \param src the data stream for the surface.
+ * \param closeio if true, calls SDL_CloseIO() on `src` before returning, even
+ *                in the case of an error.
+ * \returns a pointer to a new SDL_Surface structure or NULL on failure; call
+ *          SDL_GetError() for more information.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.6.0.
+ *
+ * \sa SDL_DestroySurface
+ * \sa SDL_LoadJPG
+ */
+extern SDL_DECLSPEC SDL_Surface * SDLCALL SDL_LoadJPG_IO(SDL_IOStream *src, bool closeio);
+
+/**
+ * Load a JPEG image from a file.
+ *
+ * This is intended as a convenience function for loading images from trusted
+ * sources. If you want to load arbitrary images you should use libjpeg or
+ * another image loading library designed with security in mind.
+ *
+ * The new surface should be freed with SDL_DestroySurface(). Not doing so
+ * will result in a memory leak.
+ *
+ * \param file the JPG file to load.
+ * \returns a pointer to a new SDL_Surface structure or NULL on failure; call
+ *          SDL_GetError() for more information.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.6.0.
+ *
+ * \sa SDL_DestroySurface
+ * \sa SDL_LoadJPG_IO
+ */
+extern SDL_DECLSPEC SDL_Surface * SDLCALL SDL_LoadJPG(const char *file);
+
 /**
 /**
  * Set the RLE acceleration hint for a surface.
  * Set the RLE acceleration hint for a surface.
  *
  *

+ 2 - 0
src/dynapi/SDL_dynapi.exports

@@ -1285,3 +1285,5 @@ _SDL_SetGPURenderStateStorageBuffers
 _SDL_GDKSuspendRenderer
 _SDL_GDKSuspendRenderer
 _SDL_GDKResumeRenderer
 _SDL_GDKResumeRenderer
 _SDL_IsPhone
 _SDL_IsPhone
+_SDL_LoadJPG_IO
+_SDL_LoadJPG

+ 2 - 0
src/dynapi/SDL_dynapi.sym

@@ -1286,6 +1286,8 @@ SDL3_0.0.0 {
     SDL_GDKSuspendRenderer;
     SDL_GDKSuspendRenderer;
     SDL_GDKResumeRenderer;
     SDL_GDKResumeRenderer;
     SDL_IsPhone;
     SDL_IsPhone;
+    SDL_LoadJPG_IO;
+    SDL_LoadJPG;
     # extra symbols go here (don't modify this line)
     # extra symbols go here (don't modify this line)
   local: *;
   local: *;
 };
 };

+ 2 - 0
src/dynapi/SDL_dynapi_overrides.h

@@ -1312,3 +1312,5 @@
 #define SDL_GDKSuspendRenderer SDL_GDKSuspendRenderer_REAL
 #define SDL_GDKSuspendRenderer SDL_GDKSuspendRenderer_REAL
 #define SDL_GDKResumeRenderer SDL_GDKResumeRenderer_REAL
 #define SDL_GDKResumeRenderer SDL_GDKResumeRenderer_REAL
 #define SDL_IsPhone SDL_IsPhone_REAL
 #define SDL_IsPhone SDL_IsPhone_REAL
+#define SDL_LoadJPG_IO SDL_LoadJPG_IO_REAL
+#define SDL_LoadJPG SDL_LoadJPG_REAL

+ 2 - 0
src/dynapi/SDL_dynapi_procs.h

@@ -1320,3 +1320,5 @@ SDL_DYNAPI_PROC(bool,SDL_SetGPURenderStateStorageBuffers,(SDL_GPURenderState *a,
 SDL_DYNAPI_PROC(void,SDL_GDKSuspendRenderer,(SDL_Renderer *a),(a),)
 SDL_DYNAPI_PROC(void,SDL_GDKSuspendRenderer,(SDL_Renderer *a),(a),)
 SDL_DYNAPI_PROC(void,SDL_GDKResumeRenderer,(SDL_Renderer *a),(a),)
 SDL_DYNAPI_PROC(void,SDL_GDKResumeRenderer,(SDL_Renderer *a),(a),)
 SDL_DYNAPI_PROC(bool,SDL_IsPhone,(void),(),return)
 SDL_DYNAPI_PROC(bool,SDL_IsPhone,(void),(),return)
+SDL_DYNAPI_PROC(SDL_Surface*,SDL_LoadJPG_IO,(SDL_IOStream *a,bool b),(a,b),return)
+SDL_DYNAPI_PROC(SDL_Surface*,SDL_LoadJPG,(const char *a),(a),return)

+ 110 - 0
src/video/SDL_stb.c

@@ -363,6 +363,116 @@ static SDL_Surface *SDL_LoadSTB_IO(SDL_IOStream *src)
 }
 }
 #endif // SDL_HAVE_STB
 #endif // SDL_HAVE_STB
 
 
+/* FIXME: This is a copypaste from JPEGLIB! Pull that out of the ifdefs */
+/* Define this for quicker (but less perfect) JPEG identification */
+#define FAST_IS_JPEG
+
+/* See if an image is contained in a data source */
+bool SDL_IsJPG(SDL_IOStream *src)
+{
+    Sint64 start;
+    bool is_JPG;
+    bool in_scan;
+    Uint8 magic[4];
+
+    /* This detection code is by Steaphan Greene <stea@cs.binghamton.edu> */
+    /* Blame me, not Sam, if this doesn't work right. */
+    /* And don't forget to report the problem to the the sdl list too! */
+
+    if (!src) {
+        return false;
+    }
+
+    start = SDL_TellIO(src);
+    is_JPG = false;
+    in_scan = false;
+    if (SDL_ReadIO(src, magic, 2) == 2) {
+        if ( (magic[0] == 0xFF) && (magic[1] == 0xD8) ) {
+            is_JPG = true;
+            while (is_JPG) {
+                if (SDL_ReadIO(src, magic, 2) != 2) {
+                    is_JPG = false;
+                } else if ( (magic[0] != 0xFF) && !in_scan ) {
+                    is_JPG = false;
+                } else if ( (magic[0] != 0xFF) || (magic[1] == 0xFF) ) {
+                    /* Extra padding in JPEG (legal) */
+                    /* or this is data and we are scanning */
+                    SDL_SeekIO(src, -1, SDL_IO_SEEK_CUR);
+                } else if (magic[1] == 0xD9) {
+                    /* Got to end of good JPEG */
+                    break;
+                } else if ( in_scan && (magic[1] == 0x00) ) {
+                    /* This is an encoded 0xFF within the data */
+                } else if ( (magic[1] >= 0xD0) && (magic[1] < 0xD9) ) {
+                    /* These have nothing else */
+                } else if (SDL_ReadIO(src, magic+2, 2) != 2) {
+                    is_JPG = false;
+                } else {
+                    /* Yes, it's big-endian */
+                    Sint64 innerStart;
+                    Uint32 size;
+                    Sint64 end;
+                    innerStart = SDL_TellIO(src);
+                    size = (magic[2] << 8) + magic[3];
+                    end = SDL_SeekIO(src, size-2, SDL_IO_SEEK_CUR);
+                    if ( end != innerStart + size - 2 ) {
+                        is_JPG = false;
+                    }
+                    if ( magic[1] == 0xDA ) {
+                        /* Now comes the actual JPEG meat */
+#ifdef  FAST_IS_JPEG
+                        /* Ok, I'm convinced.  It is a JPEG. */
+                        break;
+#else
+                        /* I'm not convinced.  Prove it! */
+                        in_scan = true;
+#endif
+                    }
+                }
+            }
+        }
+    }
+    SDL_SeekIO(src, start, SDL_IO_SEEK_SET);
+    return is_JPG;
+}
+
+SDL_Surface *SDL_LoadJPG_IO(SDL_IOStream *src, bool closeio)
+{
+    SDL_Surface *surface = NULL;
+
+    CHECK_PARAM(!src) {
+        SDL_InvalidParamError("src");
+        goto done;
+    }
+
+    if (!SDL_IsJPG(src)) {
+        SDL_SetError("File is not a JPEG file");
+        goto done;
+    }
+
+#ifdef SDL_HAVE_STB
+    surface = SDL_LoadSTB_IO(src);
+#else
+    SDL_SetError("SDL not built with STB image support");
+#endif // SDL_HAVE_STB
+
+done:
+    if (src && closeio) {
+        SDL_CloseIO(src);
+    }
+    return surface;
+}
+
+SDL_Surface *SDL_LoadJPG(const char *file)
+{
+    SDL_IOStream *stream = SDL_IOFromFile(file, "rb");
+    if (!stream) {
+        return NULL;
+    }
+
+    return SDL_LoadJPG_IO(stream, true);
+}
+
 bool SDL_IsPNG(SDL_IOStream *src)
 bool SDL_IsPNG(SDL_IOStream *src)
 {
 {
     Sint64 start;
     Sint64 start;

+ 2 - 0
src/video/SDL_surface.c

@@ -3118,6 +3118,8 @@ SDL_Surface *SDL_LoadSurface_IO(SDL_IOStream *src, bool closeio)
         return SDL_LoadBMP_IO(src, closeio);
         return SDL_LoadBMP_IO(src, closeio);
     } else if (SDL_IsPNG(src)) {
     } else if (SDL_IsPNG(src)) {
         return SDL_LoadPNG_IO(src, closeio);
         return SDL_LoadPNG_IO(src, closeio);
+    } else if (SDL_IsJPG(src)) {
+        return SDL_LoadJPG_IO(src, closeio);
     } else {
     } else {
         if (closeio) {
         if (closeio) {
             SDL_CloseIO(src);
             SDL_CloseIO(src);

+ 1 - 0
src/video/SDL_surface_c.h

@@ -94,6 +94,7 @@ extern float SDL_GetSurfaceHDRHeadroom(SDL_Surface *surface, SDL_Colorspace colo
 extern SDL_Surface *SDL_GetSurfaceImage(SDL_Surface *surface, float display_scale);
 extern SDL_Surface *SDL_GetSurfaceImage(SDL_Surface *surface, float display_scale);
 extern SDL_Surface *SDL_ConvertSurfaceRect(SDL_Surface *surface, const SDL_Rect *rect, SDL_PixelFormat format);
 extern SDL_Surface *SDL_ConvertSurfaceRect(SDL_Surface *surface, const SDL_Rect *rect, SDL_PixelFormat format);
 extern bool SDL_IsBMP(SDL_IOStream *src);
 extern bool SDL_IsBMP(SDL_IOStream *src);
+extern bool SDL_IsJPG(SDL_IOStream *src);
 extern bool SDL_IsPNG(SDL_IOStream *src);
 extern bool SDL_IsPNG(SDL_IOStream *src);
 
 
 #endif // SDL_surface_c_h_
 #endif // SDL_surface_c_h_