1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/common/imagepng.cpp 
   3 // Purpose:     wxImage PNG handler 
   4 // Author:      Robert Roebling 
   6 // Copyright:   (c) Robert Roebling 
   7 // Licence:     wxWindows licence 
   8 ///////////////////////////////////////////////////////////////////////////// 
  10 // ============================================================================ 
  12 // ============================================================================ 
  14 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) 
  15 #pragma implementation "imagpng.h" 
  18 // ---------------------------------------------------------------------------- 
  20 // ---------------------------------------------------------------------------- 
  22 // For compilers that support precompilation, includes "wx.h". 
  23 #include "wx/wxprec.h" 
  33 #if wxUSE_IMAGE && wxUSE_LIBPNG 
  35 #include "wx/imagpng.h" 
  36 #include "wx/bitmap.h" 
  41 #include "wx/filefn.h" 
  42 #include "wx/wfstream.h" 
  44 #include "wx/module.h" 
  55 // ---------------------------------------------------------------------------- 
  57 // ---------------------------------------------------------------------------- 
  59 // image can not have any transparent pixels at all, have only 100% opaque 
  60 // and/or 100% transparent pixels in which case a simple mask is enough to 
  61 // store this information in wxImage or have a real alpha channel in which case 
  62 // we need to have it in wxImage as well 
  70 // ---------------------------------------------------------------------------- 
  72 // ---------------------------------------------------------------------------- 
  74 // return the kind of transparency needed for this image assuming that it does 
  75 // have transparent pixels, i.e. either Transparency_Alpha or Transparency_Mask 
  77 CheckTransparency(unsigned char **lines
, 
  78                   png_uint_32 x
, png_uint_32 y
, png_uint_32 w
, png_uint_32 h
, 
  81 // init the alpha channel for the image and fill it with 1s up to (x, y) 
  82 static unsigned char *InitAlpha(wxImage 
*image
, png_uint_32 x
, png_uint_32 y
); 
  84 // find a free colour for the mask in the PNG data array 
  86 FindMaskColour(unsigned char **lines
, png_uint_32 width
, png_uint_32 height
, 
  87                unsigned char& rMask
, unsigned char& gMask
, unsigned char& bMask
); 
  89 // is the pixel with this value of alpha a fully opaque one? 
  91 bool IsOpaque(unsigned char a
) 
  96 // is the pixel with this value of alpha a fully transparent one? 
  98 bool IsTransparent(unsigned char a
) 
 103 // ============================================================================ 
 104 // wxPNGHandler implementation 
 105 // ============================================================================ 
 107 IMPLEMENT_DYNAMIC_CLASS(wxPNGHandler
,wxImageHandler
) 
 111 #ifndef PNGLINKAGEMODE 
 113         // we need an explicit cdecl for Watcom, at least according to 
 115         // http://sf.net/tracker/index.php?func=detail&aid=651492&group_id=9863&atid=109863 
 117         // more testing is needed for this however, please remove this comment 
 118         // if you can confirm that my fix works with Watcom 11 
 119         #define PNGLINKAGEMODE cdecl 
 121         #define PNGLINKAGEMODE LINKAGEMODE 
 126 // VS: wxPNGInfoStruct declared below is a hack that needs some explanation. 
 127 //     First, let me describe what's the problem: libpng uses jmp_buf in 
 128 //     its png_struct structure. Unfortunately, this structure is 
 129 //     compiler-specific and may vary in size, so if you use libpng compiled 
 130 //     as DLL with another compiler than the main executable, it may not work 
 131 //     (this is for example the case with wxMGL port and SciTech MGL library 
 132 //     that provides custom runtime-loadable libpng implementation with jmpbuf 
 133 //     disabled altogether). Luckily, it is still possible to use setjmp() & 
 134 //     longjmp() as long as the structure is not part of png_struct. 
 136 //     Sadly, there's no clean way to attach user-defined data to png_struct. 
 137 //     There is only one customizable place, png_struct.io_ptr, which is meant 
 138 //     only for I/O routines and is set with png_set_read_fn or 
 139 //     png_set_write_fn. The hacky part is that we use io_ptr to store 
 140 //     a pointer to wxPNGInfoStruct that holds I/O structures _and_ jmp_buf. 
 142 struct wxPNGInfoStruct
 
 154 #define WX_PNG_INFO(png_ptr) ((wxPNGInfoStruct*)png_get_io_ptr(png_ptr)) 
 156 // ---------------------------------------------------------------------------- 
 158 // ---------------------------------------------------------------------------- 
 163 void PNGLINKAGEMODE 
_PNG_stream_reader( png_structp png_ptr
, png_bytep data
, png_size_t length 
) 
 165     WX_PNG_INFO(png_ptr
)->stream
.in
->Read(data
, length
); 
 168 void PNGLINKAGEMODE 
_PNG_stream_writer( png_structp png_ptr
, png_bytep data
, png_size_t length 
) 
 170     WX_PNG_INFO(png_ptr
)->stream
.out
->Write(data
, length
); 
 174 // so that the libpng doesn't send anything on stderr 
 176 PNGLINKAGEMODE 
wx_png_error(png_structp png_ptr
, png_const_charp message
) 
 178     wxPNGInfoStruct 
*info 
= WX_PNG_INFO(png_ptr
); 
 180         wxLogError( wxString::FromAscii(message
) ); 
 182 #ifdef USE_FAR_KEYWORD 
 185        png_memcpy(jmpbuf
,info
->jmpbuf
,sizeof(jmp_buf)); 
 189     longjmp(info
->jmpbuf
, 1); 
 194 PNGLINKAGEMODE 
wx_png_warning(png_structp png_ptr
, png_const_charp message
) 
 196     wxPNGInfoStruct 
*info 
= WX_PNG_INFO(png_ptr
); 
 198         wxLogWarning( wxString::FromAscii(message
) ); 
 203 // ---------------------------------------------------------------------------- 
 204 // LoadFile() helpers 
 205 // ---------------------------------------------------------------------------- 
 207 // determine the kind of transparency we need for this image: if the only alpha 
 208 // values it has are 0 (transparent) and 0xff (opaque) then we can simply 
 209 // create a mask for it, we should be ok with a simple mask but otherwise we 
 210 // need a full blown alpha channel in wxImage 
 213 //      lines           raw PNG data 
 214 //      x, y            starting position 
 215 //      w, h            size of the image 
 216 //      numColBytes     number of colour bytes (1 for grey scale, 3 for RGB) 
 217 //                      (NB: alpha always follows the colour bytes) 
 219 CheckTransparency(unsigned char **lines
, 
 220                   png_uint_32 x
, png_uint_32 y
, png_uint_32 w
, png_uint_32 h
, 
 223     // suppose that a mask will suffice and check all the remaining alpha 
 224     // values to see if it does 
 227         // each pixel is numColBytes+1 bytes, offset into the current line by 
 228         // the current x position 
 229         unsigned const char *ptr 
= lines
[y
] + (x 
* (numColBytes 
+ 1)); 
 231         for ( png_uint_32 x2 
= x
; x2 
< w
; x2
++ ) 
 233             // skip the grey or colour byte(s) 
 236             unsigned char a2 
= *ptr
++; 
 238             if ( !IsTransparent(a2
) && !IsOpaque(a2
) ) 
 240                 // not fully opaque nor fully transparent, hence need alpha 
 241                 return Transparency_Alpha
; 
 245         // during the next loop iteration check all the pixels in the row 
 249     // mask will be enough 
 250     return Transparency_Mask
; 
 253 unsigned char *InitAlpha(wxImage 
*image
, png_uint_32 x
, png_uint_32 y
) 
 255     // create alpha channel 
 258     unsigned char *alpha 
= image
->GetAlpha(); 
 260     // set alpha for the pixels we had so far 
 261     png_uint_32 end 
= y 
* image
->GetWidth() + x
; 
 262     for ( png_uint_32 i 
= 0; i 
< end
; i
++ ) 
 264         // all the previous pixels were opaque 
 272 FindMaskColour(unsigned char **lines
, png_uint_32 width
, png_uint_32 height
, 
 273                unsigned char& rMask
, unsigned char& gMask
, unsigned char& bMask
) 
 275     // choosing the colour for the mask is more 
 276     // difficult: we need to iterate over the entire 
 277     // image for this in order to choose an unused 
 278     // colour (this is not very efficient but what else 
 281     unsigned nentries 
= 0; 
 282     unsigned char r2
, g2
, b2
; 
 283     for ( png_uint_32 y2 
= 0; y2 
< height
; y2
++ ) 
 285         const unsigned char *p 
= lines
[y2
]; 
 286         for ( png_uint_32 x2 
= 0; x2 
< width
; x2
++ ) 
 292             wxImageHistogramEntry
& 
 293                 entry 
= h
[wxImageHistogram:: MakeKey(r2
, g2
, b2
)]; 
 295             if ( entry
.value
++ == 0 ) 
 296                 entry
.index 
= nentries
++; 
 300     if ( !h
.FindFirstUnusedColour(&rMask
, &gMask
, &bMask
) ) 
 302         wxLogWarning(_("Too many colours in PNG, the image may be slightly blurred.")); 
 304         // use a fixed mask colour and we'll fudge 
 305         // the real pixels with this colour (see 
 313 // ---------------------------------------------------------------------------- 
 315 // ---------------------------------------------------------------------------- 
 317 bool wxPNGHandler::DoCanRead( wxInputStream
& stream 
) 
 319     unsigned char hdr
[4]; 
 321     if ( !stream
.Read(hdr
, WXSIZEOF(hdr
)) ) 
 324     return memcmp(hdr
, "\211PNG", WXSIZEOF(hdr
)) == 0; 
 327 // convert data from RGB to wxImage format 
 329 void CopyDataFromPNG(wxImage 
*image
, 
 330                      unsigned char **lines
, 
 335     Transparency transparency 
= Transparency_None
; 
 337     // only non NULL if transparency == Transparency_Alpha 
 338     unsigned char *alpha 
= NULL
; 
 340     // RGB of the mask colour if transparency == Transparency_Mask 
 341     // (but init them anyhow to avoid compiler warnings) 
 342     unsigned char rMask 
= 0, 
 346     unsigned char *ptrDst 
= image
->GetData(); 
 347     if ( !(color_type 
& PNG_COLOR_MASK_COLOR
) ) 
 349         // grey image: GAGAGA... where G == grey component and A == alpha 
 350         for ( png_uint_32 y 
= 0; y 
< height
; y
++ ) 
 352             const unsigned char *ptrSrc 
= lines
[y
]; 
 353             for ( png_uint_32 x 
= 0; x 
< width
; x
++ ) 
 355                 unsigned char g 
= *ptrSrc
++; 
 356                 unsigned char a 
= *ptrSrc
++; 
 358                 // the first time we encounter a transparent pixel we must 
 359                 // decide about what to do about them 
 360                 if ( !IsOpaque(a
) && transparency 
== Transparency_None 
) 
 362                     // we'll need at least the mask for this image and 
 363                     // maybe even full alpha channel info: the former is 
 364                     // only enough if we have alpha values of 0 and 0xff 
 365                     // only, otherwisewe need the latter 
 366                     transparency 
= CheckTransparency
 
 374                     if ( transparency 
== Transparency_Mask 
) 
 376                         // let's choose this colour for the mask: this is 
 377                         // not a problem here as all the other pixels are 
 378                         // grey, i.e. R == G == B which is not the case for 
 379                         // this one so no confusion is possible 
 384                     else // transparency == Transparency_Alpha 
 386                         alpha 
= InitAlpha(image
, x
, y
); 
 390                 switch ( transparency 
) 
 392                     case Transparency_Mask
: 
 393                         if ( IsTransparent(a
) ) 
 400                         // else: !transparent 
 402                         // must be opaque then as otherwise we shouldn't be 
 403                         // using the mask at all 
 404                         wxASSERT_MSG( IsOpaque(a
), _T("logic error") ); 
 408                     case Transparency_Alpha
: 
 413                     case Transparency_None
: 
 422     else // colour image: RGBRGB... 
 424         for ( png_uint_32 y 
= 0; y 
< height
; y
++ ) 
 426             const unsigned char *ptrSrc 
= lines
[y
]; 
 427             for ( png_uint_32 x 
= 0; x 
< width
; x
++ ) 
 429                 unsigned char r 
= *ptrSrc
++; 
 430                 unsigned char g 
= *ptrSrc
++; 
 431                 unsigned char b 
= *ptrSrc
++; 
 432                 unsigned char a 
= *ptrSrc
++; 
 434                 // the logic here is the same as for the grey case except 
 436                 if ( !IsOpaque(a
) && transparency 
== Transparency_None 
) 
 438                     transparency 
= CheckTransparency
 
 446                     if ( transparency 
== Transparency_Mask 
) 
 448                         FindMaskColour(lines
, width
, height
, 
 449                                        rMask
, gMask
, bMask
); 
 451                     else // transparency == Transparency_Alpha 
 453                         alpha 
= InitAlpha(image
, x
, y
); 
 458                 switch ( transparency 
) 
 460                     case Transparency_Mask
: 
 461                         if ( IsTransparent(a
) ) 
 470                             // must be opaque then as otherwise we shouldn't be 
 471                             // using the mask at all 
 472                             wxASSERT_MSG( IsOpaque(a
), _T("logic error") ); 
 474                             // if we couldn't find a unique colour for the 
 475                             // mask, we can have real pixels with the same 
 476                             // value as the mask and it's better to slightly 
 477                             // change their colour than to make them 
 479                             if ( r 
== rMask 
&& g 
== gMask 
&& b 
== bMask 
) 
 487                     case Transparency_Alpha
: 
 492                     case Transparency_None
: 
 502     if ( transparency 
== Transparency_Mask 
) 
 504         image
->SetMaskColour(rMask
, gMask
, bMask
); 
 508 // temporarily disable the warning C4611 (interaction between '_setjmp' and 
 509 // C++ object destruction is non-portable) - I don't see any dtors here 
 511     #pragma warning(disable:4611) 
 515 wxPNGHandler::LoadFile(wxImage 
*image
, 
 516                        wxInputStream
& stream
, 
 520     // VZ: as this function uses setjmp() the only fool proof error handling 
 521     //     method is to use goto (setjmp is not really C++ dtors friendly...) 
 523     unsigned char **lines 
= NULL
; 
 524     png_infop info_ptr 
= (png_infop
) NULL
; 
 525     wxPNGInfoStruct wxinfo
; 
 527     wxinfo
.verbose 
= verbose
; 
 528     wxinfo
.stream
.in 
= &stream
; 
 532     png_structp png_ptr 
= png_create_read_struct( PNG_LIBPNG_VER_STRING
, 
 534         (png_error_ptr
) NULL
, 
 535         (png_error_ptr
) NULL 
); 
 539     png_set_error_fn(png_ptr
, (png_voidp
)NULL
, wx_png_error
, wx_png_warning
); 
 541     // NB: please see the comment near wxPNGInfoStruct declaration for 
 542     //     explanation why this line is mandatory 
 543     png_set_read_fn( png_ptr
, &wxinfo
, _PNG_stream_reader
); 
 545     info_ptr 
= png_create_info_struct( png_ptr 
); 
 549     if (setjmp(wxinfo
.jmpbuf
)) 
 552     png_uint_32 i
, width
, height
; 
 553     int bit_depth
, color_type
, interlace_type
; 
 555     png_read_info( png_ptr
, info_ptr 
); 
 556     png_get_IHDR( png_ptr
, info_ptr
, &width
, &height
, &bit_depth
, &color_type
, &interlace_type
, (int*) NULL
, (int*) NULL 
); 
 558     if (color_type 
== PNG_COLOR_TYPE_PALETTE
) 
 559         png_set_expand( png_ptr 
); 
 561     // Fix for Bug [ 439207 ] Monochrome PNG images come up black 
 563         png_set_expand( png_ptr 
); 
 565     png_set_strip_16( png_ptr 
); 
 566     png_set_packing( png_ptr 
); 
 567     if (png_get_valid( png_ptr
, info_ptr
, PNG_INFO_tRNS
)) 
 568         png_set_expand( png_ptr 
); 
 569     png_set_filler( png_ptr
, 0xff, PNG_FILLER_AFTER 
); 
 571     image
->Create( (int)width
, (int)height 
); 
 576     lines 
= (unsigned char **)malloc( (size_t)(height 
* sizeof(unsigned char *)) ); 
 580     for (i 
= 0; i 
< height
; i
++) 
 582         if ((lines
[i
] = (unsigned char *)malloc( (size_t)(width 
* (sizeof(unsigned char) * 4)))) == NULL
) 
 584             for ( unsigned int n 
= 0; n 
< i
; n
++ ) 
 590     png_read_image( png_ptr
, lines 
); 
 591     png_read_end( png_ptr
, info_ptr 
); 
 592     png_destroy_read_struct( &png_ptr
, &info_ptr
, (png_infopp
) NULL 
); 
 594     // loaded successfully, now init wxImage with this data 
 595     CopyDataFromPNG(image
, lines
, width
, height
, color_type
); 
 597     for ( i 
= 0; i 
< height
; i
++ ) 
 605        wxLogError(_("Couldn't load a PNG image - file is corrupted or not enough memory.")); 
 621             png_destroy_read_struct( &png_ptr
, &info_ptr
, (png_infopp
) NULL 
); 
 625             png_destroy_read_struct( &png_ptr
, (png_infopp
) NULL
, (png_infopp
) NULL 
); 
 630 // ---------------------------------------------------------------------------- 
 632 // ---------------------------------------------------------------------------- 
 634 bool wxPNGHandler::SaveFile( wxImage 
*image
, wxOutputStream
& stream
, bool verbose 
) 
 636     wxPNGInfoStruct wxinfo
; 
 638     wxinfo
.verbose 
= verbose
; 
 639     wxinfo
.stream
.out 
= &stream
; 
 641     png_structp png_ptr 
= png_create_write_struct( PNG_LIBPNG_VER_STRING
, NULL
, NULL
, NULL
); 
 645            wxLogError(_("Couldn't save PNG image.")); 
 649     png_set_error_fn(png_ptr
, (png_voidp
)NULL
, wx_png_error
, wx_png_warning
); 
 651     png_infop info_ptr 
= png_create_info_struct(png_ptr
); 
 652     if (info_ptr 
== NULL
) 
 654         png_destroy_write_struct( &png_ptr
, (png_infopp
)NULL 
); 
 656            wxLogError(_("Couldn't save PNG image.")); 
 660     if (setjmp(wxinfo
.jmpbuf
)) 
 662         png_destroy_write_struct( &png_ptr
, (png_infopp
)NULL 
); 
 664            wxLogError(_("Couldn't save PNG image.")); 
 668     // NB: please see the comment near wxPNGInfoStruct declaration for 
 669     //     explanation why this line is mandatory 
 670     png_set_write_fn( png_ptr
, &wxinfo
, _PNG_stream_writer
, NULL
); 
 672     png_set_IHDR( png_ptr
, info_ptr
, image
->GetWidth(), image
->GetHeight(), 8, 
 673         PNG_COLOR_TYPE_RGB_ALPHA
, PNG_INTERLACE_NONE
, 
 674         PNG_COMPRESSION_TYPE_BASE
, PNG_FILTER_TYPE_BASE
); 
 681     png_set_sBIT( png_ptr
, info_ptr
, &sig_bit 
); 
 682     png_write_info( png_ptr
, info_ptr 
); 
 683     png_set_shift( png_ptr
, &sig_bit 
); 
 684     png_set_packing( png_ptr 
); 
 686     unsigned char *data 
= (unsigned char *)malloc( image
->GetWidth()*4 ); 
 689         png_destroy_write_struct( &png_ptr
, (png_infopp
)NULL 
); 
 693     for (int y 
= 0; y 
< image
->GetHeight(); y
++) 
 695         unsigned char *ptr 
= image
->GetData() + (y 
* image
->GetWidth() * 3); 
 696         for (int x 
= 0; x 
< image
->GetWidth(); x
++) 
 698             data
[(x 
<< 2) + 0] = *ptr
++; 
 699             data
[(x 
<< 2) + 1] = *ptr
++; 
 700             data
[(x 
<< 2) + 2] = *ptr
++; 
 701             if (( !image
->HasMask() ) || \
 
 702                 (data
[(x 
<< 2) + 0] != image
->GetMaskRed()) || \
 
 703                 (data
[(x 
<< 2) + 1] != image
->GetMaskGreen()) || \
 
 704                 (data
[(x 
<< 2) + 2] != image
->GetMaskBlue())) 
 706                 data
[(x 
<< 2) + 3] = 255; 
 710                 data
[(x 
<< 2) + 3] = 0; 
 713         png_bytep row_ptr 
= data
; 
 714         png_write_rows( png_ptr
, &row_ptr
, 1 ); 
 718     png_write_end( png_ptr
, info_ptr 
); 
 719     png_destroy_write_struct( &png_ptr
, (png_infopp
)&info_ptr 
); 
 725     #pragma warning(default:4611) 
 728 #endif  // wxUSE_STREAMS 
 730 #endif  // wxUSE_LIBPNG