]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/libsdl-image/contrib/IMG_png.c
f00af1ce49cc607391514a7cdc97c561e1448e1e
[l4.git] / l4 / pkg / libsdl-image / contrib / IMG_png.c
1 /*
2     SDL_image:  An example image loading library for use with SDL
3     Copyright (C) 1997-2009 Sam Lantinga
4
5     This library is free software; you can redistribute it and/or
6     modify it under the terms of the GNU Lesser General Public
7     License as published by the Free Software Foundation; either
8     version 2.1 of the License, or (at your option) any later version.
9
10     This library is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13     Lesser General Public License for more details.
14
15     You should have received a copy of the GNU Lesser General Public
16     License along with this library; if not, write to the Free Software
17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
19     Sam Lantinga
20     slouken@libsdl.org
21 */
22
23 /* This is a PNG image file loading framework */
24
25 #include <stdlib.h>
26 #include <stdio.h>
27
28 #include "SDL_image.h"
29
30 #ifdef LOAD_PNG
31
32 /*=============================================================================
33         File: SDL_png.c
34      Purpose: A PNG loader and saver for the SDL library      
35     Revision: 
36   Created by: Philippe Lavoie          (2 November 1998)
37               lavoie@zeus.genie.uottawa.ca
38  Modified by: 
39
40  Copyright notice:
41           Copyright (C) 1998 Philippe Lavoie
42  
43           This library is free software; you can redistribute it and/or
44           modify it under the terms of the GNU Library General Public
45           License as published by the Free Software Foundation; either
46           version 2 of the License, or (at your option) any later version.
47  
48           This library is distributed in the hope that it will be useful,
49           but WITHOUT ANY WARRANTY; without even the implied warranty of
50           MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
51           Library General Public License for more details.
52  
53           You should have received a copy of the GNU Library General Public
54           License along with this library; if not, write to the Free
55           Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
56
57     Comments: The load and save routine are basically the ones you can find
58              in the example.c file from the libpng distribution.
59
60   Changes:
61     5/17/99 - Modified to use the new SDL data sources - Sam Lantinga
62
63 =============================================================================*/
64
65 #include "SDL_endian.h"
66
67 #ifdef macintosh
68 #define MACOS
69 #endif
70 #include <png.h>
71
72
73 static struct {
74         int loaded;
75         void *handle;
76         png_infop (*png_create_info_struct) (png_structp png_ptr);
77         png_structp (*png_create_read_struct) (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn);
78         void (*png_destroy_read_struct) (png_structpp png_ptr_ptr, png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr);
79         png_uint_32 (*png_get_IHDR) (png_structp png_ptr, png_infop info_ptr, png_uint_32 *width, png_uint_32 *height, int *bit_depth, int *color_type, int *interlace_method, int *compression_method, int *filter_method);
80         png_voidp (*png_get_io_ptr) (png_structp png_ptr);
81         png_uint_32 (*png_get_tRNS) (png_structp png_ptr, png_infop info_ptr, png_bytep *trans, int *num_trans, png_color_16p *trans_values);
82         png_uint_32 (*png_get_valid) (png_structp png_ptr, png_infop info_ptr, png_uint_32 flag);
83         void (*png_read_image) (png_structp png_ptr, png_bytepp image);
84         void (*png_read_info) (png_structp png_ptr, png_infop info_ptr);
85         void (*png_read_update_info) (png_structp png_ptr, png_infop info_ptr);
86         void (*png_set_expand) (png_structp png_ptr);
87         void (*png_set_gray_to_rgb) (png_structp png_ptr);
88         void (*png_set_packing) (png_structp png_ptr);
89         void (*png_set_read_fn) (png_structp png_ptr, png_voidp io_ptr, png_rw_ptr read_data_fn);
90         void (*png_set_strip_16) (png_structp png_ptr);
91         int (*png_sig_cmp) (png_bytep sig, png_size_t start, png_size_t num_to_check);
92 } lib;
93
94 #ifdef LOAD_PNG_DYNAMIC
95 int IMG_InitPNG()
96 {
97         if ( lib.loaded == 0 ) {
98                 lib.handle = SDL_LoadObject(LOAD_PNG_DYNAMIC);
99                 if ( lib.handle == NULL ) {
100                         return -1;
101                 }
102                 lib.png_create_info_struct =
103                         (png_infop (*) (png_structp))
104                         SDL_LoadFunction(lib.handle, "png_create_info_struct");
105                 if ( lib.png_create_info_struct == NULL ) {
106                         SDL_UnloadObject(lib.handle);
107                         return -1;
108                 }
109                 lib.png_create_read_struct =
110                         (png_structp (*) (png_const_charp, png_voidp, png_error_ptr, png_error_ptr))
111                         SDL_LoadFunction(lib.handle, "png_create_read_struct");
112                 if ( lib.png_create_read_struct == NULL ) {
113                         SDL_UnloadObject(lib.handle);
114                         return -1;
115                 }
116                 lib.png_destroy_read_struct =
117                         (void (*) (png_structpp, png_infopp, png_infopp))
118                         SDL_LoadFunction(lib.handle, "png_destroy_read_struct");
119                 if ( lib.png_destroy_read_struct == NULL ) {
120                         SDL_UnloadObject(lib.handle);
121                         return -1;
122                 }
123                 lib.png_get_IHDR =
124                         (png_uint_32 (*) (png_structp, png_infop, png_uint_32 *, png_uint_32 *, int *, int *, int *, int *, int *))
125                         SDL_LoadFunction(lib.handle, "png_get_IHDR");
126                 if ( lib.png_get_IHDR == NULL ) {
127                         SDL_UnloadObject(lib.handle);
128                         return -1;
129                 }
130                 lib.png_get_io_ptr =
131                         (png_voidp (*) (png_structp))
132                         SDL_LoadFunction(lib.handle, "png_get_io_ptr");
133                 if ( lib.png_get_io_ptr == NULL ) {
134                         SDL_UnloadObject(lib.handle);
135                         return -1;
136                 }
137                 lib.png_get_tRNS =
138                         (png_uint_32 (*) (png_structp, png_infop, png_bytep *, int *, png_color_16p *))
139                         SDL_LoadFunction(lib.handle, "png_get_tRNS");
140                 if ( lib.png_get_tRNS == NULL ) {
141                         SDL_UnloadObject(lib.handle);
142                         return -1;
143                 }
144                 lib.png_get_valid =
145                         (png_uint_32 (*) (png_structp, png_infop, png_uint_32))
146                         SDL_LoadFunction(lib.handle, "png_get_valid");
147                 if ( lib.png_get_valid == NULL ) {
148                         SDL_UnloadObject(lib.handle);
149                         return -1;
150                 }
151                 lib.png_read_image =
152                         (void (*) (png_structp, png_bytepp))
153                         SDL_LoadFunction(lib.handle, "png_read_image");
154                 if ( lib.png_read_image == NULL ) {
155                         SDL_UnloadObject(lib.handle);
156                         return -1;
157                 }
158                 lib.png_read_info =
159                         (void (*) (png_structp, png_infop))
160                         SDL_LoadFunction(lib.handle, "png_read_info");
161                 if ( lib.png_read_info == NULL ) {
162                         SDL_UnloadObject(lib.handle);
163                         return -1;
164                 }
165                 lib.png_read_update_info =
166                         (void (*) (png_structp, png_infop))
167                         SDL_LoadFunction(lib.handle, "png_read_update_info");
168                 if ( lib.png_read_update_info == NULL ) {
169                         SDL_UnloadObject(lib.handle);
170                         return -1;
171                 }
172                 lib.png_set_expand =
173                         (void (*) (png_structp))
174                         SDL_LoadFunction(lib.handle, "png_set_expand");
175                 if ( lib.png_set_expand == NULL ) {
176                         SDL_UnloadObject(lib.handle);
177                         return -1;
178                 }
179                 lib.png_set_gray_to_rgb =
180                         (void (*) (png_structp))
181                         SDL_LoadFunction(lib.handle, "png_set_gray_to_rgb");
182                 if ( lib.png_set_gray_to_rgb == NULL ) {
183                         SDL_UnloadObject(lib.handle);
184                         return -1;
185                 }
186                 lib.png_set_packing =
187                         (void (*) (png_structp))
188                         SDL_LoadFunction(lib.handle, "png_set_packing");
189                 if ( lib.png_set_packing == NULL ) {
190                         SDL_UnloadObject(lib.handle);
191                         return -1;
192                 }
193                 lib.png_set_read_fn =
194                         (void (*) (png_structp, png_voidp, png_rw_ptr))
195                         SDL_LoadFunction(lib.handle, "png_set_read_fn");
196                 if ( lib.png_set_read_fn == NULL ) {
197                         SDL_UnloadObject(lib.handle);
198                         return -1;
199                 }
200                 lib.png_set_strip_16 =
201                         (void (*) (png_structp))
202                         SDL_LoadFunction(lib.handle, "png_set_strip_16");
203                 if ( lib.png_set_strip_16 == NULL ) {
204                         SDL_UnloadObject(lib.handle);
205                         return -1;
206                 }
207                 lib.png_sig_cmp =
208                         (int (*) (png_bytep, png_size_t, png_size_t))
209                         SDL_LoadFunction(lib.handle, "png_sig_cmp");
210                 if ( lib.png_sig_cmp == NULL ) {
211                         SDL_UnloadObject(lib.handle);
212                         return -1;
213                 }
214         }
215         ++lib.loaded;
216
217         return 0;
218 }
219 void IMG_QuitPNG()
220 {
221         if ( lib.loaded == 0 ) {
222                 return;
223         }
224         if ( lib.loaded == 1 ) {
225                 SDL_UnloadObject(lib.handle);
226         }
227         --lib.loaded;
228 }
229 #else
230 int IMG_InitPNG()
231 {
232         if ( lib.loaded == 0 ) {
233                 lib.png_create_info_struct = png_create_info_struct;
234                 lib.png_create_read_struct = png_create_read_struct;
235                 lib.png_destroy_read_struct = png_destroy_read_struct;
236                 lib.png_get_IHDR = png_get_IHDR;
237                 lib.png_get_io_ptr = png_get_io_ptr;
238                 lib.png_get_tRNS = png_get_tRNS;
239                 lib.png_get_valid = png_get_valid;
240                 lib.png_read_image = png_read_image;
241                 lib.png_read_info = png_read_info;
242                 lib.png_read_update_info = png_read_update_info;
243                 lib.png_set_expand = png_set_expand;
244                 lib.png_set_gray_to_rgb = png_set_gray_to_rgb;
245                 lib.png_set_packing = png_set_packing;
246                 lib.png_set_read_fn = png_set_read_fn;
247                 lib.png_set_strip_16 = png_set_strip_16;
248                 lib.png_sig_cmp = png_sig_cmp;
249         }
250         ++lib.loaded;
251
252         return 0;
253 }
254 void IMG_QuitPNG()
255 {
256         if ( lib.loaded == 0 ) {
257                 return;
258         }
259         if ( lib.loaded == 1 ) {
260         }
261         --lib.loaded;
262 }
263 #endif /* LOAD_PNG_DYNAMIC */
264
265 /* See if an image is contained in a data source */
266 int IMG_isPNG(SDL_RWops *src)
267 {
268         int start;
269         int is_PNG;
270         Uint8 magic[4];
271
272         if ( !src )
273                 return 0;
274         start = SDL_RWtell(src);
275         is_PNG = 0;
276         if ( SDL_RWread(src, magic, 1, sizeof(magic)) == sizeof(magic) ) {
277                 if ( magic[0] == 0x89 &&
278                      magic[1] == 'P' &&
279                      magic[2] == 'N' &&
280                      magic[3] == 'G' ) {
281                         is_PNG = 1;
282                 }
283         }
284         SDL_RWseek(src, start, SEEK_SET);
285         return(is_PNG);
286 }
287
288 /* Load a PNG type image from an SDL datasource */
289 static void png_read_data(png_structp ctx, png_bytep area, png_size_t size)
290 {
291         SDL_RWops *src;
292
293         src = (SDL_RWops *)lib.png_get_io_ptr(ctx);
294         SDL_RWread(src, area, size, 1);
295 }
296 SDL_Surface *IMG_LoadPNG_RW(SDL_RWops *src)
297 {
298         int start;
299         const char *error;
300         SDL_Surface *volatile surface;
301         png_structp png_ptr;
302         png_infop info_ptr;
303         png_uint_32 width, height;
304         int bit_depth, color_type, interlace_type;
305         Uint32 Rmask;
306         Uint32 Gmask;
307         Uint32 Bmask;
308         Uint32 Amask;
309         SDL_Palette *palette;
310         png_bytep *volatile row_pointers;
311         int row, i;
312         volatile int ckey = -1;
313         png_color_16 *transv;
314
315         if ( !src ) {
316                 /* The error message has been set in SDL_RWFromFile */
317                 return NULL;
318         }
319         start = SDL_RWtell(src);
320
321         if ( IMG_Init(IMG_INIT_PNG) < 0 ) {
322                 return NULL;
323         }
324
325         /* Initialize the data we will clean up when we're done */
326         error = NULL;
327         png_ptr = NULL; info_ptr = NULL; row_pointers = NULL; surface = NULL;
328
329         /* Create the PNG loading context structure */
330         png_ptr = lib.png_create_read_struct(PNG_LIBPNG_VER_STRING,
331                                           NULL,NULL,NULL);
332         if (png_ptr == NULL){
333                 error = "Couldn't allocate memory for PNG file or incompatible PNG dll";
334                 goto done;
335         }
336
337          /* Allocate/initialize the memory for image information.  REQUIRED. */
338         info_ptr = lib.png_create_info_struct(png_ptr);
339         if (info_ptr == NULL) {
340                 error = "Couldn't create image information for PNG file";
341                 goto done;
342         }
343
344         /* Set error handling if you are using setjmp/longjmp method (this is
345          * the normal method of doing things with libpng).  REQUIRED unless you
346          * set up your own error handlers in png_create_read_struct() earlier.
347          */
348         if ( setjmp(png_ptr->jmpbuf) ) {
349                 error = "Error reading the PNG file.";
350                 goto done;
351         }
352
353         /* Set up the input control */
354         lib.png_set_read_fn(png_ptr, src, png_read_data);
355
356         /* Read PNG header info */
357         lib.png_read_info(png_ptr, info_ptr);
358         lib.png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth,
359                         &color_type, &interlace_type, NULL, NULL);
360
361         /* tell libpng to strip 16 bit/color files down to 8 bits/color */
362         lib.png_set_strip_16(png_ptr) ;
363
364         /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single
365          * byte into separate bytes (useful for paletted and grayscale images).
366          */
367         lib.png_set_packing(png_ptr);
368
369         /* scale greyscale values to the range 0..255 */
370         if(color_type == PNG_COLOR_TYPE_GRAY)
371                 lib.png_set_expand(png_ptr);
372
373         /* For images with a single "transparent colour", set colour key;
374            if more than one index has transparency, or if partially transparent
375            entries exist, use full alpha channel */
376         if (lib.png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
377                 int num_trans;
378                 Uint8 *trans;
379                 lib.png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans,
380                              &transv);
381                 if(color_type == PNG_COLOR_TYPE_PALETTE) {
382                     /* Check if all tRNS entries are opaque except one */
383                     int i, t = -1;
384                     for(i = 0; i < num_trans; i++)
385                         if(trans[i] == 0) {
386                             if(t >= 0)
387                                 break;
388                             t = i;
389                         } else if(trans[i] != 255)
390                             break;
391                     if(i == num_trans) {
392                         /* exactly one transparent index */
393                         ckey = t;
394                     } else {
395                         /* more than one transparent index, or translucency */
396                         lib.png_set_expand(png_ptr);
397                     }
398                 } else
399                     ckey = 0; /* actual value will be set later */
400         }
401
402         if ( color_type == PNG_COLOR_TYPE_GRAY_ALPHA )
403                 lib.png_set_gray_to_rgb(png_ptr);
404
405         lib.png_read_update_info(png_ptr, info_ptr);
406
407         lib.png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth,
408                         &color_type, &interlace_type, NULL, NULL);
409
410         /* Allocate the SDL surface to hold the image */
411         Rmask = Gmask = Bmask = Amask = 0 ; 
412         if ( color_type != PNG_COLOR_TYPE_PALETTE ) {
413                 if ( SDL_BYTEORDER == SDL_LIL_ENDIAN ) {
414                         Rmask = 0x000000FF;
415                         Gmask = 0x0000FF00;
416                         Bmask = 0x00FF0000;
417                         Amask = (info_ptr->channels == 4) ? 0xFF000000 : 0;
418                 } else {
419                         int s = (info_ptr->channels == 4) ? 0 : 8;
420                         Rmask = 0xFF000000 >> s;
421                         Gmask = 0x00FF0000 >> s;
422                         Bmask = 0x0000FF00 >> s;
423                         Amask = 0x000000FF >> s;
424                 }
425         }
426         surface = SDL_AllocSurface(SDL_SWSURFACE, width, height,
427                         bit_depth*info_ptr->channels, Rmask,Gmask,Bmask,Amask);
428         if ( surface == NULL ) {
429                 error = "Out of memory";
430                 goto done;
431         }
432
433         if(ckey != -1) {
434                 if(color_type != PNG_COLOR_TYPE_PALETTE)
435                         /* FIXME: Should these be truncated or shifted down? */
436                         ckey = SDL_MapRGB(surface->format,
437                                          (Uint8)transv->red,
438                                          (Uint8)transv->green,
439                                          (Uint8)transv->blue);
440                 SDL_SetColorKey(surface, SDL_SRCCOLORKEY, ckey);
441         }
442
443         /* Create the array of pointers to image data */
444         row_pointers = (png_bytep*) malloc(sizeof(png_bytep)*height);
445         if ( (row_pointers == NULL) ) {
446                 error = "Out of memory";
447                 goto done;
448         }
449         for (row = 0; row < (int)height; row++) {
450                 row_pointers[row] = (png_bytep)
451                                 (Uint8 *)surface->pixels + row*surface->pitch;
452         }
453
454         /* Read the entire image in one go */
455         lib.png_read_image(png_ptr, row_pointers);
456
457         /* and we're done!  (png_read_end() can be omitted if no processing of
458          * post-IDAT text/time/etc. is desired)
459          * In some cases it can't read PNG's created by some popular programs (ACDSEE),
460          * we do not want to process comments, so we omit png_read_end
461
462         lib.png_read_end(png_ptr, info_ptr);
463         */
464
465         /* Load the palette, if any */
466         palette = surface->format->palette;
467         if ( palette ) {
468             if(color_type == PNG_COLOR_TYPE_GRAY) {
469                 palette->ncolors = 256;
470                 for(i = 0; i < 256; i++) {
471                     palette->colors[i].r = i;
472                     palette->colors[i].g = i;
473                     palette->colors[i].b = i;
474                 }
475             } else if (info_ptr->num_palette > 0 ) {
476                 palette->ncolors = info_ptr->num_palette; 
477                 for( i=0; i<info_ptr->num_palette; ++i ) {
478                     palette->colors[i].b = info_ptr->palette[i].blue;
479                     palette->colors[i].g = info_ptr->palette[i].green;
480                     palette->colors[i].r = info_ptr->palette[i].red;
481                 }
482             }
483         }
484
485 done:   /* Clean up and return */
486         if ( png_ptr ) {
487                 lib.png_destroy_read_struct(&png_ptr,
488                                         info_ptr ? &info_ptr : (png_infopp)0,
489                                                                 (png_infopp)0);
490         }
491         if ( row_pointers ) {
492                 free(row_pointers);
493         }
494         if ( error ) {
495                 SDL_RWseek(src, start, SEEK_SET);
496                 if ( surface ) {
497                         SDL_FreeSurface(surface);
498                         surface = NULL;
499                 }
500                 IMG_SetError(error);
501         }
502         return(surface); 
503 }
504
505 #else
506
507 int IMG_InitPNG()
508 {
509         IMG_SetError("PNG images are not supported");
510         return(-1);
511 }
512
513 void IMG_QuitPNG()
514 {
515 }
516
517 /* See if an image is contained in a data source */
518 int IMG_isPNG(SDL_RWops *src)
519 {
520         return(0);
521 }
522
523 /* Load a PNG type image from an SDL datasource */
524 SDL_Surface *IMG_LoadPNG_RW(SDL_RWops *src)
525 {
526         return(NULL);
527 }
528
529 #endif /* LOAD_PNG */