IMG_webp.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 01 Jan 2017 18:50:51 -0800
changeset 496 6332f9425dcc
parent 490 fd7214657669
child 528 7f9e88f4b45e
permissions -rw-r--r--
Updated copyright for 2017
     1 /*
     2   SDL_image:  An example image loading library for use with SDL
     3   Copyright (C) 1997-2017 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 <stdlib.h>
    25 #include <stdio.h>
    26 
    27 #include "SDL_image.h"
    28 
    29 #ifdef LOAD_WEBP
    30 
    31 /*=============================================================================
    32         File: SDL_webp.c
    33      Purpose: A WEBP loader for the SDL library
    34     Revision:
    35   Created by: Michael Bonfils (Murlock) (26 November 2011)
    36               murlock42@gmail.com
    37 
    38 =============================================================================*/
    39 
    40 #include "SDL_endian.h"
    41 
    42 #ifdef macintosh
    43 #define MACOS
    44 #endif
    45 #include <webp/decode.h>
    46 
    47 static struct {
    48     int loaded;
    49     void *handle;
    50     VP8StatusCode (*webp_get_features_internal) (const uint8_t *data, size_t data_size, WebPBitstreamFeatures* features, int decoder_abi_version);
    51     uint8_t*    (*webp_decode_rgb_into) (const uint8_t* data, size_t data_size, uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
    52     uint8_t*    (*webp_decode_rgba_into) (const uint8_t* data, size_t data_size, uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
    53 } lib;
    54 
    55 #ifdef LOAD_WEBP_DYNAMIC
    56 int IMG_InitWEBP()
    57 {
    58     if ( lib.loaded == 0 ) {
    59         lib.handle = SDL_LoadObject(LOAD_WEBP_DYNAMIC);
    60         if ( lib.handle == NULL ) {
    61             return -1;
    62         }
    63 
    64         lib.webp_get_features_internal =
    65             ( VP8StatusCode (*) (const uint8_t *, size_t, WebPBitstreamFeatures*, int) )
    66             SDL_LoadFunction(lib.handle, "WebPGetFeaturesInternal" );
    67         if ( lib.webp_get_features_internal == NULL ) {
    68             SDL_UnloadObject(lib.handle);
    69             return -1;
    70         }
    71 
    72         lib.webp_decode_rgb_into =
    73             ( uint8_t* (*) (const uint8_t*, size_t, uint8_t*, size_t, int ) )
    74             SDL_LoadFunction(lib.handle, "WebPDecodeRGBInto" );
    75         if ( lib.webp_decode_rgb_into == NULL ) {
    76             SDL_UnloadObject(lib.handle);
    77             return -1;
    78         }
    79 
    80         lib.webp_decode_rgba_into =
    81             ( uint8_t* (*) (const uint8_t*, size_t, uint8_t*, size_t, int ) )
    82             SDL_LoadFunction(lib.handle, "WebPDecodeRGBAInto" );
    83         if ( lib.webp_decode_rgba_into == NULL ) {
    84             SDL_UnloadObject(lib.handle);
    85             return -1;
    86         }
    87     }
    88     ++lib.loaded;
    89 
    90     return 0;
    91 }
    92 void IMG_QuitWEBP()
    93 {
    94     if ( lib.loaded == 0 ) {
    95         return;
    96     }
    97     if ( lib.loaded == 1 ) {
    98         SDL_UnloadObject(lib.handle);
    99     }
   100     --lib.loaded;
   101 }
   102 #else
   103 int IMG_InitWEBP()
   104 {
   105     if ( lib.loaded == 0 ) {
   106 #ifdef __MACOSX__
   107         extern VP8StatusCode WebPGetFeaturesInternal(const uint8_t*, size_t, WebPBitstreamFeatures*, int) __attribute__((weak_import));
   108         if ( WebPGetFeaturesInternal == NULL )
   109         {
   110             /* Missing weakly linked framework */
   111             IMG_SetError("Missing webp.framework");
   112             return -1;
   113         }
   114 #endif // __MACOSX__
   115 
   116         lib.webp_get_features_internal = WebPGetFeaturesInternal;
   117         lib.webp_decode_rgb_into = WebPDecodeRGBInto;
   118         lib.webp_decode_rgba_into = WebPDecodeRGBAInto;
   119     }
   120     ++lib.loaded;
   121 
   122     return 0;
   123 }
   124 void IMG_QuitWEBP()
   125 {
   126     if ( lib.loaded == 0 ) {
   127         return;
   128     }
   129     if ( lib.loaded == 1 ) {
   130     }
   131     --lib.loaded;
   132 }
   133 #endif /* LOAD_WEBP_DYNAMIC */
   134 
   135 static int webp_getinfo( SDL_RWops *src, int *datasize ) {
   136     Sint64 start;
   137     int is_WEBP;
   138     Uint8 magic[20];
   139 
   140     if ( !src )
   141         return 0;
   142     start = SDL_RWtell(src);
   143     is_WEBP = 0;
   144     if ( SDL_RWread(src, magic, 1, sizeof(magic)) == sizeof(magic) ) {
   145         if ( magic[ 0] == 'R' &&
   146                      magic[ 1] == 'I' &&
   147                      magic[ 2] == 'F' &&
   148                      magic[ 3] == 'F' &&
   149                                          magic[ 8] == 'W' &&
   150                                          magic[ 9] == 'E' &&
   151                                          magic[10] == 'B' &&
   152                                          magic[11] == 'P' &&
   153                                          magic[12] == 'V' &&
   154                                          magic[13] == 'P' &&
   155                                          magic[14] == '8' &&
   156 #if WEBP_DECODER_ABI_VERSION < 0x0003 /* old versions don't support WEBPVP8X and WEBPVP8L */
   157                                          magic[15] == ' ') {
   158 #else
   159                                          (magic[15] == ' ' || magic[15] == 'X' || magic[15] == 'L')) {
   160 #endif
   161             is_WEBP = 1;
   162             if ( datasize ) {
   163                 *datasize = (int)SDL_RWseek(src, 0, SEEK_END);
   164             }
   165         }
   166     }
   167     SDL_RWseek(src, start, RW_SEEK_SET);
   168     return(is_WEBP);
   169 }
   170 
   171 /* See if an image is contained in a data source */
   172 int IMG_isWEBP(SDL_RWops *src)
   173 {
   174     return webp_getinfo( src, NULL );
   175 }
   176 
   177 SDL_Surface *IMG_LoadWEBP_RW(SDL_RWops *src)
   178 {
   179     Sint64 start;
   180     const char *error = NULL;
   181     SDL_Surface *volatile surface = NULL;
   182     Uint32 Rmask;
   183     Uint32 Gmask;
   184     Uint32 Bmask;
   185     Uint32 Amask;
   186     WebPBitstreamFeatures features;
   187     int raw_data_size;
   188     uint8_t *raw_data = NULL;
   189     int r;
   190     uint8_t *ret;
   191 
   192     if ( !src ) {
   193         /* The error message has been set in SDL_RWFromFile */
   194         return NULL;
   195     }
   196 
   197     start = SDL_RWtell(src);
   198 
   199     if ( (IMG_Init(IMG_INIT_WEBP) & IMG_INIT_WEBP) == 0 ) {
   200         goto error;
   201     }
   202 
   203     raw_data_size = -1;
   204     if ( !webp_getinfo( src, &raw_data_size ) ) {
   205         error = "Invalid WEBP";
   206         goto error;
   207     }
   208 
   209     // seek to start of file
   210     SDL_RWseek(src, 0, RW_SEEK_SET );
   211 
   212     raw_data = (uint8_t*) SDL_malloc( raw_data_size );
   213     if ( raw_data == NULL ) {
   214         error = "Failed to allocate enought buffer for WEBP";
   215         goto error;
   216     }
   217 
   218     r = SDL_RWread(src, raw_data, 1, raw_data_size );
   219     if ( r != raw_data_size ) {
   220         error = "Failed to read WEBP";
   221         goto error;
   222     }
   223 
   224 #if 0
   225     // extract size of picture, not interesting since we don't know about alpha channel
   226     int width = -1, height = -1;
   227     if ( !WebPGetInfo( raw_data, raw_data_size, &width, &height ) ) {
   228         printf("WebPGetInfo has failed\n" );
   229         return NULL;
   230     }
   231 #endif
   232 
   233     if ( lib.webp_get_features_internal( raw_data, raw_data_size, &features, WEBP_DECODER_ABI_VERSION ) != VP8_STATUS_OK ) {
   234         error = "WebPGetFeatures has failed";
   235         goto error;
   236     }
   237 
   238     /* Check if it's ok !*/
   239 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
   240     Rmask = 0x000000FF;
   241     Gmask = 0x0000FF00;
   242     Bmask = 0x00FF0000;
   243     Amask = (features.has_alpha) ? 0xFF000000 : 0;
   244 #else
   245     {
   246         int s = (features.has_alpha) ? 0 : 8;
   247         Rmask = 0xFF000000 >> s;
   248         Gmask = 0x00FF0000 >> s;
   249         Bmask = 0x0000FF00 >> s;
   250         Amask = 0x000000FF >> s;
   251     }
   252 #endif
   253 
   254     surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
   255             features.width, features.height,
   256             features.has_alpha?32:24, Rmask,Gmask,Bmask,Amask);
   257 
   258     if ( surface == NULL ) {
   259         error = "Failed to allocate SDL_Surface";
   260         goto error;
   261     }
   262 
   263     if ( features.has_alpha ) {
   264         ret = lib.webp_decode_rgba_into( raw_data, raw_data_size, (uint8_t *)surface->pixels, surface->pitch * surface->h,  surface->pitch );
   265     } else {
   266         ret = lib.webp_decode_rgb_into( raw_data, raw_data_size, (uint8_t *)surface->pixels, surface->pitch * surface->h,  surface->pitch );
   267     }
   268 
   269     if ( !ret ) {
   270         error = "Failed to decode WEBP";
   271         goto error;
   272     }
   273 
   274     if ( raw_data ) {
   275         SDL_free( raw_data );
   276     }
   277 
   278     return surface;
   279 
   280 
   281 error:
   282 
   283     if ( raw_data ) {
   284         SDL_free( raw_data );
   285     }
   286 
   287     if ( surface ) {
   288         SDL_FreeSurface( surface );
   289     }
   290 
   291     if ( error ) {
   292         IMG_SetError( "%s", error );
   293     }
   294 
   295     SDL_RWseek(src, start, RW_SEEK_SET);
   296     return(NULL);
   297 }
   298 
   299 #else
   300 
   301 int IMG_InitWEBP()
   302 {
   303     IMG_SetError("WEBP images are not supported");
   304     return(-1);
   305 }
   306 
   307 void IMG_QuitWEBP()
   308 {
   309 }
   310 
   311 /* See if an image is contained in a data source */
   312 int IMG_isWEBP(SDL_RWops *src)
   313 {
   314     return(0);
   315 }
   316 
   317 /* Load a WEBP type image from an SDL datasource */
   318 SDL_Surface *IMG_LoadWEBP_RW(SDL_RWops *src)
   319 {
   320     return(NULL);
   321 }
   322 
   323 #endif /* LOAD_WEBP */