1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/imagpng.cpp
3 // Purpose: wxImage PNG handler
4 // Author: Robert Roebling
6 // Copyright: (c) Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
10 // ============================================================================
12 // ============================================================================
14 // ----------------------------------------------------------------------------
16 // ----------------------------------------------------------------------------
18 // For compilers that support precompilation, includes "wx.h".
19 #include "wx/wxprec.h"
25 #if wxUSE_IMAGE && wxUSE_LIBPNG
27 #include "wx/imagpng.h"
28 #include "wx/versioninfo.h"
33 #include "wx/palette.h"
34 #include "wx/stream.h"
42 // ----------------------------------------------------------------------------
44 // ----------------------------------------------------------------------------
46 // image cannot 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
57 // ----------------------------------------------------------------------------
59 // ----------------------------------------------------------------------------
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
64 CheckTransparency(unsigned char **lines
,
65 png_uint_32 x
, png_uint_32 y
, png_uint_32 w
, png_uint_32 h
,
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
);
71 // find a free colour for the mask in the PNG data array
73 FindMaskColour(unsigned char **lines
, png_uint_32 width
, png_uint_32 height
,
74 unsigned char& rMask
, unsigned char& gMask
, unsigned char& bMask
);
76 // is the pixel with this value of alpha a fully opaque one?
78 bool IsOpaque(unsigned char a
)
83 // is the pixel with this value of alpha a fully transparent one?
85 bool IsTransparent(unsigned char a
)
90 // ============================================================================
91 // wxPNGHandler implementation
92 // ============================================================================
94 IMPLEMENT_DYNAMIC_CLASS(wxPNGHandler
,wxImageHandler
)
98 #ifndef PNGLINKAGEMODE
100 #define PNGLINKAGEMODE PNGAPI
101 #elif defined(__WATCOMC__)
102 // we need an explicit cdecl for Watcom, at least according to
104 // http://sf.net/tracker/index.php?func=detail&aid=651492&group_id=9863&atid=109863
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
110 #define PNGLINKAGEMODE LINKAGEMODE
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 // Luckily, it is still possible to use setjmp() & longjmp() as long as the
121 // structure is not part of png_struct.
123 // Sadly, there's no clean way to attach user-defined data to png_struct.
124 // There is only one customizable place, png_struct.io_ptr, which is meant
125 // only for I/O routines and is set with png_set_read_fn or
126 // png_set_write_fn. The hacky part is that we use io_ptr to store
127 // a pointer to wxPNGInfoStruct that holds I/O structures _and_ jmp_buf.
129 struct wxPNGInfoStruct
141 #define WX_PNG_INFO(png_ptr) ((wxPNGInfoStruct*)png_get_io_ptr(png_ptr))
143 // ----------------------------------------------------------------------------
145 // ----------------------------------------------------------------------------
150 static void PNGLINKAGEMODE
wx_PNG_stream_reader( png_structp png_ptr
, png_bytep data
,
153 WX_PNG_INFO(png_ptr
)->stream
.in
->Read(data
, length
);
156 static void PNGLINKAGEMODE
wx_PNG_stream_writer( png_structp png_ptr
, png_bytep data
,
159 WX_PNG_INFO(png_ptr
)->stream
.out
->Write(data
, length
);
163 PNGLINKAGEMODE
wx_png_warning(png_structp png_ptr
, png_const_charp message
)
165 wxPNGInfoStruct
*info
= png_ptr
? WX_PNG_INFO(png_ptr
) : NULL
;
166 if ( !info
|| info
->verbose
)
168 wxLogWarning( wxString::FromAscii(message
) );
173 // so that the libpng doesn't send anything on stderr
175 PNGLINKAGEMODE
wx_png_error(png_structp png_ptr
, png_const_charp message
)
177 wx_png_warning(NULL
, message
);
179 // we're not using libpng built-in jump buffer (see comment before
180 // wxPNGInfoStruct above) so we have to return ourselves, otherwise libpng
182 longjmp(WX_PNG_INFO(png_ptr
)->jmpbuf
, 1);
187 // ----------------------------------------------------------------------------
188 // LoadFile() helpers
189 // ----------------------------------------------------------------------------
191 // determine the kind of transparency we need for this image: if the only alpha
192 // values it has are 0 (transparent) and 0xff (opaque) then we can simply
193 // create a mask for it, we should be ok with a simple mask but otherwise we
194 // need a full blown alpha channel in wxImage
197 // lines raw PNG data
198 // x, y starting position
199 // w, h size of the image
200 // numColBytes number of colour bytes (1 for grey scale, 3 for RGB)
201 // (NB: alpha always follows the colour bytes)
203 CheckTransparency(unsigned char **lines
,
204 png_uint_32 x
, png_uint_32 y
, png_uint_32 w
, png_uint_32 h
,
207 // suppose that a mask will suffice and check all the remaining alpha
208 // values to see if it does
211 // each pixel is numColBytes+1 bytes, offset into the current line by
212 // the current x position
213 unsigned const char *ptr
= lines
[y
] + (x
* (numColBytes
+ 1));
215 for ( png_uint_32 x2
= x
; x2
< w
; x2
++ )
217 // skip the grey or colour byte(s)
220 unsigned char a2
= *ptr
++;
222 if ( !IsTransparent(a2
) && !IsOpaque(a2
) )
224 // not fully opaque nor fully transparent, hence need alpha
225 return Transparency_Alpha
;
229 // during the next loop iteration check all the pixels in the row
233 // mask will be enough
234 return Transparency_Mask
;
237 unsigned char *InitAlpha(wxImage
*image
, png_uint_32 x
, png_uint_32 y
)
239 // create alpha channel
242 unsigned char *alpha
= image
->GetAlpha();
244 // set alpha for the pixels we had so far
245 png_uint_32 end
= y
* image
->GetWidth() + x
;
246 for ( png_uint_32 i
= 0; i
< end
; i
++ )
248 // all the previous pixels were opaque
256 FindMaskColour(unsigned char **lines
, png_uint_32 width
, png_uint_32 height
,
257 unsigned char& rMask
, unsigned char& gMask
, unsigned char& bMask
)
259 // choosing the colour for the mask is more
260 // difficult: we need to iterate over the entire
261 // image for this in order to choose an unused
262 // colour (this is not very efficient but what else
265 unsigned nentries
= 0;
266 unsigned char r2
, g2
, b2
;
267 for ( png_uint_32 y2
= 0; y2
< height
; y2
++ )
269 const unsigned char *p
= lines
[y2
];
270 for ( png_uint_32 x2
= 0; x2
< width
; x2
++ )
275 ++p
; // jump over alpha
277 wxImageHistogramEntry
&
278 entry
= h
[wxImageHistogram:: MakeKey(r2
, g2
, b2
)];
280 if ( entry
.value
++ == 0 )
281 entry
.index
= nentries
++;
285 if ( !h
.FindFirstUnusedColour(&rMask
, &gMask
, &bMask
) )
287 wxLogWarning(_("Too many colours in PNG, the image may be slightly blurred."));
289 // use a fixed mask colour and we'll fudge
290 // the real pixels with this colour (see
298 // ----------------------------------------------------------------------------
300 // ----------------------------------------------------------------------------
302 bool wxPNGHandler::DoCanRead( wxInputStream
& stream
)
304 unsigned char hdr
[4];
306 if ( !stream
.Read(hdr
, WXSIZEOF(hdr
)) ) // it's ok to modify the stream position here
309 return memcmp(hdr
, "\211PNG", WXSIZEOF(hdr
)) == 0;
312 // convert data from RGB to wxImage format
314 void CopyDataFromPNG(wxImage
*image
,
315 unsigned char **lines
,
320 Transparency transparency
= Transparency_None
;
322 // only non NULL if transparency == Transparency_Alpha
323 unsigned char *alpha
= NULL
;
325 // RGB of the mask colour if transparency == Transparency_Mask
326 // (but init them anyhow to avoid compiler warnings)
327 unsigned char rMask
= 0,
331 unsigned char *ptrDst
= image
->GetData();
332 if ( !(color_type
& PNG_COLOR_MASK_COLOR
) )
334 // grey image: GAGAGA... where G == grey component and A == alpha
335 for ( png_uint_32 y
= 0; y
< height
; y
++ )
337 const unsigned char *ptrSrc
= lines
[y
];
338 for ( png_uint_32 x
= 0; x
< width
; x
++ )
340 unsigned char g
= *ptrSrc
++;
341 unsigned char a
= *ptrSrc
++;
343 // the first time we encounter a transparent pixel we must
344 // decide about what to do about them
345 if ( !IsOpaque(a
) && transparency
== Transparency_None
)
347 // we'll need at least the mask for this image and
348 // maybe even full alpha channel info: the former is
349 // only enough if we have alpha values of 0 and 0xff
350 // only, otherwisewe need the latter
351 transparency
= CheckTransparency
359 if ( transparency
== Transparency_Mask
)
361 // let's choose this colour for the mask: this is
362 // not a problem here as all the other pixels are
363 // grey, i.e. R == G == B which is not the case for
364 // this one so no confusion is possible
369 else // transparency == Transparency_Alpha
371 alpha
= InitAlpha(image
, x
, y
);
375 switch ( transparency
)
377 case Transparency_Mask
:
378 if ( IsTransparent(a
) )
385 // else: !transparent
387 // must be opaque then as otherwise we shouldn't be
388 // using the mask at all
389 wxASSERT_MSG( IsOpaque(a
), wxT("logic error") );
393 case Transparency_Alpha
:
398 case Transparency_None
:
407 else // colour image: RGBRGB...
409 for ( png_uint_32 y
= 0; y
< height
; y
++ )
411 const unsigned char *ptrSrc
= lines
[y
];
412 for ( png_uint_32 x
= 0; x
< width
; x
++ )
414 unsigned char r
= *ptrSrc
++;
415 unsigned char g
= *ptrSrc
++;
416 unsigned char b
= *ptrSrc
++;
417 unsigned char a
= *ptrSrc
++;
419 // the logic here is the same as for the grey case except
421 if ( !IsOpaque(a
) && transparency
== Transparency_None
)
423 transparency
= CheckTransparency
431 if ( transparency
== Transparency_Mask
)
433 FindMaskColour(lines
, width
, height
,
434 rMask
, gMask
, bMask
);
436 else // transparency == Transparency_Alpha
438 alpha
= InitAlpha(image
, x
, y
);
443 switch ( transparency
)
445 case Transparency_Mask
:
446 if ( IsTransparent(a
) )
455 // must be opaque then as otherwise we shouldn't be
456 // using the mask at all
457 wxASSERT_MSG( IsOpaque(a
), wxT("logic error") );
459 // if we couldn't find a unique colour for the
460 // mask, we can have real pixels with the same
461 // value as the mask and it's better to slightly
462 // change their colour than to make them
464 if ( r
== rMask
&& g
== gMask
&& b
== bMask
)
472 case Transparency_Alpha
:
477 case Transparency_None
:
487 if ( transparency
== Transparency_Mask
)
489 image
->SetMaskColour(rMask
, gMask
, bMask
);
493 // temporarily disable the warning C4611 (interaction between '_setjmp' and
494 // C++ object destruction is non-portable) - I don't see any dtors here
496 #pragma warning(disable:4611)
500 wxPNGHandler::LoadFile(wxImage
*image
,
501 wxInputStream
& stream
,
505 // VZ: as this function uses setjmp() the only fool-proof error handling
506 // method is to use goto (setjmp is not really C++ dtors friendly...)
508 unsigned char **lines
= NULL
;
509 png_infop info_ptr
= (png_infop
) NULL
;
510 wxPNGInfoStruct wxinfo
;
512 png_uint_32 i
, width
, height
= 0;
513 int bit_depth
, color_type
, interlace_type
;
515 wxinfo
.verbose
= verbose
;
516 wxinfo
.stream
.in
= &stream
;
520 png_structp png_ptr
= png_create_read_struct
522 PNG_LIBPNG_VER_STRING
,
530 // NB: please see the comment near wxPNGInfoStruct declaration for
531 // explanation why this line is mandatory
532 png_set_read_fn( png_ptr
, &wxinfo
, wx_PNG_stream_reader
);
534 info_ptr
= png_create_info_struct( png_ptr
);
538 if (setjmp(wxinfo
.jmpbuf
))
541 png_read_info( png_ptr
, info_ptr
);
542 png_get_IHDR( png_ptr
, info_ptr
, &width
, &height
, &bit_depth
, &color_type
, &interlace_type
, NULL
, NULL
);
544 if (color_type
== PNG_COLOR_TYPE_PALETTE
)
545 png_set_expand( png_ptr
);
547 // Fix for Bug [ 439207 ] Monochrome PNG images come up black
549 png_set_expand( png_ptr
);
551 png_set_strip_16( png_ptr
);
552 png_set_packing( png_ptr
);
553 if (png_get_valid( png_ptr
, info_ptr
, PNG_INFO_tRNS
))
554 png_set_expand( png_ptr
);
555 png_set_filler( png_ptr
, 0xff, PNG_FILLER_AFTER
);
557 image
->Create((int)width
, (int)height
, (bool) false /* no need to init pixels */);
562 // initialize all line pointers to NULL to ensure that they can be safely
563 // free()d if an error occurs before all of them could be allocated
564 lines
= (unsigned char **)calloc(height
, sizeof(unsigned char *));
568 for (i
= 0; i
< height
; i
++)
570 if ((lines
[i
] = (unsigned char *)malloc( (size_t)(width
* (sizeof(unsigned char) * 4)))) == NULL
)
574 png_read_image( png_ptr
, lines
);
575 png_read_end( png_ptr
, info_ptr
);
578 if (color_type
== PNG_COLOR_TYPE_PALETTE
)
580 png_colorp palette
= NULL
;
583 (void) png_get_PLTE(png_ptr
, info_ptr
, &palette
, &numPalette
);
585 unsigned char* r
= new unsigned char[numPalette
];
586 unsigned char* g
= new unsigned char[numPalette
];
587 unsigned char* b
= new unsigned char[numPalette
];
589 for (int j
= 0; j
< numPalette
; j
++)
591 r
[j
] = palette
[j
].red
;
592 g
[j
] = palette
[j
].green
;
593 b
[j
] = palette
[j
].blue
;
596 image
->SetPalette(wxPalette(numPalette
, r
, g
, b
));
601 #endif // wxUSE_PALETTE
604 // set the image resolution if it's available
605 png_uint_32 resX
, resY
;
607 if (png_get_pHYs(png_ptr
, info_ptr
, &resX
, &resY
, &unitType
)
610 wxImageResolution res
= wxIMAGE_RESOLUTION_CM
;
615 wxLogWarning(_("Unknown PNG resolution unit %d"), unitType
);
618 case PNG_RESOLUTION_UNKNOWN
:
619 image
->SetOption(wxIMAGE_OPTION_RESOLUTIONX
, resX
);
620 image
->SetOption(wxIMAGE_OPTION_RESOLUTIONY
, resY
);
622 res
= wxIMAGE_RESOLUTION_NONE
;
625 case PNG_RESOLUTION_METER
:
627 Convert meters to centimeters.
628 Use a string to not lose precision (converting to cm and then
629 to inch would result in integer rounding error).
630 If an app wants an int, GetOptionInt will convert and round
633 image
->SetOption(wxIMAGE_OPTION_RESOLUTIONX
,
634 wxString::FromCDouble((double) resX
/ 100.0, 2));
635 image
->SetOption(wxIMAGE_OPTION_RESOLUTIONY
,
636 wxString::FromCDouble((double) resY
/ 100.0, 2));
640 image
->SetOption(wxIMAGE_OPTION_RESOLUTIONUNIT
, res
);
644 png_destroy_read_struct( &png_ptr
, &info_ptr
, (png_infopp
) NULL
);
646 // loaded successfully, now init wxImage with this data
647 CopyDataFromPNG(image
, lines
, width
, height
, color_type
);
649 for ( i
= 0; i
< height
; i
++ )
658 wxLogError(_("Couldn't load a PNG image - file is corrupted or not enough memory."));
668 for ( unsigned int n
= 0; n
< height
; n
++ )
678 png_destroy_read_struct( &png_ptr
, &info_ptr
, (png_infopp
) NULL
);
682 png_destroy_read_struct( &png_ptr
, (png_infopp
) NULL
, (png_infopp
) NULL
);
687 // ----------------------------------------------------------------------------
688 // SaveFile() palette helpers
689 // ----------------------------------------------------------------------------
691 typedef wxLongToLongHashMap PaletteMap
;
693 static unsigned long PaletteMakeKey(const png_color_8
& clr
)
695 return (wxImageHistogram::MakeKey(clr
.red
, clr
.green
, clr
.blue
) << 8) | clr
.alpha
;
698 static long PaletteFind(const PaletteMap
& palette
, const png_color_8
& clr
)
700 unsigned long value
= PaletteMakeKey(clr
);
701 PaletteMap::const_iterator it
= palette
.find(value
);
703 return (it
!= palette
.end()) ? it
->second
: wxNOT_FOUND
;
706 static long PaletteAdd(PaletteMap
*palette
, const png_color_8
& clr
)
708 unsigned long value
= PaletteMakeKey(clr
);
709 PaletteMap::const_iterator it
= palette
->find(value
);
712 if (it
== palette
->end())
714 index
= palette
->size();
715 (*palette
)[value
] = index
;
725 // ----------------------------------------------------------------------------
727 // ----------------------------------------------------------------------------
729 bool wxPNGHandler::SaveFile( wxImage
*image
, wxOutputStream
& stream
, bool verbose
)
731 wxPNGInfoStruct wxinfo
;
733 wxinfo
.verbose
= verbose
;
734 wxinfo
.stream
.out
= &stream
;
736 png_structp png_ptr
= png_create_write_struct
738 PNG_LIBPNG_VER_STRING
,
747 wxLogError(_("Couldn't save PNG image."));
752 png_infop info_ptr
= png_create_info_struct(png_ptr
);
753 if (info_ptr
== NULL
)
755 png_destroy_write_struct( &png_ptr
, (png_infopp
)NULL
);
758 wxLogError(_("Couldn't save PNG image."));
763 if (setjmp(wxinfo
.jmpbuf
))
765 png_destroy_write_struct( &png_ptr
, (png_infopp
)NULL
);
768 wxLogError(_("Couldn't save PNG image."));
773 // NB: please see the comment near wxPNGInfoStruct declaration for
774 // explanation why this line is mandatory
775 png_set_write_fn( png_ptr
, &wxinfo
, wx_PNG_stream_writer
, NULL
);
777 const int iHeight
= image
->GetHeight();
778 const int iWidth
= image
->GetWidth();
780 const bool bHasPngFormatOption
781 = image
->HasOption(wxIMAGE_OPTION_PNG_FORMAT
);
783 int iColorType
= bHasPngFormatOption
784 ? image
->GetOptionInt(wxIMAGE_OPTION_PNG_FORMAT
)
787 bool bHasAlpha
= image
->HasAlpha();
788 bool bHasMask
= image
->HasMask();
790 bool bUsePalette
= iColorType
== wxPNG_TYPE_PALETTE
792 || (!bHasPngFormatOption
&& image
->HasPalette() )
796 png_color_8 mask
= { 0, 0, 0, 0, 0 };
800 mask
.red
= image
->GetMaskRed();
801 mask
.green
= image
->GetMaskGreen();
802 mask
.blue
= image
->GetMaskBlue();
809 png_color png_rgb
[PNG_MAX_PALETTE_LENGTH
];
810 png_byte png_trans
[PNG_MAX_PALETTE_LENGTH
];
812 const unsigned char *pColors
= image
->GetData();
813 const unsigned char* pAlpha
= image
->GetAlpha();
815 if (bHasMask
&& !pAlpha
)
817 // Mask must be first
818 PaletteAdd(&palette
, mask
);
821 for (int y
= 0; y
< iHeight
; y
++)
823 for (int x
= 0; x
< iWidth
; x
++)
827 rgba
.red
= *pColors
++;
828 rgba
.green
= *pColors
++;
829 rgba
.blue
= *pColors
++;
831 rgba
.alpha
= (pAlpha
&& !bHasMask
) ? *pAlpha
++ : 0;
833 // save in our palette
834 long index
= PaletteAdd(&palette
, rgba
);
836 if (index
< PNG_MAX_PALETTE_LENGTH
)
838 // save in libpng's palette
839 png_rgb
[index
].red
= rgba
.red
;
840 png_rgb
[index
].green
= rgba
.green
;
841 png_rgb
[index
].blue
= rgba
.blue
;
842 png_trans
[index
] = rgba
.alpha
;
854 png_set_PLTE(png_ptr
, info_ptr
, png_rgb
, palette
.size());
856 if (bHasMask
&& !pAlpha
)
858 wxASSERT(PaletteFind(palette
, mask
) == 0);
860 png_set_tRNS(png_ptr
, info_ptr
, png_trans
, 1, NULL
);
862 else if (pAlpha
&& !bHasMask
)
864 png_set_tRNS(png_ptr
, info_ptr
, png_trans
, palette
.size(), NULL
);
870 If saving palettised was requested but it was decided we can't use a
871 palette then reset the colour type to RGB.
873 if (!bUsePalette
&& iColorType
== wxPNG_TYPE_PALETTE
)
875 iColorType
= wxPNG_TYPE_COLOUR
;
878 bool bUseAlpha
= !bUsePalette
&& (bHasAlpha
|| bHasMask
);
884 iPngColorType
= PNG_COLOR_TYPE_PALETTE
;
885 iColorType
= wxPNG_TYPE_PALETTE
;
887 else if ( iColorType
==wxPNG_TYPE_COLOUR
)
889 iPngColorType
= bUseAlpha
? PNG_COLOR_TYPE_RGB_ALPHA
890 : PNG_COLOR_TYPE_RGB
;
894 iPngColorType
= bUseAlpha
? PNG_COLOR_TYPE_GRAY_ALPHA
895 : PNG_COLOR_TYPE_GRAY
;
898 if (image
->HasOption(wxIMAGE_OPTION_PNG_FILTER
))
899 png_set_filter( png_ptr
, PNG_FILTER_TYPE_BASE
, image
->GetOptionInt(wxIMAGE_OPTION_PNG_FILTER
) );
901 if (image
->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_LEVEL
))
902 png_set_compression_level( png_ptr
, image
->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_LEVEL
) );
904 if (image
->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_MEM_LEVEL
))
905 png_set_compression_mem_level( png_ptr
, image
->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_MEM_LEVEL
) );
907 if (image
->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_STRATEGY
))
908 png_set_compression_strategy( png_ptr
, image
->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_STRATEGY
) );
910 if (image
->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_BUFFER_SIZE
))
911 png_set_compression_buffer_size( png_ptr
, image
->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_BUFFER_SIZE
) );
913 int iBitDepth
= !bUsePalette
&& image
->HasOption(wxIMAGE_OPTION_PNG_BITDEPTH
)
914 ? image
->GetOptionInt(wxIMAGE_OPTION_PNG_BITDEPTH
)
917 png_set_IHDR( png_ptr
, info_ptr
, image
->GetWidth(), image
->GetHeight(),
918 iBitDepth
, iPngColorType
,
919 PNG_INTERLACE_NONE
, PNG_COMPRESSION_TYPE_BASE
,
920 PNG_FILTER_TYPE_BASE
);
925 if ( iPngColorType
& PNG_COLOR_MASK_COLOR
)
929 sig_bit
.blue
= (png_byte
)iBitDepth
;
934 sig_bit
.gray
= (png_byte
)iBitDepth
;
940 sig_bit
.alpha
= (png_byte
)iBitDepth
;
944 if ( iBitDepth
== 16 )
947 // save the image resolution if we have it
949 switch ( GetResolutionFromOptions(*image
, &resX
, &resY
) )
951 case wxIMAGE_RESOLUTION_INCHES
:
953 const double INCHES_IN_METER
= 10000.0 / 254;
954 resX
= int(resX
* INCHES_IN_METER
);
955 resY
= int(resY
* INCHES_IN_METER
);
959 case wxIMAGE_RESOLUTION_CM
:
964 case wxIMAGE_RESOLUTION_NONE
:
968 wxFAIL_MSG( wxT("unsupported image resolution units") );
972 png_set_pHYs( png_ptr
, info_ptr
, resX
, resY
, PNG_RESOLUTION_METER
);
974 png_set_sBIT( png_ptr
, info_ptr
, &sig_bit
);
975 png_write_info( png_ptr
, info_ptr
);
976 png_set_shift( png_ptr
, &sig_bit
);
977 png_set_packing( png_ptr
);
980 data
= (unsigned char *)malloc( image
->GetWidth() * iElements
);
983 png_destroy_write_struct( &png_ptr
, (png_infopp
)NULL
);
987 const unsigned char *
988 pAlpha
= (const unsigned char *)(bHasAlpha
? image
->GetAlpha() : NULL
);
990 const unsigned char *pColors
= image
->GetData();
992 for (int y
= 0; y
!= iHeight
; ++y
)
994 unsigned char *pData
= data
;
995 for (int x
= 0; x
!= iWidth
; x
++)
998 clr
.red
= *pColors
++;
999 clr
.green
= *pColors
++;
1000 clr
.blue
= *pColors
++;
1002 clr
.alpha
= (bUsePalette
&& pAlpha
) ? *pAlpha
++ : 0; // use with wxPNG_TYPE_PALETTE only
1004 switch ( iColorType
)
1007 wxFAIL_MSG( wxT("unknown wxPNG_TYPE_XXX") );
1010 case wxPNG_TYPE_COLOUR
:
1012 if ( iBitDepth
== 16 )
1014 *pData
++ = clr
.green
;
1015 if ( iBitDepth
== 16 )
1017 *pData
++ = clr
.blue
;
1018 if ( iBitDepth
== 16 )
1022 case wxPNG_TYPE_GREY
:
1024 // where do these coefficients come from? maybe we
1025 // should have image options for them as well?
1027 (unsigned) (76.544*(unsigned)clr
.red
+
1028 150.272*(unsigned)clr
.green
+
1029 36.864*(unsigned)clr
.blue
);
1031 *pData
++ = (unsigned char)((uiColor
>> 8) & 0xFF);
1032 if ( iBitDepth
== 16 )
1033 *pData
++ = (unsigned char)(uiColor
& 0xFF);
1037 case wxPNG_TYPE_GREY_RED
:
1039 if ( iBitDepth
== 16 )
1043 case wxPNG_TYPE_PALETTE
:
1044 *pData
++ = (unsigned char) PaletteFind(palette
, clr
);
1050 unsigned char uchAlpha
= 255;
1052 uchAlpha
= *pAlpha
++;
1056 if ( (clr
.red
== mask
.red
)
1057 && (clr
.green
== mask
.green
)
1058 && (clr
.blue
== mask
.blue
) )
1062 *pData
++ = uchAlpha
;
1063 if ( iBitDepth
== 16 )
1068 png_bytep row_ptr
= data
;
1069 png_write_rows( png_ptr
, &row_ptr
, 1 );
1073 png_write_end( png_ptr
, info_ptr
);
1074 png_destroy_write_struct( &png_ptr
, (png_infopp
)&info_ptr
);
1080 #pragma warning(default:4611)
1083 #endif // wxUSE_STREAMS
1085 /*static*/ wxVersionInfo
wxPNGHandler::GetLibraryVersionInfo()
1087 // The version string seems to always have a leading space and a trailing
1088 // new line, get rid of them both.
1089 wxString str
= png_get_header_version(NULL
) + 1;
1090 str
.Replace("\n", "");
1092 return wxVersionInfo("libpng",
1093 PNG_LIBPNG_VER_MAJOR
,
1094 PNG_LIBPNG_VER_MINOR
,
1095 PNG_LIBPNG_VER_RELEASE
,
1099 #endif // wxUSE_LIBPNG