1 /******************************************************************************
2  * $Id$
3  *
4  * Project:  MapServer
5  * Purpose:  Low level PNG/JPEG/GIF image io native functions
6  * Author:   Thomas Bonfort (tbonfort)
7  *
8  ******************************************************************************
9  * Copyright (c) 2009 Thomas Bonfort
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included in
19  * all copies of this Software or works derived from this Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  ****************************************************************************/
29 
30 #include "mapserver.h"
31 #include <png.h>
32 #include <setjmp.h>
33 #include <assert.h>
34 #include <jpeglib.h>
35 #include <stdlib.h>
36 
37 #ifdef USE_GIF
38 #include <gif_lib.h>
39 #endif
40 
41 
42 
43 typedef struct _streamInfo {
44   FILE *fp;
45   bufferObj *buffer;
46 } streamInfo;
47 
48 static
png_write_data_to_stream(png_structp png_ptr,png_bytep data,png_size_t length)49 void png_write_data_to_stream(png_structp png_ptr, png_bytep data, png_size_t length)
50 {
51   FILE *fp = ((streamInfo*)png_get_io_ptr(png_ptr))->fp;
52   msIO_fwrite(data,length,1,fp);
53 }
54 
55 static
png_write_data_to_buffer(png_structp png_ptr,png_bytep data,png_size_t length)56 void png_write_data_to_buffer(png_structp png_ptr, png_bytep data, png_size_t length)
57 {
58   bufferObj *buffer = ((streamInfo*)png_get_io_ptr(png_ptr))->buffer;
59   msBufferAppend(buffer,data,length);
60 }
61 
62 static
png_flush_data(png_structp png_ptr)63 void png_flush_data(png_structp png_ptr)
64 {
65   /* do nothing */
66 }
67 
68 typedef struct {
69   struct jpeg_destination_mgr pub;
70   unsigned char *data;
71 } ms_destination_mgr;
72 
73 typedef struct {
74   ms_destination_mgr mgr;
75   FILE *stream;
76 } ms_stream_destination_mgr;
77 
78 typedef struct {
79   ms_destination_mgr mgr;
80   bufferObj *buffer;
81 } ms_buffer_destination_mgr;
82 
83 #define OUTPUT_BUF_SIZE 4096
84 
85 static void
jpeg_init_destination(j_compress_ptr cinfo)86 jpeg_init_destination (j_compress_ptr cinfo)
87 {
88   ms_destination_mgr *dest = (ms_destination_mgr*) cinfo->dest;
89 
90   /* Allocate the output buffer --- it will be released when done with image */
91   dest->data = (unsigned char *)
92                (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
93                    OUTPUT_BUF_SIZE * sizeof (unsigned char));
94 
95   dest->pub.next_output_byte = dest->data;
96   dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
97 }
98 
99 static
jpeg_stream_term_destination(j_compress_ptr cinfo)100 void jpeg_stream_term_destination (j_compress_ptr cinfo)
101 {
102   ms_stream_destination_mgr *dest = (ms_stream_destination_mgr*) cinfo->dest;
103   msIO_fwrite(dest->mgr.data, OUTPUT_BUF_SIZE-dest->mgr.pub.free_in_buffer, 1, dest->stream);
104   dest->mgr.pub.next_output_byte = dest->mgr.data;
105   dest->mgr.pub.free_in_buffer = OUTPUT_BUF_SIZE;
106 }
107 
108 static
jpeg_buffer_term_destination(j_compress_ptr cinfo)109 void jpeg_buffer_term_destination (j_compress_ptr cinfo)
110 {
111   ms_buffer_destination_mgr *dest = (ms_buffer_destination_mgr*) cinfo->dest;
112   msBufferAppend(dest->buffer, dest->mgr.data, OUTPUT_BUF_SIZE-dest->mgr.pub.free_in_buffer);
113   dest->mgr.pub.next_output_byte = dest->mgr.data;
114   dest->mgr.pub.free_in_buffer = OUTPUT_BUF_SIZE;
115 }
116 
117 static
jpeg_stream_empty_output_buffer(j_compress_ptr cinfo)118 boolean jpeg_stream_empty_output_buffer (j_compress_ptr cinfo)
119 {
120   ms_stream_destination_mgr *dest = (ms_stream_destination_mgr*) cinfo->dest;
121   msIO_fwrite(dest->mgr.data, OUTPUT_BUF_SIZE, 1, dest->stream);
122   dest->mgr.pub.next_output_byte = dest->mgr.data;
123   dest->mgr.pub.free_in_buffer = OUTPUT_BUF_SIZE;
124   return TRUE;
125 }
126 
127 static
jpeg_buffer_empty_output_buffer(j_compress_ptr cinfo)128 boolean jpeg_buffer_empty_output_buffer (j_compress_ptr cinfo)
129 {
130   ms_buffer_destination_mgr *dest = (ms_buffer_destination_mgr*) cinfo->dest;
131   msBufferAppend(dest->buffer, dest->mgr.data, OUTPUT_BUF_SIZE);
132   dest->mgr.pub.next_output_byte = dest->mgr.data;
133   dest->mgr.pub.free_in_buffer = OUTPUT_BUF_SIZE;
134   return TRUE;
135 }
136 
msJPEGErrorExit(j_common_ptr cinfo)137 static void msJPEGErrorExit(j_common_ptr cinfo)
138 {
139     jmp_buf* pJmpBuffer = (jmp_buf* ) cinfo->client_data;
140     char buffer[JMSG_LENGTH_MAX];
141 
142     /* Create the message */
143     (*cinfo->err->format_message) (cinfo, buffer);
144 
145     msSetError(MS_MISCERR,"libjpeg: %s","jpeg_ErrorExit()", buffer);
146 
147     /* Return control to the setjmp point */
148     longjmp(*pJmpBuffer, 1);
149 }
150 
saveAsJPEG(mapObj * map,rasterBufferObj * rb,streamInfo * info,outputFormatObj * format)151 int saveAsJPEG(mapObj *map, rasterBufferObj *rb, streamInfo *info,
152                outputFormatObj *format)
153 {
154   struct jpeg_compress_struct cinfo;
155   struct jpeg_error_mgr jerr;
156   int quality;
157   const char* pszOptimized;
158   int optimized;
159   int arithmetic;
160   ms_destination_mgr *dest;
161   JSAMPLE *rowdata = NULL;
162   unsigned int row;
163   jmp_buf setjmp_buffer;
164 
165   quality = atoi(msGetOutputFormatOption( format, "QUALITY", "75"));
166   pszOptimized = msGetOutputFormatOption( format, "OPTIMIZED", "YES");
167   optimized = EQUAL(pszOptimized, "YES") || EQUAL(pszOptimized, "ON") ||
168               EQUAL(pszOptimized, "TRUE");
169   arithmetic = EQUAL(pszOptimized, "ARITHMETIC");
170 
171   if (setjmp(setjmp_buffer))
172   {
173      jpeg_destroy_compress(&cinfo);
174      free(rowdata);
175      return MS_FAILURE;
176   }
177 
178   cinfo.err = jpeg_std_error(&jerr);
179   jerr.error_exit = msJPEGErrorExit;
180   cinfo.client_data = (void *) &(setjmp_buffer);
181   jpeg_create_compress(&cinfo);
182 
183   if (cinfo.dest == NULL) {
184     if(info->fp) {
185       cinfo.dest = (struct jpeg_destination_mgr *)
186                    (*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT,
187                                               sizeof (ms_stream_destination_mgr));
188       ((ms_stream_destination_mgr*)cinfo.dest)->mgr.pub.empty_output_buffer = jpeg_stream_empty_output_buffer;
189       ((ms_stream_destination_mgr*)cinfo.dest)->mgr.pub.term_destination = jpeg_stream_term_destination;
190       ((ms_stream_destination_mgr*)cinfo.dest)->stream = info->fp;
191     } else {
192 
193       cinfo.dest = (struct jpeg_destination_mgr *)
194                    (*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT,
195                                               sizeof (ms_buffer_destination_mgr));
196       ((ms_buffer_destination_mgr*)cinfo.dest)->mgr.pub.empty_output_buffer = jpeg_buffer_empty_output_buffer;
197       ((ms_buffer_destination_mgr*)cinfo.dest)->mgr.pub.term_destination = jpeg_buffer_term_destination;
198       ((ms_buffer_destination_mgr*)cinfo.dest)->buffer = info->buffer;
199     }
200   }
201   dest = (ms_destination_mgr*) cinfo.dest;
202   dest->pub.init_destination = jpeg_init_destination;
203 
204   cinfo.image_width = rb->width;
205   cinfo.image_height = rb->height;
206   cinfo.input_components = 3;
207   cinfo.in_color_space = JCS_RGB;
208   jpeg_set_defaults(&cinfo);
209   jpeg_set_quality(&cinfo, quality, TRUE);
210   if( arithmetic )
211     cinfo.arith_code = TRUE;
212   else if( optimized )
213     cinfo.optimize_coding = TRUE;
214 
215   if( arithmetic || optimized ) {
216     /* libjpeg turbo 1.5.2 honours max_memory_to_use, but has no backing */
217     /* store implementation, so better not set max_memory_to_use ourselves. */
218     /* See https://github.com/libjpeg-turbo/libjpeg-turbo/issues/162 */
219     if( cinfo.mem->max_memory_to_use > 0 ) {
220       if (map == NULL || msGetConfigOption(map, "JPEGMEM") == NULL) {
221         /* If the user doesn't provide a value for JPEGMEM, we want to be sure */
222         /* that at least the image size will be used before creating the temporary file */
223         cinfo.mem->max_memory_to_use =
224           MS_MAX(cinfo.mem->max_memory_to_use, cinfo.input_components * rb->width * rb->height);
225       }
226     }
227   }
228 
229   jpeg_start_compress(&cinfo, TRUE);
230   rowdata = (JSAMPLE*)malloc(rb->width*cinfo.input_components*sizeof(JSAMPLE));
231 
232   for(row=0; row<rb->height; row++) {
233     JSAMPLE *pixptr = rowdata;
234     int col;
235     unsigned char *r,*g,*b;
236     r=rb->data.rgba.r+row*rb->data.rgba.row_step;
237     g=rb->data.rgba.g+row*rb->data.rgba.row_step;
238     b=rb->data.rgba.b+row*rb->data.rgba.row_step;
239     for(col=0; col<rb->width; col++) {
240       *(pixptr++) = *r;
241       *(pixptr++) = *g;
242       *(pixptr++) = *b;
243       r+=rb->data.rgba.pixel_step;
244       g+=rb->data.rgba.pixel_step;
245       b+=rb->data.rgba.pixel_step;
246     }
247     (void) jpeg_write_scanlines(&cinfo, &rowdata, 1);
248   }
249 
250   /* Step 6: Finish compression */
251 
252   jpeg_finish_compress(&cinfo);
253   jpeg_destroy_compress(&cinfo);
254   free(rowdata);
255   return MS_SUCCESS;
256 }
257 
258 /*
259  * sort a given list of rgba entries so that all the opaque pixels are at the end
260  */
remapPaletteForPNG(rasterBufferObj * rb,rgbPixel * rgb,unsigned char * a,int * num_a)261 int remapPaletteForPNG(rasterBufferObj *rb, rgbPixel *rgb, unsigned char *a, int *num_a)
262 {
263   int bot_idx, top_idx, x;
264   int remap[256];
265   unsigned int maxval = rb->data.palette.scaling_maxval;
266 
267   assert(rb->type == MS_BUFFER_BYTE_PALETTE);
268 
269   /*
270   ** remap the palette colors so that all entries with
271   ** the maximal alpha value (i.e., fully opaque) are at the end and can
272   ** therefore be omitted from the tRNS chunk.  Note that the ordering of
273   ** opaque entries is reversed from how they were previously arranged
274   ** --not that this should matter to anyone.
275   */
276 
277   for (top_idx = rb->data.palette.num_entries-1, bot_idx = x = 0;  x < rb->data.palette.num_entries;  ++x) {
278     if (rb->data.palette.palette[x].a == maxval)
279       remap[x] = top_idx--;
280     else
281       remap[x] = bot_idx++;
282   }
283   /* sanity check:  top and bottom indices should have just crossed paths */
284   if (bot_idx != top_idx + 1) {
285     msSetError(MS_MISCERR,"quantization sanity check failed","createPNGPalette()");
286     return MS_FAILURE;
287   }
288 
289   *num_a = bot_idx;
290 
291   for(x=0; x<rb->width*rb->height; x++)
292     rb->data.palette.pixels[x] = remap[rb->data.palette.pixels[x]];
293 
294 
295   for (x = 0; x < rb->data.palette.num_entries; ++x) {
296     if(maxval == 255) {
297       a[remap[x]] = rb->data.palette.palette[x].a;
298       rgb[remap[x]].r = rb->data.palette.palette[x].r;
299       rgb[remap[x]].g = rb->data.palette.palette[x].g;
300       rgb[remap[x]].b = rb->data.palette.palette[x].b;
301     } else {
302       /* rescale palette */
303       rgb[remap[x]].r = (rb->data.palette.palette[x].r * 255 + (maxval >> 1)) / maxval;
304       rgb[remap[x]].g = (rb->data.palette.palette[x].g * 255 + (maxval >> 1)) / maxval;
305       rgb[remap[x]].b = (rb->data.palette.palette[x].b * 255 + (maxval >> 1)) / maxval;
306       a[remap[x]] = (rb->data.palette.palette[x].a * 255 + (maxval >> 1)) / maxval;
307     }
308     if(a[remap[x]] != 255) {
309       /* un-premultiply pixels */
310       double da = 255.0/a[remap[x]];
311       rgb[remap[x]].r *=  da;
312       rgb[remap[x]].g *=  da;
313       rgb[remap[x]].b *=  da;
314     }
315   }
316 
317   return MS_SUCCESS;
318 }
319 
savePalettePNG(rasterBufferObj * rb,streamInfo * info,int compression)320 int savePalettePNG(rasterBufferObj *rb, streamInfo *info, int compression)
321 {
322   png_infop info_ptr;
323   rgbPixel rgb[256];
324   unsigned char a[256];
325   int num_a;
326   int row,sample_depth;
327   png_structp png_ptr = png_create_write_struct(
328                           PNG_LIBPNG_VER_STRING, NULL,NULL,NULL);
329 
330   assert(rb->type == MS_BUFFER_BYTE_PALETTE);
331 
332   if (!png_ptr)
333     return (MS_FAILURE);
334 
335   png_set_compression_level(png_ptr, compression);
336   png_set_filter (png_ptr,0, PNG_FILTER_NONE);
337 
338   info_ptr = png_create_info_struct(png_ptr);
339   if (!info_ptr) {
340     png_destroy_write_struct(&png_ptr,
341                              (png_infopp)NULL);
342     return (MS_FAILURE);
343   }
344 
345   if (setjmp(png_jmpbuf(png_ptr))) {
346     png_destroy_write_struct(&png_ptr, &info_ptr);
347     return (MS_FAILURE);
348   }
349   if(info->fp)
350     png_set_write_fn(png_ptr,info, png_write_data_to_stream, png_flush_data);
351   else
352     png_set_write_fn(png_ptr,info, png_write_data_to_buffer, png_flush_data);
353 
354 
355   if (rb->data.palette.num_entries <= 2)
356     sample_depth = 1;
357   else if (rb->data.palette.num_entries <= 4)
358     sample_depth = 2;
359   else if (rb->data.palette.num_entries <= 16)
360     sample_depth = 4;
361   else
362     sample_depth = 8;
363 
364   png_set_IHDR(png_ptr, info_ptr, rb->width, rb->height,
365                sample_depth, PNG_COLOR_TYPE_PALETTE,
366                0, PNG_COMPRESSION_TYPE_DEFAULT,
367                PNG_FILTER_TYPE_DEFAULT);
368 
369   remapPaletteForPNG(rb,rgb,a,&num_a);
370 
371   png_set_PLTE(png_ptr, info_ptr, (png_colorp)(rgb),rb->data.palette.num_entries);
372   if(num_a)
373     png_set_tRNS(png_ptr, info_ptr, a,num_a, NULL);
374 
375   png_write_info(png_ptr, info_ptr);
376   png_set_packing(png_ptr);
377 
378   for(row=0; row<rb->height; row++) {
379     unsigned char *rowptr = &(rb->data.palette.pixels[row*rb->width]);
380     png_write_row(png_ptr, rowptr);
381   }
382   png_write_end(png_ptr, info_ptr);
383   png_destroy_write_struct(&png_ptr, &info_ptr);
384   return MS_SUCCESS;
385 }
386 
readPalette(const char * palette,rgbaPixel * entries,unsigned int * nEntries,int useAlpha)387 int readPalette(const char *palette, rgbaPixel *entries, unsigned int *nEntries, int useAlpha)
388 {
389   FILE *stream = NULL;
390   char buffer[MS_BUFFER_LENGTH];
391   *nEntries = 0;
392 
393   stream = fopen(palette, "r");
394   if(!stream) {
395     msSetError(MS_IOERR, "Error opening palette file %s.", "readPalette()", palette);
396     return MS_FAILURE;
397   }
398 
399   while(fgets(buffer, MS_BUFFER_LENGTH, stream) && *nEntries<256) {
400     int r,g,b,a = 0;
401     /* while there are colors to load */
402     if(buffer[0] == '#' || buffer[0] == '\n' || buffer[0] == '\r')
403       continue; /* skip comments and blank lines */
404     if(!useAlpha) {
405       if(3 != sscanf(buffer,"%d,%d,%d\n",&r,&g,&b)) {
406         fclose(stream);
407         msSetError(MS_MISCERR,"failed to parse color %d r,g,b triplet in line \"%s\" from file %s","readPalette()",*nEntries+1,buffer,palette);
408         return MS_FAILURE;
409       }
410     } else {
411       if(4 != sscanf(buffer,"%d,%d,%d,%d\n",&r,&g,&b,&a)) {
412         fclose(stream);
413         msSetError(MS_MISCERR,"failed to parse color %d r,g,b,a quadruplet in line \"%s\" from file %s","readPalette()",*nEntries+1,buffer,palette);
414         return MS_FAILURE;
415       }
416     }
417     if(useAlpha && a != 255) {
418       double da = a/255.0;
419       entries[*nEntries].r = r * da;
420       entries[*nEntries].g = g * da;
421       entries[*nEntries].b = b * da;
422       entries[*nEntries].a = a;
423     } else {
424       entries[*nEntries].r = r;
425       entries[*nEntries].g = g;
426       entries[*nEntries].b = b;
427       entries[*nEntries].a = 255;
428     }
429     (*nEntries)++;
430   }
431   fclose(stream);
432   return MS_SUCCESS;
433 }
434 
saveAsPNG(mapObj * map,rasterBufferObj * rb,streamInfo * info,outputFormatObj * format)435 int saveAsPNG(mapObj *map,rasterBufferObj *rb, streamInfo *info, outputFormatObj *format)
436 {
437   int force_pc256 = MS_FALSE;
438   int force_palette = MS_FALSE;
439 
440   int ret = MS_FAILURE;
441 
442   const char *force_string,*zlib_compression;
443   int compression = -1;
444 
445   zlib_compression = msGetOutputFormatOption( format, "COMPRESSION", NULL);
446   if(zlib_compression && *zlib_compression) {
447     char *endptr;
448     compression = strtol(zlib_compression,&endptr,10);
449     if(*endptr || compression<-1 || compression>9) {
450       msSetError(MS_MISCERR,"failed to parse FORMATOPTION \"COMPRESSION=%s\", expecting integer from 0 to 9.","saveAsPNG()",zlib_compression);
451       return MS_FAILURE;
452     }
453   }
454 
455 
456   force_string = msGetOutputFormatOption( format, "QUANTIZE_FORCE", NULL );
457   if( force_string && (strcasecmp(force_string,"on") == 0  || strcasecmp(force_string,"yes") == 0 || strcasecmp(force_string,"true") == 0) )
458     force_pc256 = MS_TRUE;
459 
460   force_string = msGetOutputFormatOption( format, "PALETTE_FORCE", NULL );
461   if( force_string && (strcasecmp(force_string,"on") == 0  || strcasecmp(force_string,"yes") == 0 || strcasecmp(force_string,"true") == 0) )
462     force_palette = MS_TRUE;
463 
464   if(force_pc256 || force_palette) {
465     rasterBufferObj qrb;
466     rgbaPixel palette[256], paletteGiven[256];
467     unsigned int numPaletteGivenEntries;
468     memset(&qrb,0,sizeof(rasterBufferObj));
469     qrb.type = MS_BUFFER_BYTE_PALETTE;
470     qrb.width = rb->width;
471     qrb.height = rb->height;
472     qrb.data.palette.pixels = (unsigned char*)malloc(qrb.width*qrb.height*sizeof(unsigned char));
473     qrb.data.palette.scaling_maxval = 255;
474     if(force_pc256) {
475       qrb.data.palette.palette = palette;
476       qrb.data.palette.num_entries = atoi(msGetOutputFormatOption( format, "QUANTIZE_COLORS", "256"));
477       ret = msQuantizeRasterBuffer(rb,&(qrb.data.palette.num_entries),qrb.data.palette.palette,
478                                    NULL, 0,
479                                    &qrb.data.palette.scaling_maxval);
480     } else {
481       int colorsWanted = atoi(msGetOutputFormatOption( format, "QUANTIZE_COLORS", "0"));
482       const char *palettePath = msGetOutputFormatOption( format, "PALETTE", "palette.txt");
483       char szPath[MS_MAXPATHLEN];
484       if(map) {
485         msBuildPath(szPath, map->mappath, palettePath);
486         palettePath = szPath;
487       }
488       if(readPalette(palettePath,paletteGiven,&numPaletteGivenEntries,format->transparent) != MS_SUCCESS) {
489         return MS_FAILURE;
490       }
491 
492       if(numPaletteGivenEntries == 256 || colorsWanted == 0) {
493         qrb.data.palette.palette = paletteGiven;
494         qrb.data.palette.num_entries = numPaletteGivenEntries;
495         ret = MS_SUCCESS;
496 
497         /* we have a full palette and don't want an additional quantization step */
498       } else {
499         /* quantize the image, and mix our colours in the resulting palette */
500         qrb.data.palette.palette = palette;
501         qrb.data.palette.num_entries = MS_MAX(colorsWanted,numPaletteGivenEntries);
502         ret = msQuantizeRasterBuffer(rb,&(qrb.data.palette.num_entries),qrb.data.palette.palette,
503                                      paletteGiven,numPaletteGivenEntries,
504                                      &qrb.data.palette.scaling_maxval);
505       }
506     }
507     if(ret != MS_FAILURE) {
508       ret = msClassifyRasterBuffer(rb,&qrb);
509       ret = savePalettePNG(&qrb,info,compression);
510     }
511     msFree(qrb.data.palette.pixels);
512     return ret;
513   } else if(rb->type == MS_BUFFER_BYTE_RGBA) {
514     png_infop info_ptr;
515     int color_type;
516     int row;
517     unsigned int *rowdata;
518     png_structp png_ptr = png_create_write_struct(
519                             PNG_LIBPNG_VER_STRING, NULL,NULL,NULL);
520 
521     if (!png_ptr)
522       return (MS_FAILURE);
523 
524     png_set_compression_level(png_ptr, compression);
525     png_set_filter (png_ptr,0, PNG_FILTER_NONE);
526 
527     info_ptr = png_create_info_struct(png_ptr);
528     if (!info_ptr) {
529       png_destroy_write_struct(&png_ptr,
530                                (png_infopp)NULL);
531       return (MS_FAILURE);
532     }
533 
534     if (setjmp(png_jmpbuf(png_ptr))) {
535       png_destroy_write_struct(&png_ptr, &info_ptr);
536       return (MS_FAILURE);
537     }
538     if(info->fp)
539       png_set_write_fn(png_ptr,info, png_write_data_to_stream, png_flush_data);
540     else
541       png_set_write_fn(png_ptr,info, png_write_data_to_buffer, png_flush_data);
542 
543     if(rb->data.rgba.a)
544       color_type = PNG_COLOR_TYPE_RGB_ALPHA;
545     else
546       color_type = PNG_COLOR_TYPE_RGB;
547 
548     png_set_IHDR(png_ptr, info_ptr, rb->width, rb->height,
549                  8, color_type, PNG_INTERLACE_NONE,
550                  PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
551 
552     png_write_info(png_ptr, info_ptr);
553 
554     if(!rb->data.rgba.a && rb->data.rgba.pixel_step==4)
555       png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
556 
557     rowdata = (unsigned int*)malloc(rb->width*sizeof(unsigned int));
558     for(row=0; row<rb->height; row++) {
559       unsigned int *pixptr = rowdata;
560       int col;
561       unsigned char *a,*r,*g,*b;
562       r=rb->data.rgba.r+row*rb->data.rgba.row_step;
563       g=rb->data.rgba.g+row*rb->data.rgba.row_step;
564       b=rb->data.rgba.b+row*rb->data.rgba.row_step;
565       if(rb->data.rgba.a) {
566         a=rb->data.rgba.a+row*rb->data.rgba.row_step;
567         for(col=0; col<rb->width; col++) {
568           if(*a) {
569             double da = *a/255.0;
570             unsigned char *pix = (unsigned char*)pixptr;
571             pix[0] = *r/da;
572             pix[1] = *g/da;
573             pix[2] = *b/da;
574             pix[3] = *a;
575           } else {
576             *pixptr=0;
577           }
578           pixptr++;
579           a+=rb->data.rgba.pixel_step;
580           r+=rb->data.rgba.pixel_step;
581           g+=rb->data.rgba.pixel_step;
582           b+=rb->data.rgba.pixel_step;
583         }
584       } else {
585         for(col=0; col<rb->width; col++) {
586           unsigned char *pix = (unsigned char*)pixptr;
587           pix[0] = *r;
588           pix[1] = *g;
589           pix[2] = *b;
590           pixptr++;
591           r+=rb->data.rgba.pixel_step;
592           g+=rb->data.rgba.pixel_step;
593           b+=rb->data.rgba.pixel_step;
594         }
595       }
596 
597       png_write_row(png_ptr,(png_bytep)rowdata);
598 
599     }
600     png_write_end(png_ptr, info_ptr);
601     free(rowdata);
602     png_destroy_write_struct(&png_ptr, &info_ptr);
603     return MS_SUCCESS;
604   } else {
605     msSetError(MS_MISCERR,"Unknown buffer type","saveAsPNG()");
606     return MS_FAILURE;
607   }
608 }
609 
610 /* For platforms with incomplete ANSI defines. Fortunately,
611    SEEK_SET is defined to be zero by the standard. */
612 
613 #ifndef SEEK_SET
614 #define SEEK_SET 0
615 #endif /* SEEK_SET */
616 
617 
readPNG(char * path,rasterBufferObj * rb)618 int readPNG(char *path, rasterBufferObj *rb)
619 {
620   png_uint_32 width,height;
621   unsigned char *a,*r,*g,*b;
622   int bit_depth,color_type,i;
623   unsigned char **row_pointers;
624   png_structp png_ptr = NULL;
625   png_infop info_ptr = NULL;
626 
627   FILE *stream = fopen(path,"rb");
628   if(!stream)
629     return MS_FAILURE;
630 
631   /* could pass pointers to user-defined error handlers instead of NULLs: */
632   png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
633   if (!png_ptr) {
634     fclose(stream);
635     return MS_FAILURE;   /* out of memory */
636   }
637 
638   info_ptr = png_create_info_struct(png_ptr);
639   if (!info_ptr) {
640     png_destroy_read_struct(&png_ptr, NULL, NULL);
641     fclose(stream);
642     return MS_FAILURE;   /* out of memory */
643   }
644 
645   if(setjmp(png_jmpbuf(png_ptr))) {
646     png_destroy_read_struct(&png_ptr,&info_ptr,NULL);
647     fclose(stream);
648     return MS_FAILURE;
649   }
650 
651   png_init_io(png_ptr,stream);
652   png_read_info(png_ptr,info_ptr);
653   png_get_IHDR(png_ptr, info_ptr, &width, &height,
654                &bit_depth, &color_type,
655                NULL,NULL,NULL);
656 
657   rb->width = width;
658   rb->height = height;
659   rb->type = MS_BUFFER_BYTE_RGBA;
660   rb->data.rgba.pixels = (unsigned char*)malloc(width*height*4*sizeof(unsigned char));
661   row_pointers = (unsigned char**)malloc(height*sizeof(unsigned char*));
662   rb->data.rgba.pixel_step=4;
663   rb->data.rgba.row_step = width*4;
664   b = rb->data.rgba.b = &rb->data.rgba.pixels[0];
665   g = rb->data.rgba.g = &rb->data.rgba.pixels[1];
666   r = rb->data.rgba.r = &rb->data.rgba.pixels[2];
667   a = rb->data.rgba.a = &rb->data.rgba.pixels[3];
668 
669   for(i=0; i<height; i++) {
670     row_pointers[i] = &(rb->data.rgba.pixels[i*rb->data.rgba.row_step]);
671   }
672 
673   if (color_type == PNG_COLOR_TYPE_PALETTE)
674     /* expand palette images to RGB */
675     png_set_expand(png_ptr);
676   if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
677     /* expand low bit-depth grayscale to 8bits */
678     png_set_expand(png_ptr);
679   if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
680     /* expand transparency chunks to full alpha */
681     png_set_expand(png_ptr);
682   if (bit_depth == 16)
683     /* scale 16bits down to 8 */
684     png_set_strip_16(png_ptr);
685   if (color_type == PNG_COLOR_TYPE_GRAY ||
686       color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
687     /* convert grayscale to rgba */
688     png_set_gray_to_rgb(png_ptr);
689 
690   png_set_bgr(png_ptr);
691   if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY ||
692       color_type == PNG_COLOR_TYPE_PALETTE)
693     png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER);
694 
695   png_read_update_info(png_ptr, info_ptr);
696   assert(png_get_rowbytes(png_ptr, info_ptr) == rb->data.rgba.row_step);
697 
698   png_read_image(png_ptr, row_pointers);
699   free(row_pointers);
700   row_pointers=NULL;
701   png_read_end(png_ptr,NULL);
702   png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
703 
704   /*premultiply data*/
705   for(i=0; i<width*height; i++) {
706     if(*a < 255) {
707       if(*a == 0) {
708         *r=*g=*b=0;
709       } else {
710         *r = (*r * *a + 255) >> 8;
711         *g = (*g * *a + 255) >> 8;
712         *b = (*b * *a + 255) >> 8;
713       }
714     }
715     a+=4;
716     b+=4;
717     g+=4;
718     r+=4;
719   }
720 
721   fclose(stream);
722   return MS_SUCCESS;
723 
724 }
725 
msSaveRasterBuffer(mapObj * map,rasterBufferObj * rb,FILE * stream,outputFormatObj * format)726 int msSaveRasterBuffer(mapObj *map, rasterBufferObj *rb, FILE *stream,
727                        outputFormatObj *format)
728 {
729   if(strcasestr(format->driver,"/png")) {
730     streamInfo info;
731     info.fp = stream;
732     info.buffer = NULL;
733 
734     return saveAsPNG(map, rb,&info,format);
735   } else if(strcasestr(format->driver,"/jpeg")) {
736     streamInfo info;
737     info.fp = stream;
738     info.buffer=NULL;
739 
740     return saveAsJPEG(map, rb,&info,format);
741   } else {
742     msSetError(MS_MISCERR,"unsupported image format\n", "msSaveRasterBuffer()");
743     return MS_FAILURE;
744   }
745 }
746 
msSaveRasterBufferToBuffer(rasterBufferObj * data,bufferObj * buffer,outputFormatObj * format)747 int msSaveRasterBufferToBuffer(rasterBufferObj *data, bufferObj *buffer,
748                                outputFormatObj *format)
749 {
750   if(strcasestr(format->driver,"/png")) {
751     streamInfo info;
752     info.fp = NULL;
753     info.buffer = buffer;
754     return saveAsPNG(NULL, data,&info,format);
755   } else if(strcasestr(format->driver,"/jpeg")) {
756     streamInfo info;
757     info.fp = NULL;
758     info.buffer=buffer;
759     return saveAsJPEG(NULL, data,&info,format);
760   } else {
761     msSetError(MS_MISCERR,"unsupported image format\n", "msSaveRasterBuffer()");
762     return MS_FAILURE;
763   }
764 }
765 
766 #ifdef USE_GIF
767 #if defined GIFLIB_MAJOR && GIFLIB_MAJOR >= 5
gif_error_msg(int code)768 static char const *gif_error_msg(int code)
769 #else
770 static char const *gif_error_msg()
771 #endif
772 {
773   static char msg[80];
774 
775 #if (!defined GIFLIB_MAJOR) || (GIFLIB_MAJOR < 5)
776   int code = GifLastError();
777 #endif
778   switch (code) {
779     case E_GIF_ERR_OPEN_FAILED: /* should not see this */
780       return "Failed to open given file";
781 
782     case E_GIF_ERR_WRITE_FAILED:
783       return "Write failed";
784 
785     case E_GIF_ERR_HAS_SCRN_DSCR: /* should not see this */
786       return "Screen descriptor already passed to giflib";
787 
788     case E_GIF_ERR_HAS_IMAG_DSCR: /* should not see this */
789       return "Image descriptor already passed to giflib";
790 
791     case E_GIF_ERR_NO_COLOR_MAP: /* should not see this */
792       return "Neither global nor local color map set";
793 
794     case E_GIF_ERR_DATA_TOO_BIG: /* should not see this */
795       return "Too much pixel data passed to giflib";
796 
797     case E_GIF_ERR_NOT_ENOUGH_MEM:
798       return "Out of memory";
799 
800     case E_GIF_ERR_DISK_IS_FULL:
801       return "Disk is full";
802 
803     case E_GIF_ERR_CLOSE_FAILED: /* should not see this */
804       return "File close failed";
805 
806     case E_GIF_ERR_NOT_WRITEABLE: /* should not see this */
807       return "File not writable";
808 
809     case D_GIF_ERR_OPEN_FAILED:
810       return "Failed to open file";
811 
812     case D_GIF_ERR_READ_FAILED:
813       return "Failed to read from file";
814 
815     case D_GIF_ERR_NOT_GIF_FILE:
816       return "File is not a GIF file";
817 
818     case D_GIF_ERR_NO_SCRN_DSCR:
819       return "No screen descriptor detected - invalid file";
820 
821     case D_GIF_ERR_NO_IMAG_DSCR:
822       return "No image descriptor detected - invalid file";
823 
824     case D_GIF_ERR_NO_COLOR_MAP:
825       return "No global or local color map found";
826 
827     case D_GIF_ERR_WRONG_RECORD:
828       return "Wrong record type detected - invalid file?";
829 
830     case D_GIF_ERR_DATA_TOO_BIG:
831       return "Data in file too big for image";
832 
833     case D_GIF_ERR_NOT_ENOUGH_MEM:
834       return "Out of memory";
835 
836     case D_GIF_ERR_CLOSE_FAILED:
837       return "Close failed";
838 
839     case D_GIF_ERR_NOT_READABLE:
840       return "File not opened for read";
841 
842     case D_GIF_ERR_IMAGE_DEFECT:
843       return "Defective image";
844 
845     case D_GIF_ERR_EOF_TOO_SOON:
846       return "Unexpected EOF - invalid file";
847 
848     default:
849       sprintf(msg, "Unknown giflib error code %d", code);
850       return msg;
851   }
852 }
853 
854 
855 /* not fully implemented and tested */
856 /* missing: set the first pointer to a,r,g,b */
readGIF(char * path,rasterBufferObj * rb)857 int readGIF(char *path, rasterBufferObj *rb)
858 {
859   int i, j, codeSize, extCode, firstImageRead = MS_FALSE;
860   unsigned char *r,*g,*b,*a;
861   int transIdx = -1;
862   GifFileType *image;
863   GifPixelType *line;
864   GifRecordType recordType;
865   GifByteType *codeBlock, *extension;
866   ColorMapObject *cmap;
867   int interlacedOffsets[] = {0,4,2,1};
868   int interlacedJumps[] = {8,8,4,2};
869 #if defined GIFLIB_MAJOR && GIFLIB_MAJOR >= 5
870   int errcode;
871 #endif
872 
873 
874   rb->type = MS_BUFFER_BYTE_RGBA;
875 #if defined GIFLIB_MAJOR && GIFLIB_MAJOR >= 5
876   image =  DGifOpenFileName(path, &errcode);
877   if (image == NULL) {
878     msSetError(MS_MISCERR,"failed to load gif image: %s","readGIF()", gif_error_msg(errcode));
879     return MS_FAILURE;
880   }
881 #else
882   image =  DGifOpenFileName(path);
883   if (image == NULL) {
884     msSetError(MS_MISCERR,"failed to load gif image: %s","readGIF()", gif_error_msg());
885     return MS_FAILURE;
886   }
887 #endif
888   rb->width = image->SWidth;
889   rb->height = image->SHeight;
890   rb->data.rgba.row_step = rb->width * 4;
891   rb->data.rgba.pixel_step = 4;
892   rb->data.rgba.pixels = (unsigned char*)malloc(rb->width*rb->height*4*sizeof(unsigned char));
893   b = rb->data.rgba.b = &rb->data.rgba.pixels[0];
894   g = rb->data.rgba.g = &rb->data.rgba.pixels[1];
895   r = rb->data.rgba.r = &rb->data.rgba.pixels[2];
896   a = rb->data.rgba.a = &rb->data.rgba.pixels[3];
897 
898   cmap = (image->Image.ColorMap)?image->Image.ColorMap:image->SColorMap;
899   for(i=0; i<rb->width*rb->height; i++) {
900     *a = 255;
901     *r = cmap->Colors[image->SBackGroundColor].Red;
902     *g = cmap->Colors[image->SBackGroundColor].Green;
903     *b = cmap->Colors[image->SBackGroundColor].Blue;
904     a+=rb->data.rgba.pixel_step;
905     r+=rb->data.rgba.pixel_step;
906     g+=rb->data.rgba.pixel_step;
907     b+=rb->data.rgba.pixel_step;
908   }
909 
910   do {
911     if (DGifGetRecordType(image, &recordType) == GIF_ERROR) {
912 #if defined GIFLIB_MAJOR && GIFLIB_MAJOR >= 5
913       msSetError(MS_MISCERR,"corrupted gif image?: %s","readGIF()", gif_error_msg(image->Error));
914 #else
915       msSetError(MS_MISCERR,"corrupted gif image?: %s","readGIF()", gif_error_msg());
916 #endif
917       return MS_FAILURE;
918     }
919 
920     switch (recordType) {
921       case SCREEN_DESC_RECORD_TYPE:
922         DGifGetScreenDesc(image);
923         break;
924       case IMAGE_DESC_RECORD_TYPE:
925         if (DGifGetImageDesc(image) == GIF_ERROR) {
926 #if defined GIFLIB_MAJOR && GIFLIB_MAJOR >= 5
927           msSetError(MS_MISCERR,"corrupted gif image?: %s","readGIF()", gif_error_msg(image->Error));
928 #else
929           msSetError(MS_MISCERR,"corrupted gif image?: %s","readGIF()", gif_error_msg());
930 #endif
931           return MS_FAILURE;
932         }
933         if (!firstImageRead) {
934 
935           int row = image->Image.Top;
936           int col = image->Image.Left;
937           int width = image->Image.Width;
938           int height = image->Image.Height;
939 
940           /* sanity check: */
941           if(col+width>rb->width || row+height>rb->height) {
942             msSetError(MS_MISCERR,"corrupted gif image, img size exceeds screen size","readGIF()");
943             return MS_FAILURE;
944           }
945 
946           line = (GifPixelType *) malloc(width * sizeof(GifPixelType));
947           if(image->Image.Interlace) {
948             int count;
949             for(count=0; count<4; count++) {
950               for(i=row+interlacedOffsets[count]; i<row+height; i+=interlacedJumps[count]) {
951                 int offset = i * rb->data.rgba.row_step + col * rb->data.rgba.pixel_step;
952                 a = rb->data.rgba.a + offset;
953                 r = rb->data.rgba.r + offset;
954                 g = rb->data.rgba.g + offset;
955                 b = rb->data.rgba.b + offset;
956                 if (DGifGetLine(image, line, width) == GIF_ERROR) {
957 #if defined GIFLIB_MAJOR && GIFLIB_MAJOR >= 5
958                   msSetError(MS_MISCERR,"corrupted gif image?: %s","readGIF()",gif_error_msg(image->Error));
959 #else
960                   msSetError(MS_MISCERR,"corrupted gif image?: %s","readGIF()",gif_error_msg());
961 #endif
962                   return MS_FAILURE;
963                 }
964 
965                 for(j=0; j<width; j++) {
966                   GifPixelType pix = line[j];
967                   if(transIdx == -1 || pix != transIdx) {
968                     *a = 255;
969                     *r = cmap->Colors[pix].Red;
970                     *g = cmap->Colors[pix].Green;
971                     *b = cmap->Colors[pix].Blue;
972                   } else {
973                     *a = *r = *g = *b = 0;
974                   }
975                   a+=rb->data.rgba.pixel_step;
976                   r+=rb->data.rgba.pixel_step;
977                   g+=rb->data.rgba.pixel_step;
978                   b+=rb->data.rgba.pixel_step;
979                 }
980               }
981             }
982           } else {
983             for (i = 0; i < height; i++) {
984               int offset = i * rb->data.rgba.row_step + col * rb->data.rgba.pixel_step;
985               a = rb->data.rgba.a + offset;
986               r = rb->data.rgba.r + offset;
987               g = rb->data.rgba.g + offset;
988               b = rb->data.rgba.b + offset;
989               if (DGifGetLine(image, line, width) == GIF_ERROR) {
990 #if defined GIFLIB_MAJOR && GIFLIB_MAJOR >= 5
991                 msSetError(MS_MISCERR,"corrupted gif image?: %s","readGIF()",gif_error_msg(image->Error));
992 #else
993                 msSetError(MS_MISCERR,"corrupted gif image?: %s","readGIF()",gif_error_msg());
994 #endif
995                 return MS_FAILURE;
996               }
997               for(j=0; j<width; j++) {
998                 GifPixelType pix = line[j];
999                 if(transIdx == -1 || pix != transIdx) {
1000                   *a = 255;
1001                   *r = cmap->Colors[pix].Red;
1002                   *g = cmap->Colors[pix].Green;
1003                   *b = cmap->Colors[pix].Blue;
1004                 } else {
1005                   *a = *r = *g = *b = 0;
1006                 }
1007                 a+=rb->data.rgba.pixel_step;
1008                 r+=rb->data.rgba.pixel_step;
1009                 g+=rb->data.rgba.pixel_step;
1010                 b+=rb->data.rgba.pixel_step;
1011               }
1012             }
1013           }
1014           free((char *) line);
1015           firstImageRead = MS_TRUE;
1016         } else {
1017           /* Skip all next images */
1018           if (DGifGetCode(image, &codeSize, &codeBlock) == GIF_ERROR) {
1019 #if defined GIFLIB_MAJOR && GIFLIB_MAJOR >= 5
1020             msSetError(MS_MISCERR,"corrupted gif image?: %s","readGIF()", gif_error_msg(image->Error));
1021 #else
1022             msSetError(MS_MISCERR,"corrupted gif image?: %s","readGIF()", gif_error_msg());
1023 #endif
1024             return MS_FAILURE;
1025           }
1026           while (codeBlock != NULL) {
1027             if (DGifGetCodeNext(image, &codeBlock) == GIF_ERROR) {
1028 #if defined GIFLIB_MAJOR && GIFLIB_MAJOR >= 5
1029               msSetError(MS_MISCERR,"corrupted gif image?: %s","readGIF()", gif_error_msg(image->Error));
1030 #else
1031               msSetError(MS_MISCERR,"corrupted gif image?: %s","readGIF()", gif_error_msg());
1032 #endif
1033               return MS_FAILURE;
1034             }
1035           }
1036         }
1037         break;
1038       case EXTENSION_RECORD_TYPE:
1039         /* skip all extension blocks */
1040         if (DGifGetExtension(image, &extCode, &extension) == GIF_ERROR) {
1041 #if defined GIFLIB_MAJOR && GIFLIB_MAJOR >= 5
1042           msSetError(MS_MISCERR,"corrupted gif image?: %s","readGIF()", gif_error_msg(image->Error));
1043 #else
1044           msSetError(MS_MISCERR,"corrupted gif image?: %s","readGIF()", gif_error_msg());
1045 #endif
1046           return MS_FAILURE;
1047         }
1048         if(extCode == 249 && (extension[1] & 1))
1049           transIdx = extension[4];
1050         for (;;) {
1051           if (DGifGetExtensionNext(image, &extension) == GIF_ERROR) {
1052 #if defined GIFLIB_MAJOR && GIFLIB_MAJOR >= 5
1053             msSetError(MS_MISCERR,"corrupted gif image?: %s","readGIF()", gif_error_msg(image->Error));
1054 #else
1055             msSetError(MS_MISCERR,"corrupted gif image?: %s","readGIF()", gif_error_msg());
1056 #endif
1057             return MS_FAILURE;
1058           }
1059           if (extension == NULL)
1060             break;
1061           if(extension[1] & 1)
1062             transIdx = extension[4];
1063         }
1064         break;
1065       case UNDEFINED_RECORD_TYPE:
1066         break;
1067       case TERMINATE_RECORD_TYPE:
1068         break;
1069       default:
1070         break;
1071     }
1072 
1073   } while (recordType != TERMINATE_RECORD_TYPE);
1074 
1075 
1076 #if defined GIFLIB_MAJOR && GIFLIB_MINOR && ((GIFLIB_MAJOR == 5 && GIFLIB_MINOR >= 1) || (GIFLIB_MAJOR > 5))
1077   if (DGifCloseFile(image, &errcode) == GIF_ERROR) {
1078     msSetError(MS_MISCERR,"failed to close gif after loading: %s","readGIF()", gif_error_msg(errcode));
1079     return MS_FAILURE;
1080   }
1081 #else
1082   if (DGifCloseFile(image) == GIF_ERROR) {
1083 #if defined GIFLIB_MAJOR && GIFLIB_MAJOR >= 5
1084     msSetError(MS_MISCERR,"failed to close gif after loading: %s","readGIF()", gif_error_msg(image->Error));
1085 #else
1086     msSetError(MS_MISCERR,"failed to close gif after loading: %s","readGIF()", gif_error_msg());
1087 #endif
1088     return MS_FAILURE;
1089   }
1090 #endif
1091 
1092   return MS_SUCCESS;
1093 }
1094 #endif
1095 
msLoadMSRasterBufferFromFile(char * path,rasterBufferObj * rb)1096 int msLoadMSRasterBufferFromFile(char *path, rasterBufferObj *rb)
1097 {
1098   FILE *stream;
1099   unsigned char signature[8];
1100   int ret = MS_FAILURE;
1101   stream = fopen(path,"rb");
1102   if(!stream) {
1103     msSetError(MS_MISCERR, "unable to open file %s for reading", "msLoadMSRasterBufferFromFile()", path);
1104     return MS_FAILURE;
1105   }
1106   if(1 != fread(signature,8,1,stream)) {
1107     fclose(stream);
1108     msSetError(MS_MISCERR, "Unable to read header from image file %s", "msLoadMSRasterBufferFromFile()",path);
1109     return MS_FAILURE;
1110   }
1111   fclose(stream);
1112   if(png_sig_cmp(signature,0,8) == 0) {
1113     ret = readPNG(path,rb);
1114   } else if (!strncmp((char*)signature,"GIF",3)) {
1115 #ifdef USE_GIF
1116     ret = readGIF(path,rb);
1117 #else
1118     msSetError(MS_MISCERR,"pixmap %s seems to be GIF formatted, but this server is compiled without GIF support","readImage()",path);
1119     return MS_FAILURE;
1120 #endif
1121   } else {
1122     msSetError(MS_MISCERR,"unsupported pixmap format","readImage()");
1123     return MS_FAILURE;
1124   }
1125   return ret;
1126 }
1127