Compilation fix for PNG saving code when wxUSE_PALETTE==0.
[wxWidgets.git] / src / common / imagpng.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/imagpng.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 // ----------------------------------------------------------------------------
15 // headers
16 // ----------------------------------------------------------------------------
17
18 // For compilers that support precompilation, includes "wx.h".
19 #include "wx/wxprec.h"
20
21 #ifdef __BORLANDC__
22 #pragma hdrstop
23 #endif
24
25 #if wxUSE_IMAGE && wxUSE_LIBPNG
26
27 #include "wx/imagpng.h"
28 #include "wx/versioninfo.h"
29
30 #ifndef WX_PRECOMP
31 #include "wx/log.h"
32 #include "wx/intl.h"
33 #include "wx/palette.h"
34 #include "wx/stream.h"
35 #endif
36
37 #include "png.h"
38
39 // For memcpy
40 #include <string.h>
41
42 // ----------------------------------------------------------------------------
43 // constants
44 // ----------------------------------------------------------------------------
45
46 // image can not have any transparent pixels at all, have only 100% opaque
47 // and/or 100% transparent pixels in which case a simple mask is enough to
48 // store this information in wxImage or have a real alpha channel in which case
49 // we need to have it in wxImage as well
50 enum Transparency
51 {
52 Transparency_None,
53 Transparency_Mask,
54 Transparency_Alpha
55 };
56
57 // ----------------------------------------------------------------------------
58 // local functions
59 // ----------------------------------------------------------------------------
60
61 // return the kind of transparency needed for this image assuming that it does
62 // have transparent pixels, i.e. either Transparency_Alpha or Transparency_Mask
63 static Transparency
64 CheckTransparency(unsigned char **lines,
65 png_uint_32 x, png_uint_32 y, png_uint_32 w, png_uint_32 h,
66 size_t numColBytes);
67
68 // init the alpha channel for the image and fill it with 1s up to (x, y)
69 static unsigned char *InitAlpha(wxImage *image, png_uint_32 x, png_uint_32 y);
70
71 // find a free colour for the mask in the PNG data array
72 static void
73 FindMaskColour(unsigned char **lines, png_uint_32 width, png_uint_32 height,
74 unsigned char& rMask, unsigned char& gMask, unsigned char& bMask);
75
76 // is the pixel with this value of alpha a fully opaque one?
77 static inline
78 bool IsOpaque(unsigned char a)
79 {
80 return a == 0xff;
81 }
82
83 // is the pixel with this value of alpha a fully transparent one?
84 static inline
85 bool IsTransparent(unsigned char a)
86 {
87 return !a;
88 }
89
90 // ============================================================================
91 // wxPNGHandler implementation
92 // ============================================================================
93
94 IMPLEMENT_DYNAMIC_CLASS(wxPNGHandler,wxImageHandler)
95
96 #if wxUSE_STREAMS
97
98 #ifndef PNGLINKAGEMODE
99 #ifdef PNGAPI
100 #define PNGLINKAGEMODE PNGAPI
101 #elif defined(__WATCOMC__)
102 // we need an explicit cdecl for Watcom, at least according to
103 //
104 // http://sf.net/tracker/index.php?func=detail&aid=651492&group_id=9863&atid=109863
105 //
106 // more testing is needed for this however, please remove this comment
107 // if you can confirm that my fix works with Watcom 11
108 #define PNGLINKAGEMODE cdecl
109 #else
110 #define PNGLINKAGEMODE LINKAGEMODE
111 #endif
112 #endif
113
114
115 // VS: wxPNGInfoStruct declared below is a hack that needs some explanation.
116 // First, let me describe what's the problem: libpng uses jmp_buf in
117 // its png_struct structure. Unfortunately, this structure is
118 // compiler-specific and may vary in size, so if you use libpng compiled
119 // as DLL with another compiler than the main executable, it may not work
120 // (this is for example the case with wxMGL port and SciTech MGL library
121 // that provides custom runtime-loadable libpng implementation with jmpbuf
122 // disabled altogether). Luckily, it is still possible to use setjmp() &
123 // longjmp() as long as the structure is not part of png_struct.
124 //
125 // Sadly, there's no clean way to attach user-defined data to png_struct.
126 // There is only one customizable place, png_struct.io_ptr, which is meant
127 // only for I/O routines and is set with png_set_read_fn or
128 // png_set_write_fn. The hacky part is that we use io_ptr to store
129 // a pointer to wxPNGInfoStruct that holds I/O structures _and_ jmp_buf.
130
131 struct wxPNGInfoStruct
132 {
133 jmp_buf jmpbuf;
134 bool verbose;
135
136 union
137 {
138 wxInputStream *in;
139 wxOutputStream *out;
140 } stream;
141 };
142
143 #define WX_PNG_INFO(png_ptr) ((wxPNGInfoStruct*)png_get_io_ptr(png_ptr))
144
145 // ----------------------------------------------------------------------------
146 // helper functions
147 // ----------------------------------------------------------------------------
148
149 extern "C"
150 {
151
152 static void PNGLINKAGEMODE wx_PNG_stream_reader( png_structp png_ptr, png_bytep data,
153 png_size_t length )
154 {
155 WX_PNG_INFO(png_ptr)->stream.in->Read(data, length);
156 }
157
158 static void PNGLINKAGEMODE wx_PNG_stream_writer( png_structp png_ptr, png_bytep data,
159 png_size_t length )
160 {
161 WX_PNG_INFO(png_ptr)->stream.out->Write(data, length);
162 }
163
164 static void
165 PNGLINKAGEMODE wx_png_warning(png_structp png_ptr, png_const_charp message)
166 {
167 wxPNGInfoStruct *info = png_ptr ? WX_PNG_INFO(png_ptr) : NULL;
168 if ( !info || info->verbose )
169 {
170 wxLogWarning( wxString::FromAscii(message) );
171 }
172 }
173
174 // from pngerror.c
175 // so that the libpng doesn't send anything on stderr
176 static void
177 PNGLINKAGEMODE wx_png_error(png_structp png_ptr, png_const_charp message)
178 {
179 wx_png_warning(NULL, message);
180
181 // we're not using libpng built-in jump buffer (see comment before
182 // wxPNGInfoStruct above) so we have to return ourselves, otherwise libpng
183 // would just abort
184 longjmp(WX_PNG_INFO(png_ptr)->jmpbuf, 1);
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 (transparent) and 0xff (opaque) then we can simply
195 // create a mask for it, we should be ok with a simple mask but otherwise we
196 // need a full blown alpha channel in wxImage
197 //
198 // parameters:
199 // lines raw PNG data
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(unsigned char **lines,
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 for ( ; y < h; y++ )
212 {
213 // each pixel is numColBytes+1 bytes, offset into the current line by
214 // the current x position
215 unsigned const char *ptr = lines[y] + (x * (numColBytes + 1));
216
217 for ( png_uint_32 x2 = x; x2 < w; x2++ )
218 {
219 // skip the grey or colour byte(s)
220 ptr += numColBytes;
221
222 unsigned char a2 = *ptr++;
223
224 if ( !IsTransparent(a2) && !IsOpaque(a2) )
225 {
226 // not fully opaque nor fully transparent, hence need alpha
227 return Transparency_Alpha;
228 }
229 }
230
231 // during the next loop iteration check all the pixels in the row
232 x = 0;
233 }
234
235 // mask will be enough
236 return Transparency_Mask;
237 }
238
239 unsigned char *InitAlpha(wxImage *image, png_uint_32 x, png_uint_32 y)
240 {
241 // create alpha channel
242 image->SetAlpha();
243
244 unsigned char *alpha = image->GetAlpha();
245
246 // set alpha for the pixels we had so far
247 png_uint_32 end = y * image->GetWidth() + x;
248 for ( png_uint_32 i = 0; i < end; i++ )
249 {
250 // all the previous pixels were opaque
251 *alpha++ = 0xff;
252 }
253
254 return alpha;
255 }
256
257 void
258 FindMaskColour(unsigned char **lines, png_uint_32 width, png_uint_32 height,
259 unsigned char& rMask, unsigned char& gMask, unsigned char& bMask)
260 {
261 // choosing the colour for the mask is more
262 // difficult: we need to iterate over the entire
263 // image for this in order to choose an unused
264 // colour (this is not very efficient but what else
265 // can we do?)
266 wxImageHistogram h;
267 unsigned nentries = 0;
268 unsigned char r2, g2, b2;
269 for ( png_uint_32 y2 = 0; y2 < height; y2++ )
270 {
271 const unsigned char *p = lines[y2];
272 for ( png_uint_32 x2 = 0; x2 < width; x2++ )
273 {
274 r2 = *p++;
275 g2 = *p++;
276 b2 = *p++;
277 ++p; // jump over alpha
278
279 wxImageHistogramEntry&
280 entry = h[wxImageHistogram:: MakeKey(r2, g2, b2)];
281
282 if ( entry.value++ == 0 )
283 entry.index = nentries++;
284 }
285 }
286
287 if ( !h.FindFirstUnusedColour(&rMask, &gMask, &bMask) )
288 {
289 wxLogWarning(_("Too many colours in PNG, the image may be slightly blurred."));
290
291 // use a fixed mask colour and we'll fudge
292 // the real pixels with this colour (see
293 // below)
294 rMask = 0xfe;
295 gMask = 0;
296 bMask = 0xff;
297 }
298 }
299
300 // ----------------------------------------------------------------------------
301 // reading PNGs
302 // ----------------------------------------------------------------------------
303
304 bool wxPNGHandler::DoCanRead( wxInputStream& stream )
305 {
306 unsigned char hdr[4];
307
308 if ( !stream.Read(hdr, WXSIZEOF(hdr)) ) // it's ok to modify the stream position here
309 return false;
310
311 return memcmp(hdr, "\211PNG", WXSIZEOF(hdr)) == 0;
312 }
313
314 // convert data from RGB to wxImage format
315 static
316 void CopyDataFromPNG(wxImage *image,
317 unsigned char **lines,
318 png_uint_32 width,
319 png_uint_32 height,
320 int color_type)
321 {
322 Transparency transparency = Transparency_None;
323
324 // only non NULL if transparency == Transparency_Alpha
325 unsigned char *alpha = NULL;
326
327 // RGB of the mask colour if transparency == Transparency_Mask
328 // (but init them anyhow to avoid compiler warnings)
329 unsigned char rMask = 0,
330 gMask = 0,
331 bMask = 0;
332
333 unsigned char *ptrDst = image->GetData();
334 if ( !(color_type & PNG_COLOR_MASK_COLOR) )
335 {
336 // grey image: GAGAGA... where G == grey component and A == alpha
337 for ( png_uint_32 y = 0; y < height; y++ )
338 {
339 const unsigned char *ptrSrc = lines[y];
340 for ( png_uint_32 x = 0; x < width; x++ )
341 {
342 unsigned char g = *ptrSrc++;
343 unsigned char a = *ptrSrc++;
344
345 // the first time we encounter a transparent pixel we must
346 // decide about what to do about them
347 if ( !IsOpaque(a) && transparency == Transparency_None )
348 {
349 // we'll need at least the mask for this image and
350 // maybe even full alpha channel info: the former is
351 // only enough if we have alpha values of 0 and 0xff
352 // only, otherwisewe need the latter
353 transparency = CheckTransparency
354 (
355 lines,
356 x, y,
357 width, height,
358 1
359 );
360
361 if ( transparency == Transparency_Mask )
362 {
363 // let's choose this colour for the mask: this is
364 // not a problem here as all the other pixels are
365 // grey, i.e. R == G == B which is not the case for
366 // this one so no confusion is possible
367 rMask = 0xff;
368 gMask = 0;
369 bMask = 0xff;
370 }
371 else // transparency == Transparency_Alpha
372 {
373 alpha = InitAlpha(image, x, y);
374 }
375 }
376
377 switch ( transparency )
378 {
379 case Transparency_Mask:
380 if ( IsTransparent(a) )
381 {
382 *ptrDst++ = rMask;
383 *ptrDst++ = gMask;
384 *ptrDst++ = bMask;
385 break;
386 }
387 // else: !transparent
388
389 // must be opaque then as otherwise we shouldn't be
390 // using the mask at all
391 wxASSERT_MSG( IsOpaque(a), wxT("logic error") );
392
393 // fall through
394
395 case Transparency_Alpha:
396 if ( alpha )
397 *alpha++ = a;
398 // fall through
399
400 case Transparency_None:
401 *ptrDst++ = g;
402 *ptrDst++ = g;
403 *ptrDst++ = g;
404 break;
405 }
406 }
407 }
408 }
409 else // colour image: RGBRGB...
410 {
411 for ( png_uint_32 y = 0; y < height; y++ )
412 {
413 const unsigned char *ptrSrc = lines[y];
414 for ( png_uint_32 x = 0; x < width; x++ )
415 {
416 unsigned char r = *ptrSrc++;
417 unsigned char g = *ptrSrc++;
418 unsigned char b = *ptrSrc++;
419 unsigned char a = *ptrSrc++;
420
421 // the logic here is the same as for the grey case except
422 // where noted
423 if ( !IsOpaque(a) && transparency == Transparency_None )
424 {
425 transparency = CheckTransparency
426 (
427 lines,
428 x, y,
429 width, height,
430 3
431 );
432
433 if ( transparency == Transparency_Mask )
434 {
435 FindMaskColour(lines, width, height,
436 rMask, gMask, bMask);
437 }
438 else // transparency == Transparency_Alpha
439 {
440 alpha = InitAlpha(image, x, y);
441 }
442
443 }
444
445 switch ( transparency )
446 {
447 case Transparency_Mask:
448 if ( IsTransparent(a) )
449 {
450 *ptrDst++ = rMask;
451 *ptrDst++ = gMask;
452 *ptrDst++ = bMask;
453 break;
454 }
455 else // !transparent
456 {
457 // must be opaque then as otherwise we shouldn't be
458 // using the mask at all
459 wxASSERT_MSG( IsOpaque(a), wxT("logic error") );
460
461 // if we couldn't find a unique colour for the
462 // mask, we can have real pixels with the same
463 // value as the mask and it's better to slightly
464 // change their colour than to make them
465 // transparent
466 if ( r == rMask && g == gMask && b == bMask )
467 {
468 r++;
469 }
470 }
471
472 // fall through
473
474 case Transparency_Alpha:
475 if ( alpha )
476 *alpha++ = a;
477 // fall through
478
479 case Transparency_None:
480 *ptrDst++ = r;
481 *ptrDst++ = g;
482 *ptrDst++ = b;
483 break;
484 }
485 }
486 }
487 }
488
489 if ( transparency == Transparency_Mask )
490 {
491 image->SetMaskColour(rMask, gMask, bMask);
492 }
493 }
494
495 // temporarily disable the warning C4611 (interaction between '_setjmp' and
496 // C++ object destruction is non-portable) - I don't see any dtors here
497 #ifdef __VISUALC__
498 #pragma warning(disable:4611)
499 #endif /* VC++ */
500
501 bool
502 wxPNGHandler::LoadFile(wxImage *image,
503 wxInputStream& stream,
504 bool verbose,
505 int WXUNUSED(index))
506 {
507 // VZ: as this function uses setjmp() the only fool-proof error handling
508 // method is to use goto (setjmp is not really C++ dtors friendly...)
509
510 unsigned char **lines = NULL;
511 png_infop info_ptr = (png_infop) NULL;
512 wxPNGInfoStruct wxinfo;
513
514 png_uint_32 i, width, height = 0;
515 int bit_depth, color_type, interlace_type;
516
517 wxinfo.verbose = verbose;
518 wxinfo.stream.in = &stream;
519
520 image->Destroy();
521
522 png_structp png_ptr = png_create_read_struct
523 (
524 PNG_LIBPNG_VER_STRING,
525 (voidp) NULL,
526 wx_png_error,
527 wx_png_warning
528 );
529 if (!png_ptr)
530 goto error;
531
532 // NB: please see the comment near wxPNGInfoStruct declaration for
533 // explanation why this line is mandatory
534 png_set_read_fn( png_ptr, &wxinfo, wx_PNG_stream_reader);
535
536 info_ptr = png_create_info_struct( png_ptr );
537 if (!info_ptr)
538 goto error;
539
540 if (setjmp(wxinfo.jmpbuf))
541 goto error;
542
543 png_read_info( png_ptr, info_ptr );
544 png_get_IHDR( png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL );
545
546 if (color_type == PNG_COLOR_TYPE_PALETTE)
547 png_set_expand( png_ptr );
548
549 // Fix for Bug [ 439207 ] Monochrome PNG images come up black
550 if (bit_depth < 8)
551 png_set_expand( png_ptr );
552
553 png_set_strip_16( png_ptr );
554 png_set_packing( png_ptr );
555 if (png_get_valid( png_ptr, info_ptr, PNG_INFO_tRNS))
556 png_set_expand( png_ptr );
557 png_set_filler( png_ptr, 0xff, PNG_FILLER_AFTER );
558
559 image->Create((int)width, (int)height, (bool) false /* no need to init pixels */);
560
561 if (!image->Ok())
562 goto error;
563
564 // initialize all line pointers to NULL to ensure that they can be safely
565 // free()d if an error occurs before all of them could be allocated
566 lines = (unsigned char **)calloc(height, sizeof(unsigned char *));
567 if ( !lines )
568 goto error;
569
570 for (i = 0; i < height; i++)
571 {
572 if ((lines[i] = (unsigned char *)malloc( (size_t)(width * (sizeof(unsigned char) * 4)))) == NULL)
573 goto error;
574 }
575
576 png_read_image( png_ptr, lines );
577 png_read_end( png_ptr, info_ptr );
578
579 #if wxUSE_PALETTE
580 if (color_type == PNG_COLOR_TYPE_PALETTE)
581 {
582 png_colorp palette = NULL;
583 int numPalette = 0;
584
585 (void) png_get_PLTE(png_ptr, info_ptr, &palette, &numPalette);
586
587 unsigned char* r = new unsigned char[numPalette];
588 unsigned char* g = new unsigned char[numPalette];
589 unsigned char* b = new unsigned char[numPalette];
590
591 for (int j = 0; j < numPalette; j++)
592 {
593 r[j] = palette[j].red;
594 g[j] = palette[j].green;
595 b[j] = palette[j].blue;
596 }
597
598 image->SetPalette(wxPalette(numPalette, r, g, b));
599 delete[] r;
600 delete[] g;
601 delete[] b;
602 }
603 #endif // wxUSE_PALETTE
604
605 png_destroy_read_struct( &png_ptr, &info_ptr, (png_infopp) NULL );
606
607 // loaded successfully, now init wxImage with this data
608 CopyDataFromPNG(image, lines, width, height, color_type);
609
610 for ( i = 0; i < height; i++ )
611 free( lines[i] );
612 free( lines );
613
614 return true;
615
616 error:
617 if (verbose)
618 {
619 wxLogError(_("Couldn't load a PNG image - file is corrupted or not enough memory."));
620 }
621
622 if ( image->Ok() )
623 {
624 image->Destroy();
625 }
626
627 if ( lines )
628 {
629 for ( unsigned int n = 0; n < height; n++ )
630 free( lines[n] );
631
632 free( lines );
633 }
634
635 if ( png_ptr )
636 {
637 if ( info_ptr )
638 {
639 png_destroy_read_struct( &png_ptr, &info_ptr, (png_infopp) NULL );
640 free(info_ptr);
641 }
642 else
643 png_destroy_read_struct( &png_ptr, (png_infopp) NULL, (png_infopp) NULL );
644 }
645 return false;
646 }
647
648 // ----------------------------------------------------------------------------
649 // SaveFile() helpers
650 // ----------------------------------------------------------------------------
651
652 #if wxUSE_PALETTE
653
654 static int PaletteFind(const png_color& clr, const png_color *pal, int palCount)
655 {
656 for (int i = 0; i < palCount; ++i)
657 {
658 if ( (clr.red == pal[i].red)
659 && (clr.green == pal[i].green)
660 && (clr.blue == pal[i].blue))
661 {
662 return i;
663 }
664 }
665
666 return wxNOT_FOUND;
667 }
668
669 #endif // wxUSE_PALETTE
670
671 // ----------------------------------------------------------------------------
672 // writing PNGs
673 // ----------------------------------------------------------------------------
674
675 bool wxPNGHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbose )
676 {
677 wxPNGInfoStruct wxinfo;
678
679 wxinfo.verbose = verbose;
680 wxinfo.stream.out = &stream;
681
682 png_structp png_ptr = png_create_write_struct
683 (
684 PNG_LIBPNG_VER_STRING,
685 NULL,
686 wx_png_error,
687 wx_png_warning
688 );
689 if (!png_ptr)
690 {
691 if (verbose)
692 {
693 wxLogError(_("Couldn't save PNG image."));
694 }
695 return false;
696 }
697
698 png_infop info_ptr = png_create_info_struct(png_ptr);
699 if (info_ptr == NULL)
700 {
701 png_destroy_write_struct( &png_ptr, (png_infopp)NULL );
702 if (verbose)
703 {
704 wxLogError(_("Couldn't save PNG image."));
705 }
706 return false;
707 }
708
709 if (setjmp(wxinfo.jmpbuf))
710 {
711 png_destroy_write_struct( &png_ptr, (png_infopp)NULL );
712 if (verbose)
713 {
714 wxLogError(_("Couldn't save PNG image."));
715 }
716 return false;
717 }
718
719 // NB: please see the comment near wxPNGInfoStruct declaration for
720 // explanation why this line is mandatory
721 png_set_write_fn( png_ptr, &wxinfo, wx_PNG_stream_writer, NULL);
722
723 const bool bHasPngFormatOption
724 = image->HasOption(wxIMAGE_OPTION_PNG_FORMAT);
725
726 int iColorType = bHasPngFormatOption
727 ? image->GetOptionInt(wxIMAGE_OPTION_PNG_FORMAT)
728 : wxPNG_TYPE_COLOUR;
729
730 bool bHasAlpha = image->HasAlpha();
731 bool bHasMask = image->HasMask();
732
733 #if wxUSE_PALETTE
734 /*
735 Only save as an indexed image if the number of palette entries does not
736 exceed libpng's limit (256).
737 We assume here that we will need an extra palette entry if there's an
738 alpha or mask, regardless of whether a possibly needed conversion from
739 alpha to a mask fails (unlikely), or whether the mask colour already
740 can be found in the palette (more likely). In the latter case an extra
741 palette entry would not be required later on and the image could actually
742 be saved as a palettised PNG (instead now it will be saved as true colour).
743 A little bit of precision is lost, but at the benefit of a lot more
744 simplified code.
745 */
746 bool bUsePalette =
747 (!bHasPngFormatOption || iColorType == wxPNG_TYPE_PALETTE)
748 && image->HasPalette()
749 && image->GetPalette().GetColoursCount()
750 + ((bHasAlpha || bHasMask) ? 1 : 0) <= PNG_MAX_PALETTE_LENGTH;
751
752 wxImage temp_image(*image);
753 if (bUsePalette && image->HasAlpha() && !bHasMask)
754 {
755 /*
756 Only convert alpha to mask if saving as a palettised image was
757 explicitly requested. We don't want to lose alpha's precision
758 by converting to a mask just to be able to save palettised.
759 */
760 if (iColorType == wxPNG_TYPE_PALETTE
761 && temp_image.ConvertAlphaToMask())
762 {
763 image = &temp_image;
764 bHasMask = true;
765 bHasAlpha = image->HasAlpha();
766 }
767 else
768 {
769 bUsePalette = false;
770 iColorType = wxPNG_TYPE_COLOUR;
771 }
772 }
773 #else
774 bool bUsePalette = false;
775 #endif // wxUSE_PALETTE
776
777 bool bUseAlpha = !bUsePalette && (bHasAlpha || bHasMask);
778
779 png_color mask;
780 if (bHasMask)
781 {
782 mask.red = image->GetMaskRed();
783 mask.green = image->GetMaskGreen();
784 mask.blue = image->GetMaskBlue();
785 }
786
787
788 int iPngColorType;
789
790 #if wxUSE_PALETTE
791 if (bUsePalette)
792 {
793 iPngColorType = PNG_COLOR_TYPE_PALETTE;
794 iColorType = wxPNG_TYPE_PALETTE;
795 }
796 else
797 #endif // wxUSE_PALETTE
798 if ( iColorType==wxPNG_TYPE_COLOUR )
799 {
800 iPngColorType = bUseAlpha ? PNG_COLOR_TYPE_RGB_ALPHA
801 : PNG_COLOR_TYPE_RGB;
802 }
803 else
804 {
805 iPngColorType = bUseAlpha ? PNG_COLOR_TYPE_GRAY_ALPHA
806 : PNG_COLOR_TYPE_GRAY;
807 }
808
809 if (image->HasOption(wxIMAGE_OPTION_PNG_FILTER))
810 png_set_filter( png_ptr, PNG_FILTER_TYPE_BASE, image->GetOptionInt(wxIMAGE_OPTION_PNG_FILTER) );
811
812 if (image->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_LEVEL))
813 png_set_compression_level( png_ptr, image->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_LEVEL) );
814
815 if (image->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_MEM_LEVEL))
816 png_set_compression_mem_level( png_ptr, image->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_MEM_LEVEL) );
817
818 if (image->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_STRATEGY))
819 png_set_compression_strategy( png_ptr, image->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_STRATEGY) );
820
821 if (image->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_BUFFER_SIZE))
822 png_set_compression_buffer_size( png_ptr, image->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_BUFFER_SIZE) );
823
824 int iBitDepth = !bUsePalette && image->HasOption(wxIMAGE_OPTION_PNG_BITDEPTH)
825 ? image->GetOptionInt(wxIMAGE_OPTION_PNG_BITDEPTH)
826 : 8;
827
828 png_set_IHDR( png_ptr, info_ptr, image->GetWidth(), image->GetHeight(),
829 iBitDepth, iPngColorType,
830 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
831 PNG_FILTER_TYPE_BASE);
832
833 #if wxUSE_PALETTE
834 png_colorp palette = NULL;
835 int numPalette = 0;
836
837 if (bUsePalette)
838 {
839 const wxPalette& pal = image->GetPalette();
840 const int palCount = pal.GetColoursCount();
841 palette = (png_colorp) malloc(
842 (palCount + 1 /*headroom for trans */) * sizeof(png_color));
843
844 if (!palette)
845 {
846 png_destroy_write_struct( &png_ptr, (png_infopp)NULL );
847 if (verbose)
848 {
849 wxLogError(_("Couldn't save PNG image."));
850 }
851 return false;
852 }
853
854 for (int i = 0; i < palCount; ++i)
855 {
856 pal.GetRGB(i, &palette[i].red, &palette[i].green, &palette[i].blue);
857 }
858
859 numPalette = palCount;
860 if (bHasMask)
861 {
862 int index = PaletteFind(mask, palette, numPalette);
863
864 if (index)
865 {
866 if (index == wxNOT_FOUND)
867 {
868 numPalette++;
869 index = palCount;
870 palette[index] = mask;
871 }
872
873 wxSwap(palette[0], palette[index]);
874 }
875
876 png_byte trans = 0;
877 png_set_tRNS(png_ptr, info_ptr, &trans, 1, NULL);
878 }
879
880 png_set_PLTE(png_ptr, info_ptr, palette, numPalette);
881 free (palette);
882 palette = NULL;
883
884 // Let palette point to libpng's copy of the palette.
885 (void) png_get_PLTE(png_ptr, info_ptr, &palette, &numPalette);
886 }
887 #endif // wxUSE_PALETTE
888
889 int iElements;
890 png_color_8 sig_bit;
891
892 if ( iPngColorType & PNG_COLOR_MASK_COLOR )
893 {
894 sig_bit.red =
895 sig_bit.green =
896 sig_bit.blue = (png_byte)iBitDepth;
897 iElements = 3;
898 }
899 else // grey
900 {
901 sig_bit.gray = (png_byte)iBitDepth;
902 iElements = 1;
903 }
904
905 if ( iPngColorType & PNG_COLOR_MASK_ALPHA )
906 {
907 sig_bit.alpha = (png_byte)iBitDepth;
908 iElements++;
909 }
910
911 if ( iBitDepth == 16 )
912 iElements *= 2;
913
914 // save the image resolution if we have it
915 int resX, resY;
916 switch ( GetResolutionFromOptions(*image, &resX, &resY) )
917 {
918 case wxIMAGE_RESOLUTION_INCHES:
919 {
920 const double INCHES_IN_METER = 10000.0 / 254;
921 resX = int(resX * INCHES_IN_METER);
922 resY = int(resY * INCHES_IN_METER);
923 }
924 break;
925
926 case wxIMAGE_RESOLUTION_CM:
927 resX *= 100;
928 resY *= 100;
929 break;
930
931 case wxIMAGE_RESOLUTION_NONE:
932 break;
933
934 default:
935 wxFAIL_MSG( wxT("unsupported image resolution units") );
936 }
937
938 if ( resX && resY )
939 png_set_pHYs( png_ptr, info_ptr, resX, resY, PNG_RESOLUTION_METER );
940
941 png_set_sBIT( png_ptr, info_ptr, &sig_bit );
942 png_write_info( png_ptr, info_ptr );
943 png_set_shift( png_ptr, &sig_bit );
944 png_set_packing( png_ptr );
945
946 unsigned char *
947 data = (unsigned char *)malloc( image->GetWidth() * iElements );
948 if ( !data )
949 {
950 png_destroy_write_struct( &png_ptr, (png_infopp)NULL );
951 return false;
952 }
953
954 unsigned char *
955 pAlpha = (unsigned char *)(bHasAlpha ? image->GetAlpha() : NULL);
956 int iHeight = image->GetHeight();
957 int iWidth = image->GetWidth();
958
959 unsigned char *pColors = image->GetData();
960
961 for (int y = 0; y != iHeight; ++y)
962 {
963 unsigned char *pData = data;
964 for (int x = 0; x != iWidth; x++)
965 {
966 png_color clr;
967 clr.red = *pColors++;
968 clr.green = *pColors++;
969 clr.blue = *pColors++;
970
971 switch ( iColorType )
972 {
973 default:
974 wxFAIL_MSG( wxT("unknown wxPNG_TYPE_XXX") );
975 // fall through
976
977 case wxPNG_TYPE_COLOUR:
978 *pData++ = clr.red;
979 if ( iBitDepth == 16 )
980 *pData++ = 0;
981 *pData++ = clr.green;
982 if ( iBitDepth == 16 )
983 *pData++ = 0;
984 *pData++ = clr.blue;
985 if ( iBitDepth == 16 )
986 *pData++ = 0;
987 break;
988
989 case wxPNG_TYPE_GREY:
990 {
991 // where do these coefficients come from? maybe we
992 // should have image options for them as well?
993 unsigned uiColor =
994 (unsigned) (76.544*(unsigned)clr.red +
995 150.272*(unsigned)clr.green +
996 36.864*(unsigned)clr.blue);
997
998 *pData++ = (unsigned char)((uiColor >> 8) & 0xFF);
999 if ( iBitDepth == 16 )
1000 *pData++ = (unsigned char)(uiColor & 0xFF);
1001 }
1002 break;
1003
1004 case wxPNG_TYPE_GREY_RED:
1005 *pData++ = clr.red;
1006 if ( iBitDepth == 16 )
1007 *pData++ = 0;
1008 break;
1009
1010 #if wxUSE_PALETTE
1011 case wxPNG_TYPE_PALETTE:
1012 *pData++ = (unsigned char) PaletteFind(clr,
1013 palette, numPalette);
1014 break;
1015 #endif // wxUSE_PALETTE
1016 }
1017
1018 if ( bUseAlpha )
1019 {
1020 unsigned char uchAlpha = 255;
1021 if ( bHasAlpha )
1022 uchAlpha = *pAlpha++;
1023
1024 if ( bHasMask )
1025 {
1026 if ( (clr.red == mask.red)
1027 && (clr.green == mask.green)
1028 && (clr.blue == mask.blue) )
1029 uchAlpha = 0;
1030 }
1031
1032 *pData++ = uchAlpha;
1033 if ( iBitDepth == 16 )
1034 *pData++ = 0;
1035 }
1036 }
1037
1038 png_bytep row_ptr = data;
1039 png_write_rows( png_ptr, &row_ptr, 1 );
1040 }
1041
1042 free(data);
1043 png_write_end( png_ptr, info_ptr );
1044 png_destroy_write_struct( &png_ptr, (png_infopp)&info_ptr );
1045
1046 return true;
1047 }
1048
1049 #ifdef __VISUALC__
1050 #pragma warning(default:4611)
1051 #endif /* VC++ */
1052
1053 #endif // wxUSE_STREAMS
1054
1055 /*static*/ wxVersionInfo wxPNGHandler::GetLibraryVersionInfo()
1056 {
1057 // The version string seems to always have a leading space and a trailing
1058 // new line, get rid of them both.
1059 wxString str = png_get_header_version(NULL) + 1;
1060 str.Replace("\n", "");
1061
1062 return wxVersionInfo("libpng",
1063 PNG_LIBPNG_VER_MAJOR,
1064 PNG_LIBPNG_VER_MINOR,
1065 PNG_LIBPNG_VER_RELEASE,
1066 str);
1067 }
1068
1069 #endif // wxUSE_LIBPNG