fixed a harmless warning
[wxWidgets.git] / src / common / imagpng.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/imagepng.cpp
3 // Purpose: wxImage PNG handler
4 // Author: Robert Roebling
5 // RCS-ID: $Id$
6 // Copyright: (c) Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 // ============================================================================
11 // declarations
12 // ============================================================================
13
14 #ifdef __GNUG__
15 #pragma implementation "imagpng.h"
16 #endif
17
18 // ----------------------------------------------------------------------------
19 // headers
20 // ----------------------------------------------------------------------------
21
22 // For compilers that support precompilation, includes "wx.h".
23 #include "wx/wxprec.h"
24
25 #ifdef __BORLANDC__
26 #pragma hdrstop
27 #endif
28
29 #ifndef WX_PRECOMP
30 #include "wx/defs.h"
31 #endif
32
33 #if wxUSE_IMAGE && wxUSE_LIBPNG
34
35 #include "wx/imagpng.h"
36 #include "wx/bitmap.h"
37 #include "wx/debug.h"
38 #include "wx/log.h"
39 #include "wx/app.h"
40 #include "png.h"
41 #include "wx/filefn.h"
42 #include "wx/wfstream.h"
43 #include "wx/intl.h"
44 #include "wx/module.h"
45
46 // For memcpy
47 #include <string.h>
48
49 #ifdef __SALFORDC__
50 #ifdef FAR
51 #undef FAR
52 #endif
53 #endif
54
55 // ----------------------------------------------------------------------------
56 // constants
57 // ----------------------------------------------------------------------------
58
59 // image can not have any transparent pixels at all, have only 100% opaque
60 // and/or 100% transparent pixels in which case a simple mask is enough to
61 // store this information in wxImage or have a real alpha channel in which case
62 // we need to have it in wxImage as well
63 enum Transparency
64 {
65 Transparency_None,
66 Transparency_Mask,
67 Transparency_Alpha
68 };
69
70 // ----------------------------------------------------------------------------
71 // local functions
72 // ----------------------------------------------------------------------------
73
74 // return the kind of transparency needed for this image assuming that it does
75 // have transparent pixels, i.e. either Transparency_Alpha or Transparency_Mask
76 static Transparency
77 CheckTransparency(const unsigned char *ptr,
78 png_uint_32 x, png_uint_32 y, png_uint_32 w, png_uint_32 h,
79 size_t numColBytes);
80
81 // init the alpha channel for the image and fill it with 1s up to (x, y)
82 static unsigned char *InitAlpha(wxImage *image, png_uint_32 x, png_uint_32 y);
83
84 // find a free colour for the mask in the PNG data array
85 static void
86 FindMaskColour(unsigned char **lines, png_uint_32 width, png_uint_32 height,
87 unsigned char& rMask, unsigned char& gMask, unsigned char& bMask);
88
89 // ============================================================================
90 // wxPNGHandler implementation
91 // ============================================================================
92
93 IMPLEMENT_DYNAMIC_CLASS(wxPNGHandler,wxImageHandler)
94
95 #if wxUSE_STREAMS
96
97 #ifndef PNGLINKAGEMODE
98 #ifdef __WATCOMC__
99 // we need an explicit cdecl for Watcom, at least according to
100 //
101 // http://sf.net/tracker/index.php?func=detail&aid=651492&group_id=9863&atid=109863
102 //
103 // more testing is needed for this however, please remove this comment
104 // if you can confirm that my fix works with Watcom 11
105 #define PNGLINKAGEMODE cdecl
106 #else
107 #define PNGLINKAGEMODE LINKAGEMODE
108 #endif
109 #endif
110
111
112 // VS: wxPNGInfoStruct declared below is a hack that needs some explanation.
113 // First, let me describe what's the problem: libpng uses jmp_buf in
114 // its png_struct structure. Unfortunately, this structure is
115 // compiler-specific and may vary in size, so if you use libpng compiled
116 // as DLL with another compiler than the main executable, it may not work
117 // (this is for example the case with wxMGL port and SciTech MGL library
118 // that provides custom runtime-loadable libpng implementation with jmpbuf
119 // disabled altogether). Luckily, it is still possible to use setjmp() &
120 // longjmp() as long as the structure is not part of png_struct.
121 //
122 // Sadly, there's no clean way to attach user-defined data to png_struct.
123 // There is only one customizable place, png_struct.io_ptr, which is meant
124 // only for I/O routines and is set with png_set_read_fn or
125 // png_set_write_fn. The hacky part is that we use io_ptr to store
126 // a pointer to wxPNGInfoStruct that holds I/O structures _and_ jmp_buf.
127
128 struct wxPNGInfoStruct
129 {
130 jmp_buf jmpbuf;
131 bool verbose;
132
133 union
134 {
135 wxInputStream *in;
136 wxOutputStream *out;
137 } stream;
138 };
139
140 #define WX_PNG_INFO(png_ptr) ((wxPNGInfoStruct*)png_get_io_ptr(png_ptr))
141
142 // ----------------------------------------------------------------------------
143 // helper functions
144 // ----------------------------------------------------------------------------
145
146 extern "C"
147 {
148
149 void PNGLINKAGEMODE _PNG_stream_reader( png_structp png_ptr, png_bytep data, png_size_t length )
150 {
151 WX_PNG_INFO(png_ptr)->stream.in->Read(data, length);
152 }
153
154 void PNGLINKAGEMODE _PNG_stream_writer( png_structp png_ptr, png_bytep data, png_size_t length )
155 {
156 WX_PNG_INFO(png_ptr)->stream.out->Write(data, length);
157 }
158
159 // from pngerror.c
160 // so that the libpng doesn't send anything on stderr
161 void
162 PNGLINKAGEMODE wx_png_error(png_structp png_ptr, png_const_charp message)
163 {
164 wxPNGInfoStruct *info = WX_PNG_INFO(png_ptr);
165 if (info->verbose)
166 wxLogError( wxString::FromAscii(message) );
167
168 #ifdef USE_FAR_KEYWORD
169 {
170 jmp_buf jmpbuf;
171 png_memcpy(jmpbuf,info->jmpbuf,sizeof(jmp_buf));
172 longjmp(jmpbuf, 1);
173 }
174 #else
175 longjmp(info->jmpbuf, 1);
176 #endif
177 }
178
179 void
180 PNGLINKAGEMODE wx_png_warning(png_structp png_ptr, png_const_charp message)
181 {
182 wxPNGInfoStruct *info = WX_PNG_INFO(png_ptr);
183 if (info->verbose)
184 wxLogWarning( wxString::FromAscii(message) );
185 }
186
187 } // extern "C"
188
189 // ----------------------------------------------------------------------------
190 // LoadFile() helpers
191 // ----------------------------------------------------------------------------
192
193 // determine the kind of transparency we need for this image: if the only alpha
194 // values it has are 0 and 0xff then we can simply create a mask for it, we
195 // should be ok with a simple mask but otherwise we need a full blown alpha
196 // channel in wxImage
197 //
198 // parameters:
199 // ptr the start of the data to examine
200 // x, y starting position
201 // w, h size of the image
202 // numColBytes number of colour bytes (1 for grey scale, 3 for RGB)
203 // (NB: alpha always follows the colour bytes)
204 Transparency
205 CheckTransparency(const unsigned char *ptr,
206 png_uint_32 x, png_uint_32 y, png_uint_32 w, png_uint_32 h,
207 size_t numColBytes)
208 {
209 // suppose that a mask will suffice and check all the remaining alpha
210 // values to see if it does
211 unsigned char a2;
212 unsigned const char *ptr2 = ptr;
213 for ( png_uint_32 y2 = y; y2 < h; y2++ )
214 {
215 for ( png_uint_32 x2 = x + 1; x2 < w; x2++ )
216 {
217 // skip the grey byte
218 ptr2 += numColBytes;
219 a2 = *ptr2++;
220
221 if ( a2 && a2 != 0xff )
222 {
223 // not fully opaque nor fully transparent, hence need alpha
224 return Transparency_Alpha;
225 }
226 }
227 }
228
229 // mask will be enough
230 return Transparency_Mask;
231 }
232
233 unsigned char *InitAlpha(wxImage *image, png_uint_32 x, png_uint_32 y)
234 {
235 // create alpha channel
236 image->SetAlpha();
237
238 unsigned char *alpha = image->GetAlpha();
239
240 // set alpha for the pixels we had so far
241 for ( png_uint_32 y2 = 0; y2 <= y; y2++ )
242 {
243 for ( png_uint_32 x2 = 0; x2 < x; x2++ )
244 {
245 // all the previous pixels were opaque
246 *alpha++ = 0xff;
247 }
248 }
249
250 return alpha;
251 }
252
253 void
254 FindMaskColour(unsigned char **lines, png_uint_32 width, png_uint_32 height,
255 unsigned char& rMask, unsigned char& gMask, unsigned char& bMask)
256 {
257 // choosing the colour for the mask is more
258 // difficult: we need to iterate over the entire
259 // image for this in order to choose an unused
260 // colour (this is not very efficient but what else
261 // can we do?)
262 wxImageHistogram h;
263 unsigned nentries = 0;
264 unsigned char r2, g2, b2;
265 for ( png_uint_32 y2 = 0; y2 < height; y2++ )
266 {
267 const unsigned char *p = lines[y2];
268 for ( png_uint_32 x2 = 0; x2 < width; x2++ )
269 {
270 r2 = *p++;
271 g2 = *p++;
272 b2 = *p++;
273
274 wxImageHistogramEntry&
275 entry = h[wxImageHistogram:: MakeKey(r2, g2, b2)];
276
277 if ( entry.value++ == 0 )
278 entry.index = nentries++;
279 }
280 }
281
282 if ( !h.FindFirstUnusedColour(&rMask, &gMask, &bMask) )
283 {
284 wxLogWarning(_("Too many colours in PNG, the image may be slightly blurred."));
285
286 // use a fixed mask colour and we'll fudge
287 // the real pixels with this colour (see
288 // below)
289 rMask = 0xfe;
290 gMask = 0;
291 bMask = 0xff;
292 }
293 }
294
295 // ----------------------------------------------------------------------------
296 // reading PNGs
297 // ----------------------------------------------------------------------------
298
299 bool wxPNGHandler::DoCanRead( wxInputStream& stream )
300 {
301 unsigned char hdr[4];
302
303 if ( !stream.Read(hdr, WXSIZEOF(hdr)) )
304 return FALSE;
305
306 return memcmp(hdr, "\211PNG", WXSIZEOF(hdr)) == 0;
307 }
308
309 // convert data from RGB to wxImage format
310 static
311 void CopyDataFromPNG(wxImage *image,
312 unsigned char **lines,
313 png_uint_32 width,
314 png_uint_32 height,
315 int color_type)
316 {
317 Transparency transparency = Transparency_None;
318
319 // only non NULL if transparency == Transparency_Alpha
320 unsigned char *alpha = NULL;
321
322 // RGB of the mask colour if transparency == Transparency_Mask
323 // (but init them anyhow to avoid compiler warnings)
324 unsigned char rMask = 0,
325 gMask = 0,
326 bMask = 0;
327
328 unsigned char *ptrDst = image->GetData();
329 if ( !(color_type & PNG_COLOR_MASK_COLOR) )
330 {
331 // grey image: GAGAGA... where G == grey component and A == alpha
332 for ( png_uint_32 y = 0; y < height; y++ )
333 {
334 const unsigned char *ptrSrc = lines[y];
335 for ( png_uint_32 x = 0; x < width; x++ )
336 {
337 unsigned char g = *ptrSrc++;
338 unsigned char a = *ptrSrc++;
339
340 // the first time we encounter a transparent pixel we must
341 // decide about what to do about them
342 if ( a != 0xff && transparency == Transparency_None )
343 {
344 // we'll need at least the mask for this image and
345 // maybe even full alpha channel info: the former is
346 // only enough if we have alpha values of 0 and 0xff
347 // only, otherwisewe need the latter
348 transparency = CheckTransparency
349 (
350 ptrSrc,
351 x, y,
352 width, height,
353 1
354 );
355
356 if ( transparency == Transparency_Mask )
357 {
358 // let's choose this colour for the mask: this is
359 // not a problem here as all the other pixels are
360 // grey, i.e. R == G == B which is not the case for
361 // this one so no confusion is possible
362 rMask = 0xff;
363 gMask = 0;
364 bMask = 0xff;
365 }
366 else // transparency == Transparency_Alpha
367 {
368 alpha = InitAlpha(image, x, y);
369 }
370 }
371
372 switch ( transparency )
373 {
374 case Transparency_Alpha:
375 *alpha++ = a;
376 // fall through
377
378 case Transparency_None:
379 *ptrDst++ = g;
380 *ptrDst++ = g;
381 *ptrDst++ = g;
382 break;
383
384 case Transparency_Mask:
385 *ptrDst++ = rMask;
386 *ptrDst++ = bMask;
387 *ptrDst++ = gMask;
388 }
389 }
390 }
391 }
392 else // colour image: RGBRGB...
393 {
394 for ( png_uint_32 y = 0; y < height; y++ )
395 {
396 const unsigned char *ptrSrc = lines[y];
397 for ( png_uint_32 x = 0; x < width; x++ )
398 {
399 unsigned char r = *ptrSrc++;
400 unsigned char g = *ptrSrc++;
401 unsigned char b = *ptrSrc++;
402 unsigned char a = *ptrSrc++;
403
404 // the logic here is the same as for the grey case except
405 // where noted
406 if ( a != 0xff && transparency == Transparency_None )
407 {
408 transparency = CheckTransparency
409 (
410 ptrSrc,
411 x, y,
412 width, height,
413 3
414 );
415
416 if ( transparency == Transparency_Mask )
417 {
418 FindMaskColour(lines, width, height,
419 rMask, gMask, bMask);
420 }
421 else // transparency == Transparency_Alpha
422 {
423 alpha = InitAlpha(image, x, y);
424 }
425
426 }
427
428 switch ( transparency )
429 {
430 case Transparency_Alpha:
431 *alpha++ = a;
432 // fall through
433
434 case Transparency_None:
435 *ptrDst++ = r;
436 *ptrDst++ = g;
437 *ptrDst++ = b;
438 break;
439
440 case Transparency_Mask:
441 // if we couldn't find a unique colour for the mask, we
442 // can have real pixels with the same value as the mask
443 // and it's better to slightly change their colour than
444 // to make them transparent
445 if ( r == rMask && g == gMask && b == bMask )
446 {
447 r++;
448 }
449
450 *ptrDst++ = rMask;
451 *ptrDst++ = bMask;
452 *ptrDst++ = gMask;
453 }
454 }
455 }
456 }
457
458 if ( transparency == Transparency_Mask )
459 {
460 image->SetMaskColour(rMask, gMask, bMask);
461 }
462 }
463
464 // temporarily disable the warning C4611 (interaction between '_setjmp' and
465 // C++ object destruction is non-portable) - I don't see any dtors here
466 #ifdef __VISUALC__
467 #pragma warning(disable:4611)
468 #endif /* VC++ */
469
470 bool
471 wxPNGHandler::LoadFile(wxImage *image,
472 wxInputStream& stream,
473 bool verbose,
474 int WXUNUSED(index))
475 {
476 // VZ: as this function uses setjmp() the only fool proof error handling
477 // method is to use goto (setjmp is not really C++ dtors friendly...)
478
479 unsigned char **lines = NULL;
480 png_infop info_ptr = (png_infop) NULL;
481 wxPNGInfoStruct wxinfo;
482
483 wxinfo.verbose = verbose;
484 wxinfo.stream.in = &stream;
485
486 image->Destroy();
487
488 png_structp png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING,
489 (voidp) NULL,
490 (png_error_ptr) NULL,
491 (png_error_ptr) NULL );
492 if (!png_ptr)
493 goto error;
494
495 png_set_error_fn(png_ptr, (png_voidp)NULL, wx_png_error, wx_png_warning);
496
497 // NB: please see the comment near wxPNGInfoStruct declaration for
498 // explanation why this line is mandatory
499 png_set_read_fn( png_ptr, &wxinfo, _PNG_stream_reader);
500
501 info_ptr = png_create_info_struct( png_ptr );
502 if (!info_ptr)
503 goto error;
504
505 if (setjmp(wxinfo.jmpbuf))
506 goto error;
507
508 png_uint_32 i, width, height;
509 int bit_depth, color_type, interlace_type;
510
511 png_read_info( png_ptr, info_ptr );
512 png_get_IHDR( png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, (int*) NULL, (int*) NULL );
513
514 if (color_type == PNG_COLOR_TYPE_PALETTE)
515 png_set_expand( png_ptr );
516
517 // Fix for Bug [ 439207 ] Monochrome PNG images come up black
518 if (bit_depth < 8)
519 png_set_expand( png_ptr );
520
521 png_set_strip_16( png_ptr );
522 png_set_packing( png_ptr );
523 if (png_get_valid( png_ptr, info_ptr, PNG_INFO_tRNS))
524 png_set_expand( png_ptr );
525 png_set_filler( png_ptr, 0xff, PNG_FILLER_AFTER );
526
527 image->Create( (int)width, (int)height );
528
529 if (!image->Ok())
530 goto error;
531
532 lines = (unsigned char **)malloc( (size_t)(height * sizeof(unsigned char *)) );
533 if ( !lines )
534 goto error;
535
536 for (i = 0; i < height; i++)
537 {
538 if ((lines[i] = (unsigned char *)malloc( (size_t)(width * (sizeof(unsigned char) * 4)))) == NULL)
539 {
540 for ( unsigned int n = 0; n < i; n++ )
541 free( lines[n] );
542 goto error;
543 }
544 }
545
546 png_read_image( png_ptr, lines );
547 png_read_end( png_ptr, info_ptr );
548 png_destroy_read_struct( &png_ptr, &info_ptr, (png_infopp) NULL );
549
550 // loaded successfully, now init wxImage with this data
551 CopyDataFromPNG(image, lines, width, height, color_type);
552
553 for ( i = 0; i < height; i++ )
554 free( lines[i] );
555 free( lines );
556
557 return TRUE;
558
559 error:
560 if (verbose)
561 wxLogError(_("Couldn't load a PNG image - file is corrupted or not enough memory."));
562
563 if ( image->Ok() )
564 {
565 image->Destroy();
566 }
567
568 if ( lines )
569 {
570 free( lines );
571 }
572
573 if ( png_ptr )
574 {
575 if ( info_ptr )
576 {
577 png_destroy_read_struct( &png_ptr, &info_ptr, (png_infopp) NULL );
578 free(info_ptr);
579 }
580 else
581 png_destroy_read_struct( &png_ptr, (png_infopp) NULL, (png_infopp) NULL );
582 }
583 return FALSE;
584 }
585
586 // ----------------------------------------------------------------------------
587 // writing PNGs
588 // ----------------------------------------------------------------------------
589
590 bool wxPNGHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbose )
591 {
592 wxPNGInfoStruct wxinfo;
593
594 wxinfo.verbose = verbose;
595 wxinfo.stream.out = &stream;
596
597 png_structp png_ptr = png_create_write_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
598 if (!png_ptr)
599 {
600 if (verbose)
601 wxLogError(_("Couldn't save PNG image."));
602 return FALSE;
603 }
604
605 png_set_error_fn(png_ptr, (png_voidp)NULL, wx_png_error, wx_png_warning);
606
607 png_infop info_ptr = png_create_info_struct(png_ptr);
608 if (info_ptr == NULL)
609 {
610 png_destroy_write_struct( &png_ptr, (png_infopp)NULL );
611 if (verbose)
612 wxLogError(_("Couldn't save PNG image."));
613 return FALSE;
614 }
615
616 if (setjmp(wxinfo.jmpbuf))
617 {
618 png_destroy_write_struct( &png_ptr, (png_infopp)NULL );
619 if (verbose)
620 wxLogError(_("Couldn't save PNG image."));
621 return FALSE;
622 }
623
624 // NB: please see the comment near wxPNGInfoStruct declaration for
625 // explanation why this line is mandatory
626 png_set_write_fn( png_ptr, &wxinfo, _PNG_stream_writer, NULL);
627
628 png_set_IHDR( png_ptr, info_ptr, image->GetWidth(), image->GetHeight(), 8,
629 PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
630 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
631
632 png_color_8 sig_bit;
633 sig_bit.red = 8;
634 sig_bit.green = 8;
635 sig_bit.blue = 8;
636 sig_bit.alpha = 8;
637 png_set_sBIT( png_ptr, info_ptr, &sig_bit );
638 png_write_info( png_ptr, info_ptr );
639 png_set_shift( png_ptr, &sig_bit );
640 png_set_packing( png_ptr );
641
642 unsigned char *data = (unsigned char *)malloc( image->GetWidth()*4 );
643 if (!data)
644 {
645 png_destroy_write_struct( &png_ptr, (png_infopp)NULL );
646 return FALSE;
647 }
648
649 for (int y = 0; y < image->GetHeight(); y++)
650 {
651 unsigned char *ptr = image->GetData() + (y * image->GetWidth() * 3);
652 for (int x = 0; x < image->GetWidth(); x++)
653 {
654 data[(x << 2) + 0] = *ptr++;
655 data[(x << 2) + 1] = *ptr++;
656 data[(x << 2) + 2] = *ptr++;
657 if (( !image->HasMask() ) || \
658 (data[(x << 2) + 0] != image->GetMaskRed()) || \
659 (data[(x << 2) + 1] != image->GetMaskGreen()) || \
660 (data[(x << 2) + 2] != image->GetMaskBlue()))
661 {
662 data[(x << 2) + 3] = 255;
663 }
664 else
665 {
666 data[(x << 2) + 3] = 0;
667 }
668 }
669 png_bytep row_ptr = data;
670 png_write_rows( png_ptr, &row_ptr, 1 );
671 }
672
673 free(data);
674 png_write_end( png_ptr, info_ptr );
675 png_destroy_write_struct( &png_ptr, (png_infopp)&info_ptr );
676
677 return TRUE;
678 }
679
680 #ifdef __VISUALC__
681 #pragma warning(default:4611)
682 #endif /* VC++ */
683
684 #endif // wxUSE_STREAMS
685
686 #endif // wxUSE_LIBPNG