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