New comit of SDL2
[supertux.git] / src / SDL2 / IMG_webp.c
1 /*
2   SDL_image:  An example image loading library for use with SDL
3   Copyright (C) 1997-2013 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) ) {
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     s = (features.has_alpha) ? 0 : 8;
246     Rmask = 0xFF000000 >> s;
247     Gmask = 0x00FF0000 >> s;
248     Bmask = 0x0000FF00 >> s;
249     Amask = 0x000000FF >> s;
250 #endif
251
252     surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
253             features.width, features.height,
254             features.has_alpha?32:24, Rmask,Gmask,Bmask,Amask);
255
256     if ( surface == NULL ) {
257         error = "Failed to allocate SDL_Surface";
258         goto error;
259     }
260
261     if ( features.has_alpha ) {
262         ret = lib.webp_decode_rgba_into( raw_data, raw_data_size, (uint8_t *)surface->pixels, surface->pitch * surface->h,  surface->pitch );
263     } else {
264         ret = lib.webp_decode_rgb_into( raw_data, raw_data_size, (uint8_t *)surface->pixels, surface->pitch * surface->h,  surface->pitch );
265     }
266
267     if ( !ret ) {
268         error = "Failed to decode WEBP";
269         goto error;
270     }
271
272     return surface;
273
274
275 error:
276
277     if ( surface ) {
278         SDL_FreeSurface( surface );
279     }
280
281     if ( raw_data ) {
282         SDL_free( raw_data );
283     }
284
285     if ( error ) {
286         IMG_SetError( error );
287     }
288
289     SDL_RWseek(src, start, RW_SEEK_SET);
290     return(NULL);
291 }
292
293 #else
294
295 int IMG_InitWEBP()
296 {
297     IMG_SetError("WEBP images are not supported");
298     return(-1);
299 }
300
301 void IMG_QuitWEBP()
302 {
303 }
304
305 /* See if an image is contained in a data source */
306 int IMG_isWEBP(SDL_RWops *src)
307 {
308     return(0);
309 }
310
311 /* Load a WEBP type image from an SDL datasource */
312 SDL_Surface *IMG_LoadWEBP_RW(SDL_RWops *src)
313 {
314     return(NULL);
315 }
316
317 #endif /* LOAD_WEBP */