1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/imagpng.cpp
3 // Purpose: wxImage PNG handler
4 // Author: Robert Roebling
5 // Copyright: (c) Robert Roebling
6 // Licence: wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
9 // ============================================================================
11 // ============================================================================
13 // ----------------------------------------------------------------------------
15 // ----------------------------------------------------------------------------
17 // For compilers that support precompilation, includes "wx.h".
18 #include "wx/wxprec.h"
24 #if wxUSE_IMAGE && wxUSE_LIBPNG
26 #include "wx/imagpng.h"
27 #include "wx/versioninfo.h"
32 #include "wx/palette.h"
33 #include "wx/stream.h"
41 // ----------------------------------------------------------------------------
43 // ----------------------------------------------------------------------------
45 // image cannot have any transparent pixels at all, have only 100% opaque
46 // and/or 100% transparent pixels in which case a simple mask is enough to
47 // store this information in wxImage or have a real alpha channel in which case
48 // we need to have it in wxImage as well
56 // ----------------------------------------------------------------------------
58 // ----------------------------------------------------------------------------
60 // return the kind of transparency needed for this image assuming that it does
61 // have transparent pixels, i.e. either Transparency_Alpha or Transparency_Mask
63 CheckTransparency(unsigned char **lines
,
64 png_uint_32 x
, png_uint_32 y
, png_uint_32 w
, png_uint_32 h
,
67 // init the alpha channel for the image and fill it with 1s up to (x, y)
68 static unsigned char *InitAlpha(wxImage
*image
, png_uint_32 x
, png_uint_32 y
);
70 // find a free colour for the mask in the PNG data array
72 FindMaskColour(unsigned char **lines
, png_uint_32 width
, png_uint_32 height
,
73 unsigned char& rMask
, unsigned char& gMask
, unsigned char& bMask
);
75 // is the pixel with this value of alpha a fully opaque one?
77 bool IsOpaque(unsigned char a
)
82 // is the pixel with this value of alpha a fully transparent one?
84 bool IsTransparent(unsigned char a
)
89 // ============================================================================
90 // wxPNGHandler implementation
91 // ============================================================================
93 IMPLEMENT_DYNAMIC_CLASS(wxPNGHandler
,wxImageHandler
)
97 #ifndef PNGLINKAGEMODE
99 #define PNGLINKAGEMODE PNGAPI
100 #elif defined(__WATCOMC__)
101 // we need an explicit cdecl for Watcom, at least according to
103 // http://sf.net/tracker/index.php?func=detail&aid=651492&group_id=9863&atid=109863
105 // more testing is needed for this however, please remove this comment
106 // if you can confirm that my fix works with Watcom 11
107 #define PNGLINKAGEMODE cdecl
109 #define PNGLINKAGEMODE LINKAGEMODE
114 // VS: wxPNGInfoStruct declared below is a hack that needs some explanation.
115 // First, let me describe what's the problem: libpng uses jmp_buf in
116 // its png_struct structure. Unfortunately, this structure is
117 // compiler-specific and may vary in size, so if you use libpng compiled
118 // as DLL with another compiler than the main executable, it may not work.
119 // Luckily, it is still possible to use setjmp() & longjmp() as long as the
120 // structure is not part of png_struct.
122 // Sadly, there's no clean way to attach user-defined data to png_struct.
123 // There is only one customizable place, png_struct.io_ptr, which is meant
124 // only for I/O routines and is set with png_set_read_fn or
125 // png_set_write_fn. The hacky part is that we use io_ptr to store
126 // a pointer to wxPNGInfoStruct that holds I/O structures _and_ jmp_buf.
128 struct wxPNGInfoStruct
140 #define WX_PNG_INFO(png_ptr) ((wxPNGInfoStruct*)png_get_io_ptr(png_ptr))
142 // ----------------------------------------------------------------------------
144 // ----------------------------------------------------------------------------
149 static void PNGLINKAGEMODE
wx_PNG_stream_reader( png_structp png_ptr
, png_bytep data
,
152 WX_PNG_INFO(png_ptr
)->stream
.in
->Read(data
, length
);
155 static void PNGLINKAGEMODE
wx_PNG_stream_writer( png_structp png_ptr
, png_bytep data
,
158 WX_PNG_INFO(png_ptr
)->stream
.out
->Write(data
, length
);
162 PNGLINKAGEMODE
wx_PNG_warning(png_structp png_ptr
, png_const_charp message
)
164 wxPNGInfoStruct
*info
= png_ptr
? WX_PNG_INFO(png_ptr
) : NULL
;
165 if ( !info
|| info
->verbose
)
167 wxLogWarning( wxString::FromAscii(message
) );
172 // so that the libpng doesn't send anything on stderr
174 PNGLINKAGEMODE
wx_PNG_error(png_structp png_ptr
, png_const_charp message
)
176 wx_PNG_warning(NULL
, message
);
178 // we're not using libpng built-in jump buffer (see comment before
179 // wxPNGInfoStruct above) so we have to return ourselves, otherwise libpng
181 longjmp(WX_PNG_INFO(png_ptr
)->jmpbuf
, 1);
186 // ----------------------------------------------------------------------------
187 // LoadFile() helpers
188 // ----------------------------------------------------------------------------
190 // determine the kind of transparency we need for this image: if the only alpha
191 // values it has are 0 (transparent) and 0xff (opaque) then we can simply
192 // create a mask for it, we should be ok with a simple mask but otherwise we
193 // need a full blown alpha channel in wxImage
196 // lines raw PNG data
197 // x, y starting position
198 // w, h size of the image
199 // numColBytes number of colour bytes (1 for grey scale, 3 for RGB)
200 // (NB: alpha always follows the colour bytes)
202 CheckTransparency(unsigned char **lines
,
203 png_uint_32 x
, png_uint_32 y
, png_uint_32 w
, png_uint_32 h
,
206 // suppose that a mask will suffice and check all the remaining alpha
207 // values to see if it does
210 // each pixel is numColBytes+1 bytes, offset into the current line by
211 // the current x position
212 unsigned const char *ptr
= lines
[y
] + (x
* (numColBytes
+ 1));
214 for ( png_uint_32 x2
= x
; x2
< w
; x2
++ )
216 // skip the grey or colour byte(s)
219 unsigned char a2
= *ptr
++;
221 if ( !IsTransparent(a2
) && !IsOpaque(a2
) )
223 // not fully opaque nor fully transparent, hence need alpha
224 return Transparency_Alpha
;
228 // during the next loop iteration check all the pixels in the row
232 // mask will be enough
233 return Transparency_Mask
;
236 unsigned char *InitAlpha(wxImage
*image
, png_uint_32 x
, png_uint_32 y
)
238 // create alpha channel
241 unsigned char *alpha
= image
->GetAlpha();
243 // set alpha for the pixels we had so far
244 png_uint_32 end
= y
* image
->GetWidth() + x
;
245 for ( png_uint_32 i
= 0; i
< end
; i
++ )
247 // all the previous pixels were opaque
255 FindMaskColour(unsigned char **lines
, png_uint_32 width
, png_uint_32 height
,
256 unsigned char& rMask
, unsigned char& gMask
, unsigned char& bMask
)
258 // choosing the colour for the mask is more
259 // difficult: we need to iterate over the entire
260 // image for this in order to choose an unused
261 // colour (this is not very efficient but what else
264 unsigned nentries
= 0;
265 unsigned char r2
, g2
, b2
;
266 for ( png_uint_32 y2
= 0; y2
< height
; y2
++ )
268 const unsigned char *p
= lines
[y2
];
269 for ( png_uint_32 x2
= 0; x2
< width
; x2
++ )
274 ++p
; // jump over alpha
276 wxImageHistogramEntry
&
277 entry
= h
[wxImageHistogram:: MakeKey(r2
, g2
, b2
)];
279 if ( entry
.value
++ == 0 )
280 entry
.index
= nentries
++;
284 if ( !h
.FindFirstUnusedColour(&rMask
, &gMask
, &bMask
) )
286 wxLogWarning(_("Too many colours in PNG, the image may be slightly blurred."));
288 // use a fixed mask colour and we'll fudge
289 // the real pixels with this colour (see
297 // ----------------------------------------------------------------------------
299 // ----------------------------------------------------------------------------
301 bool wxPNGHandler::DoCanRead( wxInputStream
& stream
)
303 unsigned char hdr
[4];
305 if ( !stream
.Read(hdr
, WXSIZEOF(hdr
)) ) // it's ok to modify the stream position here
308 return memcmp(hdr
, "\211PNG", WXSIZEOF(hdr
)) == 0;
311 // convert data from RGB to wxImage format
313 void CopyDataFromPNG(wxImage
*image
,
314 unsigned char **lines
,
319 Transparency transparency
= Transparency_None
;
321 // only non NULL if transparency == Transparency_Alpha
322 unsigned char *alpha
= NULL
;
324 // RGB of the mask colour if transparency == Transparency_Mask
325 // (but init them anyhow to avoid compiler warnings)
326 unsigned char rMask
= 0,
330 unsigned char *ptrDst
= image
->GetData();
331 if ( !(color_type
& PNG_COLOR_MASK_COLOR
) )
333 // grey image: GAGAGA... where G == grey component and A == alpha
334 for ( png_uint_32 y
= 0; y
< height
; y
++ )
336 const unsigned char *ptrSrc
= lines
[y
];
337 for ( png_uint_32 x
= 0; x
< width
; x
++ )
339 unsigned char g
= *ptrSrc
++;
340 unsigned char a
= *ptrSrc
++;
342 // the first time we encounter a transparent pixel we must
343 // decide about what to do about them
344 if ( !IsOpaque(a
) && transparency
== Transparency_None
)
346 // we'll need at least the mask for this image and
347 // maybe even full alpha channel info: the former is
348 // only enough if we have alpha values of 0 and 0xff
349 // only, otherwisewe need the latter
350 transparency
= CheckTransparency
358 if ( transparency
== Transparency_Mask
)
360 // let's choose this colour for the mask: this is
361 // not a problem here as all the other pixels are
362 // grey, i.e. R == G == B which is not the case for
363 // this one so no confusion is possible
368 else // transparency == Transparency_Alpha
370 alpha
= InitAlpha(image
, x
, y
);
374 switch ( transparency
)
376 case Transparency_Mask
:
377 if ( IsTransparent(a
) )
384 // else: !transparent
386 // must be opaque then as otherwise we shouldn't be
387 // using the mask at all
388 wxASSERT_MSG( IsOpaque(a
), wxT("logic error") );
392 case Transparency_Alpha
:
397 case Transparency_None
:
406 else // colour image: RGBRGB...
408 for ( png_uint_32 y
= 0; y
< height
; y
++ )
410 const unsigned char *ptrSrc
= lines
[y
];
411 for ( png_uint_32 x
= 0; x
< width
; x
++ )
413 unsigned char r
= *ptrSrc
++;
414 unsigned char g
= *ptrSrc
++;
415 unsigned char b
= *ptrSrc
++;
416 unsigned char a
= *ptrSrc
++;
418 // the logic here is the same as for the grey case except
420 if ( !IsOpaque(a
) && transparency
== Transparency_None
)
422 transparency
= CheckTransparency
430 if ( transparency
== Transparency_Mask
)
432 FindMaskColour(lines
, width
, height
,
433 rMask
, gMask
, bMask
);
435 else // transparency == Transparency_Alpha
437 alpha
= InitAlpha(image
, x
, y
);
442 switch ( transparency
)
444 case Transparency_Mask
:
445 if ( IsTransparent(a
) )
454 // must be opaque then as otherwise we shouldn't be
455 // using the mask at all
456 wxASSERT_MSG( IsOpaque(a
), wxT("logic error") );
458 // if we couldn't find a unique colour for the
459 // mask, we can have real pixels with the same
460 // value as the mask and it's better to slightly
461 // change their colour than to make them
463 if ( r
== rMask
&& g
== gMask
&& b
== bMask
)
471 case Transparency_Alpha
:
476 case Transparency_None
:
486 if ( transparency
== Transparency_Mask
)
488 image
->SetMaskColour(rMask
, gMask
, bMask
);
492 // temporarily disable the warning C4611 (interaction between '_setjmp' and
493 // C++ object destruction is non-portable) - I don't see any dtors here
495 #pragma warning(disable:4611)
499 wxPNGHandler::LoadFile(wxImage
*image
,
500 wxInputStream
& stream
,
504 // VZ: as this function uses setjmp() the only fool-proof error handling
505 // method is to use goto (setjmp is not really C++ dtors friendly...)
507 unsigned char **lines
= NULL
;
508 png_infop info_ptr
= (png_infop
) NULL
;
509 wxPNGInfoStruct wxinfo
;
511 png_uint_32 i
, width
, height
= 0;
512 int bit_depth
, color_type
, interlace_type
;
514 wxinfo
.verbose
= verbose
;
515 wxinfo
.stream
.in
= &stream
;
519 png_structp png_ptr
= png_create_read_struct
521 PNG_LIBPNG_VER_STRING
,
529 // NB: please see the comment near wxPNGInfoStruct declaration for
530 // explanation why this line is mandatory
531 png_set_read_fn( png_ptr
, &wxinfo
, wx_PNG_stream_reader
);
533 info_ptr
= png_create_info_struct( png_ptr
);
537 if (setjmp(wxinfo
.jmpbuf
))
540 png_read_info( png_ptr
, info_ptr
);
541 png_get_IHDR( png_ptr
, info_ptr
, &width
, &height
, &bit_depth
, &color_type
, &interlace_type
, NULL
, NULL
);
543 if (color_type
== PNG_COLOR_TYPE_PALETTE
)
544 png_set_expand( png_ptr
);
546 // Fix for Bug [ 439207 ] Monochrome PNG images come up black
548 png_set_expand( png_ptr
);
550 png_set_strip_16( png_ptr
);
551 png_set_packing( png_ptr
);
552 if (png_get_valid( png_ptr
, info_ptr
, PNG_INFO_tRNS
))
553 png_set_expand( png_ptr
);
554 png_set_filler( png_ptr
, 0xff, PNG_FILLER_AFTER
);
556 image
->Create((int)width
, (int)height
, (bool) false /* no need to init pixels */);
561 // initialize all line pointers to NULL to ensure that they can be safely
562 // free()d if an error occurs before all of them could be allocated
563 lines
= (unsigned char **)calloc(height
, sizeof(unsigned char *));
567 for (i
= 0; i
< height
; i
++)
569 if ((lines
[i
] = (unsigned char *)malloc( (size_t)(width
* 4))) == NULL
)
573 png_read_image( png_ptr
, lines
);
574 png_read_end( png_ptr
, info_ptr
);
577 if (color_type
== PNG_COLOR_TYPE_PALETTE
)
579 png_colorp palette
= NULL
;
582 (void) png_get_PLTE(png_ptr
, info_ptr
, &palette
, &numPalette
);
584 unsigned char* r
= new unsigned char[numPalette
];
585 unsigned char* g
= new unsigned char[numPalette
];
586 unsigned char* b
= new unsigned char[numPalette
];
588 for (int j
= 0; j
< numPalette
; j
++)
590 r
[j
] = palette
[j
].red
;
591 g
[j
] = palette
[j
].green
;
592 b
[j
] = palette
[j
].blue
;
595 image
->SetPalette(wxPalette(numPalette
, r
, g
, b
));
600 #endif // wxUSE_PALETTE
603 // set the image resolution if it's available
604 png_uint_32 resX
, resY
;
606 if (png_get_pHYs(png_ptr
, info_ptr
, &resX
, &resY
, &unitType
)
609 wxImageResolution res
= wxIMAGE_RESOLUTION_CM
;
614 wxLogWarning(_("Unknown PNG resolution unit %d"), unitType
);
617 case PNG_RESOLUTION_UNKNOWN
:
618 image
->SetOption(wxIMAGE_OPTION_RESOLUTIONX
, resX
);
619 image
->SetOption(wxIMAGE_OPTION_RESOLUTIONY
, resY
);
621 res
= wxIMAGE_RESOLUTION_NONE
;
624 case PNG_RESOLUTION_METER
:
626 Convert meters to centimeters.
627 Use a string to not lose precision (converting to cm and then
628 to inch would result in integer rounding error).
629 If an app wants an int, GetOptionInt will convert and round
632 image
->SetOption(wxIMAGE_OPTION_RESOLUTIONX
,
633 wxString::FromCDouble((double) resX
/ 100.0, 2));
634 image
->SetOption(wxIMAGE_OPTION_RESOLUTIONY
,
635 wxString::FromCDouble((double) resY
/ 100.0, 2));
639 image
->SetOption(wxIMAGE_OPTION_RESOLUTIONUNIT
, res
);
643 png_destroy_read_struct( &png_ptr
, &info_ptr
, (png_infopp
) NULL
);
645 // loaded successfully, now init wxImage with this data
646 CopyDataFromPNG(image
, lines
, width
, height
, color_type
);
648 for ( i
= 0; i
< height
; i
++ )
657 wxLogError(_("Couldn't load a PNG image - file is corrupted or not enough memory."));
667 for ( unsigned int n
= 0; n
< height
; n
++ )
677 png_destroy_read_struct( &png_ptr
, &info_ptr
, (png_infopp
) NULL
);
681 png_destroy_read_struct( &png_ptr
, (png_infopp
) NULL
, (png_infopp
) NULL
);
686 // ----------------------------------------------------------------------------
687 // SaveFile() palette helpers
688 // ----------------------------------------------------------------------------
690 typedef wxLongToLongHashMap PaletteMap
;
692 static unsigned long PaletteMakeKey(const png_color_8
& clr
)
694 return (wxImageHistogram::MakeKey(clr
.red
, clr
.green
, clr
.blue
) << 8) | clr
.alpha
;
697 static long PaletteFind(const PaletteMap
& palette
, const png_color_8
& clr
)
699 unsigned long value
= PaletteMakeKey(clr
);
700 PaletteMap::const_iterator it
= palette
.find(value
);
702 return (it
!= palette
.end()) ? it
->second
: wxNOT_FOUND
;
705 static long PaletteAdd(PaletteMap
*palette
, const png_color_8
& clr
)
707 unsigned long value
= PaletteMakeKey(clr
);
708 PaletteMap::const_iterator it
= palette
->find(value
);
711 if (it
== palette
->end())
713 index
= palette
->size();
714 (*palette
)[value
] = index
;
724 // ----------------------------------------------------------------------------
726 // ----------------------------------------------------------------------------
728 bool wxPNGHandler::SaveFile( wxImage
*image
, wxOutputStream
& stream
, bool verbose
)
730 wxPNGInfoStruct wxinfo
;
732 wxinfo
.verbose
= verbose
;
733 wxinfo
.stream
.out
= &stream
;
735 png_structp png_ptr
= png_create_write_struct
737 PNG_LIBPNG_VER_STRING
,
746 wxLogError(_("Couldn't save PNG image."));
751 png_infop info_ptr
= png_create_info_struct(png_ptr
);
752 if (info_ptr
== NULL
)
754 png_destroy_write_struct( &png_ptr
, (png_infopp
)NULL
);
757 wxLogError(_("Couldn't save PNG image."));
762 if (setjmp(wxinfo
.jmpbuf
))
764 png_destroy_write_struct( &png_ptr
, (png_infopp
)NULL
);
767 wxLogError(_("Couldn't save PNG image."));
772 // NB: please see the comment near wxPNGInfoStruct declaration for
773 // explanation why this line is mandatory
774 png_set_write_fn( png_ptr
, &wxinfo
, wx_PNG_stream_writer
, NULL
);
776 const int iHeight
= image
->GetHeight();
777 const int iWidth
= image
->GetWidth();
779 const bool bHasPngFormatOption
780 = image
->HasOption(wxIMAGE_OPTION_PNG_FORMAT
);
782 int iColorType
= bHasPngFormatOption
783 ? image
->GetOptionInt(wxIMAGE_OPTION_PNG_FORMAT
)
786 bool bHasAlpha
= image
->HasAlpha();
787 bool bHasMask
= image
->HasMask();
789 bool bUsePalette
= iColorType
== wxPNG_TYPE_PALETTE
791 || (!bHasPngFormatOption
&& image
->HasPalette() )
795 png_color_8 mask
= { 0, 0, 0, 0, 0 };
799 mask
.red
= image
->GetMaskRed();
800 mask
.green
= image
->GetMaskGreen();
801 mask
.blue
= image
->GetMaskBlue();
808 png_color png_rgb
[PNG_MAX_PALETTE_LENGTH
];
809 png_byte png_trans
[PNG_MAX_PALETTE_LENGTH
];
811 const unsigned char *pColors
= image
->GetData();
812 const unsigned char* pAlpha
= image
->GetAlpha();
814 if (bHasMask
&& !pAlpha
)
816 // Mask must be first
817 PaletteAdd(&palette
, mask
);
820 for (int y
= 0; y
< iHeight
; y
++)
822 for (int x
= 0; x
< iWidth
; x
++)
826 rgba
.red
= *pColors
++;
827 rgba
.green
= *pColors
++;
828 rgba
.blue
= *pColors
++;
830 rgba
.alpha
= (pAlpha
&& !bHasMask
) ? *pAlpha
++ : 0;
832 // save in our palette
833 long index
= PaletteAdd(&palette
, rgba
);
835 if (index
< PNG_MAX_PALETTE_LENGTH
)
837 // save in libpng's palette
838 png_rgb
[index
].red
= rgba
.red
;
839 png_rgb
[index
].green
= rgba
.green
;
840 png_rgb
[index
].blue
= rgba
.blue
;
841 png_trans
[index
] = rgba
.alpha
;
853 png_set_PLTE(png_ptr
, info_ptr
, png_rgb
, palette
.size());
855 if (bHasMask
&& !pAlpha
)
857 wxASSERT(PaletteFind(palette
, mask
) == 0);
859 png_set_tRNS(png_ptr
, info_ptr
, png_trans
, 1, NULL
);
861 else if (pAlpha
&& !bHasMask
)
863 png_set_tRNS(png_ptr
, info_ptr
, png_trans
, palette
.size(), NULL
);
869 If saving palettised was requested but it was decided we can't use a
870 palette then reset the colour type to RGB.
872 if (!bUsePalette
&& iColorType
== wxPNG_TYPE_PALETTE
)
874 iColorType
= wxPNG_TYPE_COLOUR
;
877 bool bUseAlpha
= !bUsePalette
&& (bHasAlpha
|| bHasMask
);
883 iPngColorType
= PNG_COLOR_TYPE_PALETTE
;
884 iColorType
= wxPNG_TYPE_PALETTE
;
886 else if ( iColorType
==wxPNG_TYPE_COLOUR
)
888 iPngColorType
= bUseAlpha
? PNG_COLOR_TYPE_RGB_ALPHA
889 : PNG_COLOR_TYPE_RGB
;
893 iPngColorType
= bUseAlpha
? PNG_COLOR_TYPE_GRAY_ALPHA
894 : PNG_COLOR_TYPE_GRAY
;
897 if (image
->HasOption(wxIMAGE_OPTION_PNG_FILTER
))
898 png_set_filter( png_ptr
, PNG_FILTER_TYPE_BASE
, image
->GetOptionInt(wxIMAGE_OPTION_PNG_FILTER
) );
900 if (image
->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_LEVEL
))
901 png_set_compression_level( png_ptr
, image
->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_LEVEL
) );
903 if (image
->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_MEM_LEVEL
))
904 png_set_compression_mem_level( png_ptr
, image
->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_MEM_LEVEL
) );
906 if (image
->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_STRATEGY
))
907 png_set_compression_strategy( png_ptr
, image
->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_STRATEGY
) );
909 if (image
->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_BUFFER_SIZE
))
910 png_set_compression_buffer_size( png_ptr
, image
->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_BUFFER_SIZE
) );
912 int iBitDepth
= !bUsePalette
&& image
->HasOption(wxIMAGE_OPTION_PNG_BITDEPTH
)
913 ? image
->GetOptionInt(wxIMAGE_OPTION_PNG_BITDEPTH
)
916 png_set_IHDR( png_ptr
, info_ptr
, image
->GetWidth(), image
->GetHeight(),
917 iBitDepth
, iPngColorType
,
918 PNG_INTERLACE_NONE
, PNG_COMPRESSION_TYPE_BASE
,
919 PNG_FILTER_TYPE_BASE
);
924 if ( iPngColorType
& PNG_COLOR_MASK_COLOR
)
928 sig_bit
.blue
= (png_byte
)iBitDepth
;
933 sig_bit
.gray
= (png_byte
)iBitDepth
;
939 sig_bit
.alpha
= (png_byte
)iBitDepth
;
943 if ( iBitDepth
== 16 )
946 // save the image resolution if we have it
948 switch ( GetResolutionFromOptions(*image
, &resX
, &resY
) )
950 case wxIMAGE_RESOLUTION_INCHES
:
952 const double INCHES_IN_METER
= 10000.0 / 254;
953 resX
= int(resX
* INCHES_IN_METER
);
954 resY
= int(resY
* INCHES_IN_METER
);
958 case wxIMAGE_RESOLUTION_CM
:
963 case wxIMAGE_RESOLUTION_NONE
:
967 wxFAIL_MSG( wxT("unsupported image resolution units") );
971 png_set_pHYs( png_ptr
, info_ptr
, resX
, resY
, PNG_RESOLUTION_METER
);
973 png_set_sBIT( png_ptr
, info_ptr
, &sig_bit
);
974 png_write_info( png_ptr
, info_ptr
);
975 png_set_shift( png_ptr
, &sig_bit
);
976 png_set_packing( png_ptr
);
979 data
= (unsigned char *)malloc( image
->GetWidth() * iElements
);
982 png_destroy_write_struct( &png_ptr
, (png_infopp
)NULL
);
986 const unsigned char *
987 pAlpha
= (const unsigned char *)(bHasAlpha
? image
->GetAlpha() : NULL
);
989 const unsigned char *pColors
= image
->GetData();
991 for (int y
= 0; y
!= iHeight
; ++y
)
993 unsigned char *pData
= data
;
994 for (int x
= 0; x
!= iWidth
; x
++)
997 clr
.red
= *pColors
++;
998 clr
.green
= *pColors
++;
999 clr
.blue
= *pColors
++;
1001 clr
.alpha
= (bUsePalette
&& pAlpha
) ? *pAlpha
++ : 0; // use with wxPNG_TYPE_PALETTE only
1003 switch ( iColorType
)
1006 wxFAIL_MSG( wxT("unknown wxPNG_TYPE_XXX") );
1009 case wxPNG_TYPE_COLOUR
:
1011 if ( iBitDepth
== 16 )
1013 *pData
++ = clr
.green
;
1014 if ( iBitDepth
== 16 )
1016 *pData
++ = clr
.blue
;
1017 if ( iBitDepth
== 16 )
1021 case wxPNG_TYPE_GREY
:
1023 // where do these coefficients come from? maybe we
1024 // should have image options for them as well?
1026 (unsigned) (76.544*(unsigned)clr
.red
+
1027 150.272*(unsigned)clr
.green
+
1028 36.864*(unsigned)clr
.blue
);
1030 *pData
++ = (unsigned char)((uiColor
>> 8) & 0xFF);
1031 if ( iBitDepth
== 16 )
1032 *pData
++ = (unsigned char)(uiColor
& 0xFF);
1036 case wxPNG_TYPE_GREY_RED
:
1038 if ( iBitDepth
== 16 )
1042 case wxPNG_TYPE_PALETTE
:
1043 *pData
++ = (unsigned char) PaletteFind(palette
, clr
);
1049 unsigned char uchAlpha
= 255;
1051 uchAlpha
= *pAlpha
++;
1055 if ( (clr
.red
== mask
.red
)
1056 && (clr
.green
== mask
.green
)
1057 && (clr
.blue
== mask
.blue
) )
1061 *pData
++ = uchAlpha
;
1062 if ( iBitDepth
== 16 )
1067 png_bytep row_ptr
= data
;
1068 png_write_rows( png_ptr
, &row_ptr
, 1 );
1072 png_write_end( png_ptr
, info_ptr
);
1073 png_destroy_write_struct( &png_ptr
, (png_infopp
)&info_ptr
);
1079 #pragma warning(default:4611)
1082 #endif // wxUSE_STREAMS
1084 /*static*/ wxVersionInfo
wxPNGHandler::GetLibraryVersionInfo()
1086 // The version string seems to always have a leading space and a trailing
1087 // new line, get rid of them both.
1088 wxString str
= png_get_header_version(NULL
) + 1;
1089 str
.Replace("\n", "");
1091 return wxVersionInfo("libpng",
1092 PNG_LIBPNG_VER_MAJOR
,
1093 PNG_LIBPNG_VER_MINOR
,
1094 PNG_LIBPNG_VER_RELEASE
,
1098 #endif // wxUSE_LIBPNG