Fixed bug 2848 - IMG_SaveJPG
authorSam Lantinga <slouken@libsdl.org>
Mon, 11 Sep 2017 23:51:52 -0700
changeset 5027f374d902c16
parent 501 64a10bd1598c
child 503 1e32e1f475a9
Fixed bug 2848 - IMG_SaveJPG
CHANGES.txt
IMG_jpg.c
SDL_image.h
     1.1 --- a/CHANGES.txt	Mon Sep 11 23:42:09 2017 -0700
     1.2 +++ b/CHANGES.txt	Mon Sep 11 23:51:52 2017 -0700
     1.3 @@ -1,3 +1,8 @@
     1.4 +2.0.2:
     1.5 +Alexey - Mon Sep 11 23:50:31 PDT 2017
     1.6 + * Added JPG save support when built with jpeglib
     1.7 +	IMG_SaveJPG() and IMG_SaveJPG_RW()
     1.8 +
     1.9  2.0.1:
    1.10  Jeffrey Carpenter - Sat Nov 29 12:06:05 2014
    1.11   * Fixed image colorspace issue on iOS and Mac OS X
     2.1 --- a/IMG_jpg.c	Mon Sep 11 23:42:09 2017 -0700
     2.2 +++ b/IMG_jpg.c	Mon Sep 11 23:51:52 2017 -0700
     2.3 @@ -56,6 +56,13 @@
     2.4      JDIMENSION (*jpeg_read_scanlines) (j_decompress_ptr cinfo, JSAMPARRAY scanlines, JDIMENSION max_lines);
     2.5      boolean (*jpeg_resync_to_restart) (j_decompress_ptr cinfo, int desired);
     2.6      boolean (*jpeg_start_decompress) (j_decompress_ptr cinfo);
     2.7 +    void (*jpeg_CreateCompress) (j_compress_ptr cinfo, int version, size_t structsize);
     2.8 +    void (*jpeg_start_compress) (j_compress_ptr cinfo, boolean write_all_tables);
     2.9 +    void (*jpeg_set_quality) (j_compress_ptr cinfo, int quality, boolean force_baseline);
    2.10 +    void (*jpeg_set_defaults) (j_compress_ptr cinfo);
    2.11 +    JDIMENSION (*jpeg_write_scanlines) (j_compress_ptr cinfo, JSAMPARRAY scanlines, JDIMENSION num_lines);
    2.12 +    void (*jpeg_finish_compress) (j_compress_ptr cinfo);
    2.13 +    void (*jpeg_destroy_compress) (j_compress_ptr cinfo);
    2.14      struct jpeg_error_mgr * (*jpeg_std_error) (struct jpeg_error_mgr * err);
    2.15  } lib;
    2.16  
    2.17 @@ -123,6 +130,55 @@
    2.18              SDL_UnloadObject(lib.handle);
    2.19              return -1;
    2.20          }
    2.21 +        lib.jpeg_CreateCompress =
    2.22 +            (void (*) (j_compress_ptr, int, size_t))
    2.23 +            SDL_LoadFunction(lib.handle, "jpeg_CreateCompress");
    2.24 +        if ( lib.jpeg_CreateCompress == NULL ) {
    2.25 +            SDL_UnloadObject(lib.handle);
    2.26 +            return -1;
    2.27 +        }
    2.28 +        lib.jpeg_start_compress =
    2.29 +            (void (*) (j_compress_ptr cinfo, boolean write_all_tables))
    2.30 +            SDL_LoadFunction(lib.handle, "jpeg_start_compress");
    2.31 +        if ( lib.jpeg_start_compress == NULL ) {
    2.32 +            SDL_UnloadObject(lib.handle);
    2.33 +            return -1;
    2.34 +        }
    2.35 +        lib.jpeg_set_quality =
    2.36 +            (void(*) (j_compress_ptr cinfo, int quality, boolean force_baseline))
    2.37 +            SDL_LoadFunction(lib.handle, "jpeg_set_quality");
    2.38 +        if ( lib.jpeg_set_quality == NULL ) {
    2.39 +            SDL_UnloadObject(lib.handle);
    2.40 +            return -1;
    2.41 +        }
    2.42 +        lib.jpeg_set_defaults =
    2.43 +            (void(*) (j_compress_ptr cinfo))
    2.44 +            SDL_LoadFunction(lib.handle, "jpeg_set_defaults");
    2.45 +        if ( lib.jpeg_set_defaults == NULL ) {
    2.46 +            SDL_UnloadObject(lib.handle);
    2.47 +            return -1;
    2.48 +        }
    2.49 +        lib.jpeg_write_scanlines = 
    2.50 +            (JDIMENSION (*) (j_compress_ptr cinfo, JSAMPARRAY scanlines, JDIMENSION num_lines))
    2.51 +            SDL_LoadFunction(lib.handle, "jpeg_write_scanlines");
    2.52 +        if ( lib.jpeg_write_scanlines == NULL ) {
    2.53 +            SDL_UnloadObject(lib.handle);
    2.54 +            return -1;
    2.55 +        }
    2.56 +        lib.jpeg_finish_compress =
    2.57 +            (void (*) (j_compress_ptr cinfo))
    2.58 +            SDL_LoadFunction(lib.handle, "jpeg_finish_compress");
    2.59 +        if ( lib.jpeg_finish_compress == NULL ) {
    2.60 +            SDL_UnloadObject(lib.handle);
    2.61 +            return -1;
    2.62 +        }
    2.63 +        lib.jpeg_destroy_compress = 
    2.64 +            (void (*) (j_compress_ptr cinfo))
    2.65 +            SDL_LoadFunction(lib.handle, "jpeg_destroy_compress");
    2.66 +        if ( lib.jpeg_destroy_compress == NULL ) {
    2.67 +            SDL_UnloadObject(lib.handle);
    2.68 +            return -1;
    2.69 +        }
    2.70          lib.jpeg_std_error =
    2.71              (struct jpeg_error_mgr * (*) (struct jpeg_error_mgr *))
    2.72              SDL_LoadFunction(lib.handle, "jpeg_std_error");
    2.73 @@ -157,6 +213,13 @@
    2.74          lib.jpeg_read_scanlines = jpeg_read_scanlines;
    2.75          lib.jpeg_resync_to_restart = jpeg_resync_to_restart;
    2.76          lib.jpeg_start_decompress = jpeg_start_decompress;
    2.77 +        lib.jpeg_CreateCompress = jpeg_CreateCompress;
    2.78 +        lib.jpeg_start_compress = jpeg_start_compress;
    2.79 +        lib.jpeg_set_quality = jpeg_set_quality;
    2.80 +        lib.jpeg_set_defaults = jpeg_set_defaults;
    2.81 +        lib.jpeg_write_scanlines = jpeg_write_scanlines;
    2.82 +        lib.jpeg_finish_compress = jpeg_finish_compress;
    2.83 +        lib.jpeg_destroy_compress = jpeg_destroy_compress;
    2.84          lib.jpeg_std_error = jpeg_std_error;
    2.85      }
    2.86      ++lib.loaded;
    2.87 @@ -466,6 +529,140 @@
    2.88      return(surface);
    2.89  }
    2.90  
    2.91 +int IMG_SaveJPG(SDL_Surface *surface, const char *file, int quality)
    2.92 +{
    2.93 +    SDL_RWops *dst = SDL_RWFromFile(file, "wb");
    2.94 +    if (dst) {
    2.95 +        return IMG_SaveJPG_RW(surface, dst, 1, quality);
    2.96 +    } else {
    2.97 +        return -1;
    2.98 +    }
    2.99 +}
   2.100 +
   2.101 +#define OUTPUT_BUFFER_SIZE   4096
   2.102 +typedef struct {
   2.103 +    struct jpeg_destination_mgr pub;
   2.104 +
   2.105 +    SDL_RWops *ctx;
   2.106 +    Uint8 buffer[OUTPUT_BUFFER_SIZE];
   2.107 +} my_destination_mgr;
   2.108 +
   2.109 +static void init_destination(j_compress_ptr cinfo)
   2.110 +{
   2.111 +    /* We don't actually need to do anything */
   2.112 +    return;
   2.113 +}
   2.114 +
   2.115 +static boolean empty_output_buffer(j_compress_ptr cinfo)
   2.116 +{
   2.117 +    my_destination_mgr * dest = (my_destination_mgr *)cinfo->dest;
   2.118 +
   2.119 +    /* In typical applications, it should write out the *entire* buffer */
   2.120 +    SDL_RWwrite(dest->ctx, dest->buffer, 1, OUTPUT_BUFFER_SIZE);
   2.121 +    dest->pub.next_output_byte = dest->buffer;
   2.122 +    dest->pub.free_in_buffer = OUTPUT_BUFFER_SIZE;
   2.123 +
   2.124 +    return SDL_TRUE;
   2.125 +}
   2.126 +
   2.127 +static void term_destination(j_compress_ptr cinfo)
   2.128 +{
   2.129 +    my_destination_mgr * dest = (my_destination_mgr *)cinfo->dest;
   2.130 +
   2.131 +    /*  In most applications, this must flush any data remaining in the buffer */
   2.132 +    SDL_RWwrite(dest->ctx, dest->buffer, 1, OUTPUT_BUFFER_SIZE - dest->pub.free_in_buffer);
   2.133 +}
   2.134 +
   2.135 +static void jpeg_SDL_RW_dest(j_compress_ptr cinfo, SDL_RWops *ctx)
   2.136 +{
   2.137 +    my_destination_mgr *dest;
   2.138 +
   2.139 +    if (cinfo->dest == NULL) {
   2.140 +        cinfo->dest = (struct jpeg_destination_mgr *)
   2.141 +            (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_PERMANENT,
   2.142 +            sizeof(my_destination_mgr));
   2.143 +        dest = (my_destination_mgr *)cinfo->dest;
   2.144 +    }
   2.145 +
   2.146 +    dest = (my_destination_mgr *)cinfo->dest;
   2.147 +    dest->pub.init_destination = init_destination;
   2.148 +    dest->pub.empty_output_buffer = empty_output_buffer;
   2.149 +    dest->pub.term_destination = term_destination;
   2.150 +    dest->ctx = ctx;
   2.151 +    dest->pub.next_output_byte = dest->buffer;
   2.152 +    dest->pub.free_in_buffer = OUTPUT_BUFFER_SIZE;
   2.153 +}
   2.154 +
   2.155 +int IMG_SaveJPG_RW(SDL_Surface *surface, SDL_RWops *dst, int freedst, int quality)
   2.156 +{
   2.157 +    struct jpeg_compress_struct cinfo;
   2.158 +    struct my_error_mgr jerr;
   2.159 +    JSAMPROW row_pointer[1];
   2.160 +    SDL_Surface* jpeg_surface = surface;
   2.161 +	int result = -1;
   2.162 +
   2.163 +    if (!dst) {
   2.164 +        SDL_SetError("Passed NULL dst");
   2.165 +		goto done;
   2.166 +    }
   2.167 +
   2.168 +    if (!IMG_Init(IMG_INIT_JPG)) {
   2.169 +		goto done;
   2.170 +    }
   2.171 +
   2.172 +    /* Convert surface to format we can save */
   2.173 +#if SDL_BYTEORDER == SDL_LIL_ENDIAN
   2.174 +    static const Uint32 jpg_format = SDL_PIXELFORMAT_RGB24;
   2.175 +#else
   2.176 +    static const Uint32 jpg_format = SDL_PIXELFORMAT_BGR24;
   2.177 +#endif
   2.178 +
   2.179 +    if (surface->format->format != jpg_format) {
   2.180 +        jpeg_surface = SDL_ConvertSurfaceFormat(surface, jpg_format, 0);
   2.181 +        if (!jpeg_surface) {
   2.182 +			goto done;
   2.183 +        }
   2.184 +    }
   2.185 +
   2.186 +    /* Create a decompression structure and load the JPEG header */
   2.187 +    cinfo.err = lib.jpeg_std_error(&jerr.errmgr);
   2.188 +    jerr.errmgr.error_exit = my_error_exit;
   2.189 +    jerr.errmgr.output_message = output_no_message;
   2.190 +
   2.191 +    lib.jpeg_create_compress(&cinfo);
   2.192 +    jpeg_SDL_RW_dest(&cinfo, dst);
   2.193 +
   2.194 +    cinfo.image_width = surface->w;
   2.195 +    cinfo.image_height = surface->h;
   2.196 +    cinfo.in_color_space = JCS_RGB;
   2.197 +    cinfo.input_components = 3;
   2.198 +
   2.199 +    lib.jpeg_set_defaults(&cinfo);
   2.200 +    lib.jpeg_set_quality(&cinfo, quality, SDL_TRUE);
   2.201 +    lib.jpeg_start_compress(&cinfo, SDL_TRUE);
   2.202 +
   2.203 +    while (cinfo.next_scanline < cinfo.image_height) {
   2.204 +        int offset = cinfo.next_scanline * surface->pitch;
   2.205 +        row_pointer[0] = ((Uint8*)jpeg_surface->pixels) + offset;
   2.206 +        lib.jpeg_write_scanlines(&cinfo, row_pointer, 1);
   2.207 +    }
   2.208 +
   2.209 +    lib.jpeg_finish_compress(&cinfo);
   2.210 +    lib.jpeg_destroy_compress(&cinfo);
   2.211 +
   2.212 +    if (jpeg_surface != surface) {
   2.213 +        SDL_FreeSurface(jpeg_surface);
   2.214 +    }
   2.215 +
   2.216 +	result = 0;
   2.217 +
   2.218 +done:
   2.219 +    if (freedst) {
   2.220 +        SDL_RWclose(dst);
   2.221 +    }
   2.222 +    return result;
   2.223 +}
   2.224 +
   2.225  #else
   2.226  
   2.227  int IMG_InitJPG()
   2.228 @@ -490,6 +687,16 @@
   2.229      return(NULL);
   2.230  }
   2.231  
   2.232 +int IMG_SaveJPG(SDL_Surface *surface, const char *file, int quality)
   2.233 +{
   2.234 +    return IMG_SetError("JPEG images are not supported");
   2.235 +}
   2.236 +
   2.237 +int IMG_SaveJPG_RW(SDL_Surface *surface, SDL_RWops *dst, int freedst, int quality)
   2.238 +{
   2.239 +    return IMG_SetError("JPEG images are not supported");
   2.240 +}
   2.241 +
   2.242  #endif /* LOAD_JPG */
   2.243  
   2.244  #endif /* !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND) */
     3.1 --- a/SDL_image.h	Mon Sep 11 23:42:09 2017 -0700
     3.2 +++ b/SDL_image.h	Mon Sep 11 23:51:52 2017 -0700
     3.3 @@ -131,6 +131,8 @@
     3.4  /* Individual saving functions */
     3.5  extern DECLSPEC int SDLCALL IMG_SavePNG(SDL_Surface *surface, const char *file);
     3.6  extern DECLSPEC int SDLCALL IMG_SavePNG_RW(SDL_Surface *surface, SDL_RWops *dst, int freedst);
     3.7 +extern DECLSPEC int SDLCALL IMG_SaveJPG(SDL_Surface *surface, const char *file, int quality);
     3.8 +extern DECLSPEC int SDLCALL IMG_SaveJPG_RW(SDL_Surface *surface, SDL_RWops *dst, int freedst, int quality);
     3.9  
    3.10  /* We'll use SDL for reporting errors */
    3.11  #define IMG_SetError    SDL_SetError