1 /*
2   SDL_image:  An example image loading library for use with SDL
3   Copyright (C) 1997-2019 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 #if WEBP_DECODER_ABI_VERSION < 0x0100
48     VP8StatusCode (*WebPGetFeaturesInternal) (const uint8_t *data, uint32_t data_size, WebPBitstreamFeatures* const features, int decoder_abi_version);
49     uint8_t*    (*WebPDecodeRGBInto) (const uint8_t* data, uint32_t data_size, uint8_t* output_buffer, int output_buffer_size, int output_stride);
50     uint8_t*    (*WebPDecodeRGBAInto) (const uint8_t* data, uint32_t data_size, uint8_t* output_buffer, int output_buffer_size, int output_stride);
51 #else
52     VP8StatusCode (*WebPGetFeaturesInternal) (const uint8_t *data, size_t data_size, WebPBitstreamFeatures* features, int decoder_abi_version);
53     uint8_t*    (*WebPDecodeRGBInto) (const uint8_t* data, size_t data_size, uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
54     uint8_t*    (*WebPDecodeRGBAInto) (const uint8_t* data, size_t data_size, uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
55 #endif
56 } lib;
57 
58 #ifdef LOAD_WEBP_DYNAMIC
59 #define FUNCTION_LOADER(FUNC, SIG) \
60     lib.FUNC = (SIG) SDL_LoadFunction(lib.handle, #FUNC); \
61     if (lib.FUNC == NULL) { SDL_UnloadObject(lib.handle); return -1; }
62 #else
63 #define FUNCTION_LOADER(FUNC, SIG) \
64     lib.FUNC = FUNC;
65 #endif
66 
IMG_InitWEBP()67 int IMG_InitWEBP()
68 {
69     if ( lib.loaded == 0 ) {
70 #ifdef LOAD_WEBP_DYNAMIC
71         lib.handle = SDL_LoadObject(LOAD_WEBP_DYNAMIC);
72         if ( lib.handle == NULL ) {
73             return -1;
74         }
75 #endif
76 #if WEBP_DECODER_ABI_VERSION < 0x0100
77         FUNCTION_LOADER(WebPGetFeaturesInternal, VP8StatusCode (*) (const uint8_t *data, uint32_t data_size, WebPBitstreamFeatures* const features, int decoder_abi_version))
78         FUNCTION_LOADER(WebPDecodeRGBInto, uint8_t * (*) (const uint8_t* data, uint32_t data_size, uint8_t* output_buffer, int output_buffer_size, int output_stride))
79         FUNCTION_LOADER(WebPDecodeRGBAInto, uint8_t * (*) (const uint8_t* data, uint32_t data_size, uint8_t* output_buffer, int output_buffer_size, int output_stride))
80 #else
81         FUNCTION_LOADER(WebPGetFeaturesInternal, VP8StatusCode (*) (const uint8_t *data, size_t data_size, WebPBitstreamFeatures* features, int decoder_abi_version))
82         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))
83         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))
84 #endif
85     }
86     ++lib.loaded;
87 
88     return 0;
89 }
IMG_QuitWEBP()90 void IMG_QuitWEBP()
91 {
92     if ( lib.loaded == 0 ) {
93         return;
94     }
95     if ( lib.loaded == 1 ) {
96 #ifdef LOAD_WEBP_DYNAMIC
97         SDL_UnloadObject(lib.handle);
98 #endif
99     }
100     --lib.loaded;
101 }
102 
webp_getinfo(SDL_RWops * src,int * datasize)103 static int webp_getinfo( SDL_RWops *src, int *datasize ) {
104     Sint64 start;
105     int is_WEBP;
106     Uint8 magic[20];
107 
108     if ( !src ) {
109         return 0;
110     }
111     start = SDL_RWtell(src);
112     is_WEBP = 0;
113     if ( SDL_RWread(src, magic, 1, sizeof(magic)) == sizeof(magic) ) {
114         if ( magic[ 0] == 'R' &&
115              magic[ 1] == 'I' &&
116              magic[ 2] == 'F' &&
117              magic[ 3] == 'F' &&
118              magic[ 8] == 'W' &&
119              magic[ 9] == 'E' &&
120              magic[10] == 'B' &&
121              magic[11] == 'P' &&
122              magic[12] == 'V' &&
123              magic[13] == 'P' &&
124              magic[14] == '8' &&
125          /* old versions don't support VP8X and VP8L */
126          #if (WEBP_DECODER_ABI_VERSION < 0x0003)
127              magic[15] == ' '
128          #else
129             (magic[15] == ' ' || magic[15] == 'X' || magic[15] == 'L')
130          #endif
131            ) {
132             is_WEBP = 1;
133             if ( datasize ) {
134                 *datasize = (int)(SDL_RWseek(src, 0, RW_SEEK_END) - start);
135             }
136         }
137     }
138     SDL_RWseek(src, start, RW_SEEK_SET);
139     return(is_WEBP);
140 }
141 
142 /* See if an image is contained in a data source */
IMG_isWEBP(SDL_RWops * src)143 int IMG_isWEBP(SDL_RWops *src)
144 {
145     return webp_getinfo( src, NULL );
146 }
147 
IMG_LoadWEBP_RW(SDL_RWops * src)148 SDL_Surface *IMG_LoadWEBP_RW(SDL_RWops *src)
149 {
150     Sint64 start;
151     const char *error = NULL;
152     SDL_Surface *volatile surface = NULL;
153     Uint32 Rmask;
154     Uint32 Gmask;
155     Uint32 Bmask;
156     Uint32 Amask;
157     WebPBitstreamFeatures features;
158     int raw_data_size;
159     uint8_t *raw_data = NULL;
160     int r;
161     uint8_t *ret;
162 
163     if ( !src ) {
164         /* The error message has been set in SDL_RWFromFile */
165         return NULL;
166     }
167 
168     start = SDL_RWtell(src);
169 
170     if ( (IMG_Init(IMG_INIT_WEBP) & IMG_INIT_WEBP) == 0 ) {
171         goto error;
172     }
173 
174     raw_data_size = -1;
175     if ( !webp_getinfo( src, &raw_data_size ) ) {
176         error = "Invalid WEBP";
177         goto error;
178     }
179 
180     raw_data = (uint8_t*) SDL_malloc( raw_data_size );
181     if ( raw_data == NULL ) {
182         error = "Failed to allocate enough buffer for WEBP";
183         goto error;
184     }
185 
186     r = (int)SDL_RWread(src, raw_data, 1, raw_data_size );
187     if ( r != raw_data_size ) {
188         error = "Failed to read WEBP";
189         goto error;
190     }
191 
192 #if 0
193     // extract size of picture, not interesting since we don't know about alpha channel
194     int width = -1, height = -1;
195     if ( !WebPGetInfo( raw_data, raw_data_size, &width, &height ) ) {
196         printf("WebPGetInfo has failed\n" );
197         return NULL;
198     }
199 #endif
200 
201     if ( lib.WebPGetFeaturesInternal( raw_data, raw_data_size, &features, WEBP_DECODER_ABI_VERSION ) != VP8_STATUS_OK ) {
202         error = "WebPGetFeatures has failed";
203         goto error;
204     }
205 
206     /* Check if it's ok !*/
207 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
208     Rmask = 0x000000FF;
209     Gmask = 0x0000FF00;
210     Bmask = 0x00FF0000;
211     Amask = (features.has_alpha) ? 0xFF000000 : 0;
212 #else
213     {
214         int s = (features.has_alpha) ? 0 : 8;
215         Rmask = 0xFF000000 >> s;
216         Gmask = 0x00FF0000 >> s;
217         Bmask = 0x0000FF00 >> s;
218         Amask = 0x000000FF >> s;
219     }
220 #endif
221 
222     surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
223             features.width, features.height,
224             features.has_alpha?32:24, Rmask,Gmask,Bmask,Amask);
225 
226     if ( surface == NULL ) {
227         error = "Failed to allocate SDL_Surface";
228         goto error;
229     }
230 
231     if ( features.has_alpha ) {
232         ret = lib.WebPDecodeRGBAInto( raw_data, raw_data_size, (uint8_t *)surface->pixels, surface->pitch * surface->h,  surface->pitch );
233     } else {
234         ret = lib.WebPDecodeRGBInto( raw_data, raw_data_size, (uint8_t *)surface->pixels, surface->pitch * surface->h,  surface->pitch );
235     }
236 
237     if ( !ret ) {
238         error = "Failed to decode WEBP";
239         goto error;
240     }
241 
242     if ( raw_data ) {
243         SDL_free( raw_data );
244     }
245 
246     return surface;
247 
248 
249 error:
250 
251     if ( raw_data ) {
252         SDL_free( raw_data );
253     }
254 
255     if ( surface ) {
256         SDL_FreeSurface( surface );
257     }
258 
259     if ( error ) {
260         IMG_SetError( "%s", error );
261     }
262 
263     SDL_RWseek(src, start, RW_SEEK_SET);
264     return(NULL);
265 }
266 
267 #else
268 
IMG_InitWEBP()269 int IMG_InitWEBP()
270 {
271     IMG_SetError("WEBP images are not supported");
272     return(-1);
273 }
274 
IMG_QuitWEBP()275 void IMG_QuitWEBP()
276 {
277 }
278 
279 /* See if an image is contained in a data source */
IMG_isWEBP(SDL_RWops * src)280 int IMG_isWEBP(SDL_RWops *src)
281 {
282     return(0);
283 }
284 
285 /* Load a WEBP type image from an SDL datasource */
IMG_LoadWEBP_RW(SDL_RWops * src)286 SDL_Surface *IMG_LoadWEBP_RW(SDL_RWops *src)
287 {
288     return(NULL);
289 }
290 
291 #endif /* LOAD_WEBP */
292