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