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 // (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.
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.
131 struct wxPNGInfoStruct
143 #define WX_PNG_INFO(png_ptr) ((wxPNGInfoStruct*)png_get_io_ptr(png_ptr))
145 // ----------------------------------------------------------------------------
147 // ----------------------------------------------------------------------------
152 static void PNGLINKAGEMODE
wx_PNG_stream_reader( png_structp png_ptr
, png_bytep data
,
155 WX_PNG_INFO(png_ptr
)->stream
.in
->Read(data
, length
);
158 static void PNGLINKAGEMODE
wx_PNG_stream_writer( png_structp png_ptr
, png_bytep data
,
161 WX_PNG_INFO(png_ptr
)->stream
.out
->Write(data
, length
);
165 PNGLINKAGEMODE
wx_png_warning(png_structp png_ptr
, png_const_charp message
)
167 wxPNGInfoStruct
*info
= png_ptr
? WX_PNG_INFO(png_ptr
) : NULL
;
168 if ( !info
|| info
->verbose
)
170 wxLogWarning( wxString::FromAscii(message
) );
175 // so that the libpng doesn't send anything on stderr
177 PNGLINKAGEMODE
wx_png_error(png_structp png_ptr
, png_const_charp message
)
179 wx_png_warning(NULL
, message
);
181 // we're not using libpng built-in jump buffer (see comment before
182 // wxPNGInfoStruct above) so we have to return ourselves, otherwise libpng
184 longjmp(WX_PNG_INFO(png_ptr
)->jmpbuf
, 1);
189 // ----------------------------------------------------------------------------
190 // LoadFile() helpers
191 // ----------------------------------------------------------------------------
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
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)
205 CheckTransparency(unsigned char **lines
,
206 png_uint_32 x
, png_uint_32 y
, png_uint_32 w
, png_uint_32 h
,
209 // suppose that a mask will suffice and check all the remaining alpha
210 // values to see if it does
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));
217 for ( png_uint_32 x2
= x
; x2
< w
; x2
++ )
219 // skip the grey or colour byte(s)
222 unsigned char a2
= *ptr
++;
224 if ( !IsTransparent(a2
) && !IsOpaque(a2
) )
226 // not fully opaque nor fully transparent, hence need alpha
227 return Transparency_Alpha
;
231 // during the next loop iteration check all the pixels in the row
235 // mask will be enough
236 return Transparency_Mask
;
239 unsigned char *InitAlpha(wxImage
*image
, png_uint_32 x
, png_uint_32 y
)
241 // create alpha channel
244 unsigned char *alpha
= image
->GetAlpha();
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
++ )
250 // all the previous pixels were opaque
258 FindMaskColour(unsigned char **lines
, png_uint_32 width
, png_uint_32 height
,
259 unsigned char& rMask
, unsigned char& gMask
, unsigned char& bMask
)
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
267 unsigned nentries
= 0;
268 unsigned char r2
, g2
, b2
;
269 for ( png_uint_32 y2
= 0; y2
< height
; y2
++ )
271 const unsigned char *p
= lines
[y2
];
272 for ( png_uint_32 x2
= 0; x2
< width
; x2
++ )
277 ++p
; // jump over alpha
279 wxImageHistogramEntry
&
280 entry
= h
[wxImageHistogram:: MakeKey(r2
, g2
, b2
)];
282 if ( entry
.value
++ == 0 )
283 entry
.index
= nentries
++;
287 if ( !h
.FindFirstUnusedColour(&rMask
, &gMask
, &bMask
) )
289 wxLogWarning(_("Too many colours in PNG, the image may be slightly blurred."));
291 // use a fixed mask colour and we'll fudge
292 // the real pixels with this colour (see
300 // ----------------------------------------------------------------------------
302 // ----------------------------------------------------------------------------
304 bool wxPNGHandler::DoCanRead( wxInputStream
& stream
)
306 unsigned char hdr
[4];
308 if ( !stream
.Read(hdr
, WXSIZEOF(hdr
)) ) // it's ok to modify the stream position here
311 return memcmp(hdr
, "\211PNG", WXSIZEOF(hdr
)) == 0;
314 // convert data from RGB to wxImage format
316 void CopyDataFromPNG(wxImage
*image
,
317 unsigned char **lines
,
322 Transparency transparency
= Transparency_None
;
324 // only non NULL if transparency == Transparency_Alpha
325 unsigned char *alpha
= NULL
;
327 // RGB of the mask colour if transparency == Transparency_Mask
328 // (but init them anyhow to avoid compiler warnings)
329 unsigned char rMask
= 0,
333 unsigned char *ptrDst
= image
->GetData();
334 if ( !(color_type
& PNG_COLOR_MASK_COLOR
) )
336 // grey image: GAGAGA... where G == grey component and A == alpha
337 for ( png_uint_32 y
= 0; y
< height
; y
++ )
339 const unsigned char *ptrSrc
= lines
[y
];
340 for ( png_uint_32 x
= 0; x
< width
; x
++ )
342 unsigned char g
= *ptrSrc
++;
343 unsigned char a
= *ptrSrc
++;
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
)
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
361 if ( transparency
== Transparency_Mask
)
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
371 else // transparency == Transparency_Alpha
373 alpha
= InitAlpha(image
, x
, y
);
377 switch ( transparency
)
379 case Transparency_Mask
:
380 if ( IsTransparent(a
) )
387 // else: !transparent
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") );
395 case Transparency_Alpha
:
400 case Transparency_None
:
409 else // colour image: RGBRGB...
411 for ( png_uint_32 y
= 0; y
< height
; y
++ )
413 const unsigned char *ptrSrc
= lines
[y
];
414 for ( png_uint_32 x
= 0; x
< width
; x
++ )
416 unsigned char r
= *ptrSrc
++;
417 unsigned char g
= *ptrSrc
++;
418 unsigned char b
= *ptrSrc
++;
419 unsigned char a
= *ptrSrc
++;
421 // the logic here is the same as for the grey case except
423 if ( !IsOpaque(a
) && transparency
== Transparency_None
)
425 transparency
= CheckTransparency
433 if ( transparency
== Transparency_Mask
)
435 FindMaskColour(lines
, width
, height
,
436 rMask
, gMask
, bMask
);
438 else // transparency == Transparency_Alpha
440 alpha
= InitAlpha(image
, x
, y
);
445 switch ( transparency
)
447 case Transparency_Mask
:
448 if ( IsTransparent(a
) )
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") );
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
466 if ( r
== rMask
&& g
== gMask
&& b
== bMask
)
474 case Transparency_Alpha
:
479 case Transparency_None
:
489 if ( transparency
== Transparency_Mask
)
491 image
->SetMaskColour(rMask
, gMask
, bMask
);
495 // temporarily disable the warning C4611 (interaction between '_setjmp' and
496 // C++ object destruction is non-portable) - I don't see any dtors here
498 #pragma warning(disable:4611)
502 wxPNGHandler::LoadFile(wxImage
*image
,
503 wxInputStream
& stream
,
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...)
510 unsigned char **lines
= NULL
;
511 png_infop info_ptr
= (png_infop
) NULL
;
512 wxPNGInfoStruct wxinfo
;
514 png_uint_32 i
, width
, height
= 0;
515 int bit_depth
, color_type
, interlace_type
;
517 wxinfo
.verbose
= verbose
;
518 wxinfo
.stream
.in
= &stream
;
522 png_structp png_ptr
= png_create_read_struct
524 PNG_LIBPNG_VER_STRING
,
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
);
536 info_ptr
= png_create_info_struct( png_ptr
);
540 if (setjmp(wxinfo
.jmpbuf
))
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
);
546 if (color_type
== PNG_COLOR_TYPE_PALETTE
)
547 png_set_expand( png_ptr
);
549 // Fix for Bug [ 439207 ] Monochrome PNG images come up black
551 png_set_expand( png_ptr
);
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
);
559 image
->Create((int)width
, (int)height
, (bool) false /* no need to init pixels */);
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 *));
570 for (i
= 0; i
< height
; i
++)
572 if ((lines
[i
] = (unsigned char *)malloc( (size_t)(width
* (sizeof(unsigned char) * 4)))) == NULL
)
576 png_read_image( png_ptr
, lines
);
577 png_read_end( png_ptr
, info_ptr
);
580 if (color_type
== PNG_COLOR_TYPE_PALETTE
)
582 png_colorp palette
= NULL
;
585 (void) png_get_PLTE(png_ptr
, info_ptr
, &palette
, &numPalette
);
587 unsigned char* r
= new unsigned char[numPalette
];
588 unsigned char* g
= new unsigned char[numPalette
];
589 unsigned char* b
= new unsigned char[numPalette
];
591 for (int j
= 0; j
< numPalette
; j
++)
593 r
[j
] = palette
[j
].red
;
594 g
[j
] = palette
[j
].green
;
595 b
[j
] = palette
[j
].blue
;
598 image
->SetPalette(wxPalette(numPalette
, r
, g
, b
));
603 #endif // wxUSE_PALETTE
606 // set the image resolution if it's available
607 png_uint_32 resX
, resY
;
609 if (png_get_pHYs(png_ptr
, info_ptr
, &resX
, &resY
, &unitType
)
612 wxImageResolution res
= wxIMAGE_RESOLUTION_CM
;
617 wxLogWarning(_("Unknown PNG resolution unit %d"), unitType
);
620 case PNG_RESOLUTION_UNKNOWN
:
621 image
->SetOption(wxIMAGE_OPTION_RESOLUTIONX
, resX
);
622 image
->SetOption(wxIMAGE_OPTION_RESOLUTIONY
, resY
);
624 res
= wxIMAGE_RESOLUTION_NONE
;
627 case PNG_RESOLUTION_METER
:
629 Convert meters to centimeters.
630 Use a string to not lose precision (converting to cm and then
631 to inch would result in integer rounding error).
632 If an app wants an int, GetOptionInt will convert and round
635 image
->SetOption(wxIMAGE_OPTION_RESOLUTIONX
,
636 wxString::FromCDouble((double) resX
/ 100.0, 2));
637 image
->SetOption(wxIMAGE_OPTION_RESOLUTIONY
,
638 wxString::FromCDouble((double) resY
/ 100.0, 2));
642 image
->SetOption(wxIMAGE_OPTION_RESOLUTIONUNIT
, res
);
646 png_destroy_read_struct( &png_ptr
, &info_ptr
, (png_infopp
) NULL
);
648 // loaded successfully, now init wxImage with this data
649 CopyDataFromPNG(image
, lines
, width
, height
, color_type
);
651 for ( i
= 0; i
< height
; i
++ )
660 wxLogError(_("Couldn't load a PNG image - file is corrupted or not enough memory."));
670 for ( unsigned int n
= 0; n
< height
; n
++ )
680 png_destroy_read_struct( &png_ptr
, &info_ptr
, (png_infopp
) NULL
);
684 png_destroy_read_struct( &png_ptr
, (png_infopp
) NULL
, (png_infopp
) NULL
);
689 // ----------------------------------------------------------------------------
690 // SaveFile() palette helpers
691 // ----------------------------------------------------------------------------
693 typedef wxLongToLongHashMap PaletteMap
;
695 static unsigned long PaletteMakeKey(const png_color_8
& clr
)
697 return (wxImageHistogram::MakeKey(clr
.red
, clr
.green
, clr
.blue
) << 8) | clr
.alpha
;
700 static long PaletteFind(const PaletteMap
& palette
, const png_color_8
& clr
)
702 unsigned long value
= PaletteMakeKey(clr
);
703 PaletteMap::const_iterator it
= palette
.find(value
);
705 return (it
!= palette
.end()) ? it
->second
: wxNOT_FOUND
;
708 static long PaletteAdd(PaletteMap
*palette
, const png_color_8
& clr
)
710 unsigned long value
= PaletteMakeKey(clr
);
711 PaletteMap::const_iterator it
= palette
->find(value
);
714 if (it
== palette
->end())
716 index
= palette
->size();
717 (*palette
)[value
] = index
;
727 // ----------------------------------------------------------------------------
729 // ----------------------------------------------------------------------------
731 bool wxPNGHandler::SaveFile( wxImage
*image
, wxOutputStream
& stream
, bool verbose
)
733 wxPNGInfoStruct wxinfo
;
735 wxinfo
.verbose
= verbose
;
736 wxinfo
.stream
.out
= &stream
;
738 png_structp png_ptr
= png_create_write_struct
740 PNG_LIBPNG_VER_STRING
,
749 wxLogError(_("Couldn't save PNG image."));
754 png_infop info_ptr
= png_create_info_struct(png_ptr
);
755 if (info_ptr
== NULL
)
757 png_destroy_write_struct( &png_ptr
, (png_infopp
)NULL
);
760 wxLogError(_("Couldn't save PNG image."));
765 if (setjmp(wxinfo
.jmpbuf
))
767 png_destroy_write_struct( &png_ptr
, (png_infopp
)NULL
);
770 wxLogError(_("Couldn't save PNG image."));
775 // NB: please see the comment near wxPNGInfoStruct declaration for
776 // explanation why this line is mandatory
777 png_set_write_fn( png_ptr
, &wxinfo
, wx_PNG_stream_writer
, NULL
);
779 const int iHeight
= image
->GetHeight();
780 const int iWidth
= image
->GetWidth();
782 const bool bHasPngFormatOption
783 = image
->HasOption(wxIMAGE_OPTION_PNG_FORMAT
);
785 int iColorType
= bHasPngFormatOption
786 ? image
->GetOptionInt(wxIMAGE_OPTION_PNG_FORMAT
)
789 bool bHasAlpha
= image
->HasAlpha();
790 bool bHasMask
= image
->HasMask();
792 bool bUsePalette
= iColorType
== wxPNG_TYPE_PALETTE
794 || (!bHasPngFormatOption
&& image
->HasPalette() )
798 png_color_8 mask
= { 0, 0, 0, 0, 0 };
802 mask
.red
= image
->GetMaskRed();
803 mask
.green
= image
->GetMaskGreen();
804 mask
.blue
= image
->GetMaskBlue();
811 png_color png_rgb
[PNG_MAX_PALETTE_LENGTH
];
812 png_byte png_trans
[PNG_MAX_PALETTE_LENGTH
];
814 const unsigned char *pColors
= image
->GetData();
815 const unsigned char* pAlpha
= image
->GetAlpha();
817 if (bHasMask
&& !pAlpha
)
819 // Mask must be first
820 PaletteAdd(&palette
, mask
);
823 for (int y
= 0; y
< iHeight
; y
++)
825 for (int x
= 0; x
< iWidth
; x
++)
829 rgba
.red
= *pColors
++;
830 rgba
.green
= *pColors
++;
831 rgba
.blue
= *pColors
++;
833 rgba
.alpha
= (pAlpha
&& !bHasMask
) ? *pAlpha
++ : 0;
835 // save in our palette
836 long index
= PaletteAdd(&palette
, rgba
);
838 if (index
< PNG_MAX_PALETTE_LENGTH
)
840 // save in libpng's palette
841 png_rgb
[index
].red
= rgba
.red
;
842 png_rgb
[index
].green
= rgba
.green
;
843 png_rgb
[index
].blue
= rgba
.blue
;
844 png_trans
[index
] = rgba
.alpha
;
856 png_set_PLTE(png_ptr
, info_ptr
, png_rgb
, palette
.size());
858 if (bHasMask
&& !pAlpha
)
860 wxASSERT(PaletteFind(palette
, mask
) == 0);
862 png_set_tRNS(png_ptr
, info_ptr
, png_trans
, 1, NULL
);
864 else if (pAlpha
&& !bHasMask
)
866 png_set_tRNS(png_ptr
, info_ptr
, png_trans
, palette
.size(), NULL
);
872 If saving palettised was requested but it was decided we can't use a
873 palette then reset the colour type to RGB.
875 if (!bUsePalette
&& iColorType
== wxPNG_TYPE_PALETTE
)
877 iColorType
= wxPNG_TYPE_COLOUR
;
880 bool bUseAlpha
= !bUsePalette
&& (bHasAlpha
|| bHasMask
);
886 iPngColorType
= PNG_COLOR_TYPE_PALETTE
;
887 iColorType
= wxPNG_TYPE_PALETTE
;
889 else if ( iColorType
==wxPNG_TYPE_COLOUR
)
891 iPngColorType
= bUseAlpha
? PNG_COLOR_TYPE_RGB_ALPHA
892 : PNG_COLOR_TYPE_RGB
;
896 iPngColorType
= bUseAlpha
? PNG_COLOR_TYPE_GRAY_ALPHA
897 : PNG_COLOR_TYPE_GRAY
;
900 if (image
->HasOption(wxIMAGE_OPTION_PNG_FILTER
))
901 png_set_filter( png_ptr
, PNG_FILTER_TYPE_BASE
, image
->GetOptionInt(wxIMAGE_OPTION_PNG_FILTER
) );
903 if (image
->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_LEVEL
))
904 png_set_compression_level( png_ptr
, image
->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_LEVEL
) );
906 if (image
->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_MEM_LEVEL
))
907 png_set_compression_mem_level( png_ptr
, image
->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_MEM_LEVEL
) );
909 if (image
->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_STRATEGY
))
910 png_set_compression_strategy( png_ptr
, image
->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_STRATEGY
) );
912 if (image
->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_BUFFER_SIZE
))
913 png_set_compression_buffer_size( png_ptr
, image
->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_BUFFER_SIZE
) );
915 int iBitDepth
= !bUsePalette
&& image
->HasOption(wxIMAGE_OPTION_PNG_BITDEPTH
)
916 ? image
->GetOptionInt(wxIMAGE_OPTION_PNG_BITDEPTH
)
919 png_set_IHDR( png_ptr
, info_ptr
, image
->GetWidth(), image
->GetHeight(),
920 iBitDepth
, iPngColorType
,
921 PNG_INTERLACE_NONE
, PNG_COMPRESSION_TYPE_BASE
,
922 PNG_FILTER_TYPE_BASE
);
927 if ( iPngColorType
& PNG_COLOR_MASK_COLOR
)
931 sig_bit
.blue
= (png_byte
)iBitDepth
;
936 sig_bit
.gray
= (png_byte
)iBitDepth
;
942 sig_bit
.alpha
= (png_byte
)iBitDepth
;
946 if ( iBitDepth
== 16 )
949 // save the image resolution if we have it
951 switch ( GetResolutionFromOptions(*image
, &resX
, &resY
) )
953 case wxIMAGE_RESOLUTION_INCHES
:
955 const double INCHES_IN_METER
= 10000.0 / 254;
956 resX
= int(resX
* INCHES_IN_METER
);
957 resY
= int(resY
* INCHES_IN_METER
);
961 case wxIMAGE_RESOLUTION_CM
:
966 case wxIMAGE_RESOLUTION_NONE
:
970 wxFAIL_MSG( wxT("unsupported image resolution units") );
974 png_set_pHYs( png_ptr
, info_ptr
, resX
, resY
, PNG_RESOLUTION_METER
);
976 png_set_sBIT( png_ptr
, info_ptr
, &sig_bit
);
977 png_write_info( png_ptr
, info_ptr
);
978 png_set_shift( png_ptr
, &sig_bit
);
979 png_set_packing( png_ptr
);
982 data
= (unsigned char *)malloc( image
->GetWidth() * iElements
);
985 png_destroy_write_struct( &png_ptr
, (png_infopp
)NULL
);
989 const unsigned char *
990 pAlpha
= (const unsigned char *)(bHasAlpha
? image
->GetAlpha() : NULL
);
992 const unsigned char *pColors
= image
->GetData();
994 for (int y
= 0; y
!= iHeight
; ++y
)
996 unsigned char *pData
= data
;
997 for (int x
= 0; x
!= iWidth
; x
++)
1000 clr
.red
= *pColors
++;
1001 clr
.green
= *pColors
++;
1002 clr
.blue
= *pColors
++;
1004 clr
.alpha
= (bUsePalette
&& pAlpha
) ? *pAlpha
++ : 0; // use with wxPNG_TYPE_PALETTE only
1006 switch ( iColorType
)
1009 wxFAIL_MSG( wxT("unknown wxPNG_TYPE_XXX") );
1012 case wxPNG_TYPE_COLOUR
:
1014 if ( iBitDepth
== 16 )
1016 *pData
++ = clr
.green
;
1017 if ( iBitDepth
== 16 )
1019 *pData
++ = clr
.blue
;
1020 if ( iBitDepth
== 16 )
1024 case wxPNG_TYPE_GREY
:
1026 // where do these coefficients come from? maybe we
1027 // should have image options for them as well?
1029 (unsigned) (76.544*(unsigned)clr
.red
+
1030 150.272*(unsigned)clr
.green
+
1031 36.864*(unsigned)clr
.blue
);
1033 *pData
++ = (unsigned char)((uiColor
>> 8) & 0xFF);
1034 if ( iBitDepth
== 16 )
1035 *pData
++ = (unsigned char)(uiColor
& 0xFF);
1039 case wxPNG_TYPE_GREY_RED
:
1041 if ( iBitDepth
== 16 )
1045 case wxPNG_TYPE_PALETTE
:
1046 *pData
++ = (unsigned char) PaletteFind(palette
, clr
);
1052 unsigned char uchAlpha
= 255;
1054 uchAlpha
= *pAlpha
++;
1058 if ( (clr
.red
== mask
.red
)
1059 && (clr
.green
== mask
.green
)
1060 && (clr
.blue
== mask
.blue
) )
1064 *pData
++ = uchAlpha
;
1065 if ( iBitDepth
== 16 )
1070 png_bytep row_ptr
= data
;
1071 png_write_rows( png_ptr
, &row_ptr
, 1 );
1075 png_write_end( png_ptr
, info_ptr
);
1076 png_destroy_write_struct( &png_ptr
, (png_infopp
)&info_ptr
);
1082 #pragma warning(default:4611)
1085 #endif // wxUSE_STREAMS
1087 /*static*/ wxVersionInfo
wxPNGHandler::GetLibraryVersionInfo()
1089 // The version string seems to always have a leading space and a trailing
1090 // new line, get rid of them both.
1091 wxString str
= png_get_header_version(NULL
) + 1;
1092 str
.Replace("\n", "");
1094 return wxVersionInfo("libpng",
1095 PNG_LIBPNG_VER_MAJOR
,
1096 PNG_LIBPNG_VER_MINOR
,
1097 PNG_LIBPNG_VER_RELEASE
,
1101 #endif // wxUSE_LIBPNG