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