IMG_webp.c
author Ryan C. Gordon <icculus@icculus.org>
Wed, 26 Sep 2018 15:19:34 -0400
changeset 587 32a18ca05935
parent 575 36e9e2255178
child 610 e21b8012c9b4
permissions -rw-r--r--
pnm: Don't get into infinite loops on truncated files.
     1 /*
     2   SDL_image:  An example image loading library for use with SDL
     3   Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 
    22 /* This is a WEBP image file loading framework */
    23 
    24 #include "SDL_image.h"
    25 
    26 #ifdef LOAD_WEBP
    27 
    28 /*=============================================================================
    29         File: SDL_webp.c
    30      Purpose: A WEBP loader for the SDL library
    31     Revision:
    32   Created by: Michael Bonfils (Murlock) (26 November 2011)
    33               murlock42@gmail.com
    34 
    35 =============================================================================*/
    36 
    37 #include "SDL_endian.h"
    38 
    39 #ifdef macintosh
    40 #define MACOS
    41 #endif
    42 #include <webp/decode.h>
    43 
    44 static struct {
    45     int loaded;
    46     void *handle;
    47     VP8StatusCode (*WebPGetFeaturesInternal) (const uint8_t *data, size_t data_size, WebPBitstreamFeatures* features, int decoder_abi_version);
    48     uint8_t*    (*WebPDecodeRGBInto) (const uint8_t* data, size_t data_size, uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
    49     uint8_t*    (*WebPDecodeRGBAInto) (const uint8_t* data, size_t data_size, uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
    50 } lib;
    51 
    52 #ifdef LOAD_WEBP_DYNAMIC
    53 #define FUNCTION_LOADER(FUNC, SIG) \
    54     lib.FUNC = (SIG) SDL_LoadFunction(lib.handle, #FUNC); \
    55     if (lib.FUNC == NULL) { SDL_UnloadObject(lib.handle); return -1; }
    56 #else
    57 #define FUNCTION_LOADER(FUNC, SIG) \
    58     lib.FUNC = FUNC;
    59 #endif
    60 
    61 int IMG_InitWEBP()
    62 {
    63     if ( lib.loaded == 0 ) {
    64 #ifdef LOAD_WEBP_DYNAMIC
    65         lib.handle = SDL_LoadObject(LOAD_WEBP_DYNAMIC);
    66         if ( lib.handle == NULL ) {
    67             return -1;
    68         }
    69 #endif
    70         FUNCTION_LOADER(WebPGetFeaturesInternal, VP8StatusCode (*) (const uint8_t *data, size_t data_size, WebPBitstreamFeatures* features, int decoder_abi_version))
    71         FUNCTION_LOADER(WebPDecodeRGBInto, uint8_t * (*) (const uint8_t* data, size_t data_size, uint8_t* output_buffer, size_t output_buffer_size, int output_stride))
    72         FUNCTION_LOADER(WebPDecodeRGBAInto, uint8_t * (*) (const uint8_t* data, size_t data_size, uint8_t* output_buffer, size_t output_buffer_size, int output_stride))
    73     }
    74     ++lib.loaded;
    75 
    76     return 0;
    77 }
    78 void IMG_QuitWEBP()
    79 {
    80     if ( lib.loaded == 0 ) {
    81         return;
    82     }
    83     if ( lib.loaded == 1 ) {
    84 #ifdef LOAD_WEBP_DYNAMIC
    85         SDL_UnloadObject(lib.handle);
    86 #endif
    87     }
    88     --lib.loaded;
    89 }
    90 
    91 static int webp_getinfo( SDL_RWops *src, int *datasize ) {
    92     Sint64 start;
    93     int is_WEBP;
    94     Uint8 magic[20];
    95 
    96     if ( !src )
    97         return 0;
    98     start = SDL_RWtell(src);
    99     is_WEBP = 0;
   100     if ( SDL_RWread(src, magic, 1, sizeof(magic)) == sizeof(magic) ) {
   101         if ( magic[ 0] == 'R' &&
   102              magic[ 1] == 'I' &&
   103              magic[ 2] == 'F' &&
   104              magic[ 3] == 'F' &&
   105              magic[ 8] == 'W' &&
   106              magic[ 9] == 'E' &&
   107              magic[10] == 'B' &&
   108              magic[11] == 'P' &&
   109              magic[12] == 'V' &&
   110              magic[13] == 'P' &&
   111              magic[14] == '8' &&
   112 #if WEBP_DECODER_ABI_VERSION < 0x0003 /* old versions don't support WEBPVP8X and WEBPVP8L */
   113              magic[15] == ' ') {
   114 #else
   115              (magic[15] == ' ' || magic[15] == 'X' || magic[15] == 'L')) {
   116 #endif
   117             is_WEBP = 1;
   118             if ( datasize ) {
   119                 *datasize = (int)(SDL_RWseek(src, 0, RW_SEEK_END) - start);
   120             }
   121         }
   122     }
   123     SDL_RWseek(src, start, RW_SEEK_SET);
   124     return(is_WEBP);
   125 }
   126 
   127 /* See if an image is contained in a data source */
   128 int IMG_isWEBP(SDL_RWops *src)
   129 {
   130     return webp_getinfo( src, NULL );
   131 }
   132 
   133 SDL_Surface *IMG_LoadWEBP_RW(SDL_RWops *src)
   134 {
   135     Sint64 start;
   136     const char *error = NULL;
   137     SDL_Surface *volatile surface = NULL;
   138     Uint32 Rmask;
   139     Uint32 Gmask;
   140     Uint32 Bmask;
   141     Uint32 Amask;
   142     WebPBitstreamFeatures features;
   143     int raw_data_size;
   144     uint8_t *raw_data = NULL;
   145     int r;
   146     uint8_t *ret;
   147 
   148     if ( !src ) {
   149         /* The error message has been set in SDL_RWFromFile */
   150         return NULL;
   151     }
   152 
   153     start = SDL_RWtell(src);
   154 
   155     if ( (IMG_Init(IMG_INIT_WEBP) & IMG_INIT_WEBP) == 0 ) {
   156         goto error;
   157     }
   158 
   159     raw_data_size = -1;
   160     if ( !webp_getinfo( src, &raw_data_size ) ) {
   161         error = "Invalid WEBP";
   162         goto error;
   163     }
   164 
   165     raw_data = (uint8_t*) SDL_malloc( raw_data_size );
   166     if ( raw_data == NULL ) {
   167         error = "Failed to allocate enough buffer for WEBP";
   168         goto error;
   169     }
   170 
   171     r = (int)SDL_RWread(src, raw_data, 1, raw_data_size );
   172     if ( r != raw_data_size ) {
   173         error = "Failed to read WEBP";
   174         goto error;
   175     }
   176 
   177 #if 0
   178     // extract size of picture, not interesting since we don't know about alpha channel
   179     int width = -1, height = -1;
   180     if ( !WebPGetInfo( raw_data, raw_data_size, &width, &height ) ) {
   181         printf("WebPGetInfo has failed\n" );
   182         return NULL;
   183     }
   184 #endif
   185 
   186     if ( lib.WebPGetFeaturesInternal( raw_data, raw_data_size, &features, WEBP_DECODER_ABI_VERSION ) != VP8_STATUS_OK ) {
   187         error = "WebPGetFeatures has failed";
   188         goto error;
   189     }
   190 
   191     /* Check if it's ok !*/
   192 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
   193     Rmask = 0x000000FF;
   194     Gmask = 0x0000FF00;
   195     Bmask = 0x00FF0000;
   196     Amask = (features.has_alpha) ? 0xFF000000 : 0;
   197 #else
   198     {
   199         int s = (features.has_alpha) ? 0 : 8;
   200         Rmask = 0xFF000000 >> s;
   201         Gmask = 0x00FF0000 >> s;
   202         Bmask = 0x0000FF00 >> s;
   203         Amask = 0x000000FF >> s;
   204     }
   205 #endif
   206 
   207     surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
   208             features.width, features.height,
   209             features.has_alpha?32:24, Rmask,Gmask,Bmask,Amask);
   210 
   211     if ( surface == NULL ) {
   212         error = "Failed to allocate SDL_Surface";
   213         goto error;
   214     }
   215 
   216     if ( features.has_alpha ) {
   217         ret = lib.WebPDecodeRGBAInto( raw_data, raw_data_size, (uint8_t *)surface->pixels, surface->pitch * surface->h,  surface->pitch );
   218     } else {
   219         ret = lib.WebPDecodeRGBInto( raw_data, raw_data_size, (uint8_t *)surface->pixels, surface->pitch * surface->h,  surface->pitch );
   220     }
   221 
   222     if ( !ret ) {
   223         error = "Failed to decode WEBP";
   224         goto error;
   225     }
   226 
   227     if ( raw_data ) {
   228         SDL_free( raw_data );
   229     }
   230 
   231     return surface;
   232 
   233 
   234 error:
   235 
   236     if ( raw_data ) {
   237         SDL_free( raw_data );
   238     }
   239 
   240     if ( surface ) {
   241         SDL_FreeSurface( surface );
   242     }
   243 
   244     if ( error ) {
   245         IMG_SetError( "%s", error );
   246     }
   247 
   248     SDL_RWseek(src, start, RW_SEEK_SET);
   249     return(NULL);
   250 }
   251 
   252 #else
   253 
   254 int IMG_InitWEBP()
   255 {
   256     IMG_SetError("WEBP images are not supported");
   257     return(-1);
   258 }
   259 
   260 void IMG_QuitWEBP()
   261 {
   262 }
   263 
   264 /* See if an image is contained in a data source */
   265 int IMG_isWEBP(SDL_RWops *src)
   266 {
   267     return(0);
   268 }
   269 
   270 /* Load a WEBP type image from an SDL datasource */
   271 SDL_Surface *IMG_LoadWEBP_RW(SDL_RWops *src)
   272 {
   273     return(NULL);
   274 }
   275 
   276 #endif /* LOAD_WEBP */