+// VS: wxPNGInfoStruct declared below is a hack that needs some explanation.
+// First, let me describe what's the problem: libpng uses jmp_buf in
+// its png_struct structure. Unfortunately, this structure is
+// compiler-specific and may vary in size, so if you use libpng compiled
+// as DLL with another compiler than the main executable, it may not work
+// (this is for example the case with wxMGL port and SciTech MGL library
+// that provides custom runtime-loadable libpng implementation with jmpbuf
+// disabled altogether). Luckily, it is still possible to use setjmp() &
+// longjmp() as long as the structure is not part of png_struct.
+//
+// Sadly, there's no clean way to attach user-defined data to png_struct.
+// There is only one customizable place, png_struct.io_ptr, which is meant
+// only for I/O routines and is set with png_set_read_fn or
+// png_set_write_fn. The hacky part is that we use io_ptr to store
+// a pointer to wxPNGInfoStruct that holds I/O structures _and_ jmp_buf.
+
+struct wxPNGInfoStruct
+{
+ jmp_buf jmpbuf;
+ bool verbose;
+
+ union
+ {
+ wxInputStream *in;
+ wxOutputStream *out;
+ } stream;
+};
+
+#define WX_PNG_INFO(png_ptr) ((wxPNGInfoStruct*)png_get_io_ptr(png_ptr))
+
+// ----------------------------------------------------------------------------
+// helper functions
+// ----------------------------------------------------------------------------
+
+extern "C"
+{
+
+void PNGLINKAGEMODE wx_PNG_stream_reader( png_structp png_ptr, png_bytep data,
+ png_size_t length )
+{
+ WX_PNG_INFO(png_ptr)->stream.in->Read(data, length);
+}
+
+void PNGLINKAGEMODE wx_PNG_stream_writer( png_structp png_ptr, png_bytep data,
+ png_size_t length )
+{
+ WX_PNG_INFO(png_ptr)->stream.out->Write(data, length);
+}
+
+void
+PNGLINKAGEMODE wx_png_warning(png_structp png_ptr, png_const_charp message)
+{
+ wxPNGInfoStruct *info = png_ptr ? WX_PNG_INFO(png_ptr) : NULL;
+ if ( !info || info->verbose )
+ wxLogWarning( wxString::FromAscii(message) );
+}
+
+// from pngerror.c
+// so that the libpng doesn't send anything on stderr
+void
+PNGLINKAGEMODE wx_png_error(png_structp png_ptr, png_const_charp message)
+{
+ wx_png_warning(NULL, message);
+
+ // we're not using libpng built-in jump buffer (see comment before
+ // wxPNGInfoStruct above) so we have to return ourselves, otherwise libpng
+ // would just abort
+ longjmp(WX_PNG_INFO(png_ptr)->jmpbuf, 1);
+}
+
+} // extern "C"
+
+// ----------------------------------------------------------------------------
+// LoadFile() helpers
+// ----------------------------------------------------------------------------
+
+// determine the kind of transparency we need for this image: if the only alpha
+// values it has are 0 (transparent) and 0xff (opaque) then we can simply
+// create a mask for it, we should be ok with a simple mask but otherwise we
+// need a full blown alpha channel in wxImage
+//
+// parameters:
+// lines raw PNG data
+// x, y starting position
+// w, h size of the image
+// numColBytes number of colour bytes (1 for grey scale, 3 for RGB)
+// (NB: alpha always follows the colour bytes)
+Transparency
+CheckTransparency(unsigned char **lines,
+ png_uint_32 x, png_uint_32 y, png_uint_32 w, png_uint_32 h,
+ size_t numColBytes)
+{
+ // suppose that a mask will suffice and check all the remaining alpha
+ // values to see if it does
+ for ( ; y < h; y++ )
+ {
+ // each pixel is numColBytes+1 bytes, offset into the current line by
+ // the current x position
+ unsigned const char *ptr = lines[y] + (x * (numColBytes + 1));
+
+ for ( png_uint_32 x2 = x; x2 < w; x2++ )
+ {
+ // skip the grey or colour byte(s)
+ ptr += numColBytes;
+
+ unsigned char a2 = *ptr++;
+
+ if ( !IsTransparent(a2) && !IsOpaque(a2) )
+ {
+ // not fully opaque nor fully transparent, hence need alpha
+ return Transparency_Alpha;
+ }
+ }
+
+ // during the next loop iteration check all the pixels in the row
+ x = 0;
+ }
+
+ // mask will be enough
+ return Transparency_Mask;
+}
+
+unsigned char *InitAlpha(wxImage *image, png_uint_32 x, png_uint_32 y)
+{
+ // create alpha channel
+ image->SetAlpha();
+
+ unsigned char *alpha = image->GetAlpha();
+
+ // set alpha for the pixels we had so far
+ png_uint_32 end = y * image->GetWidth() + x;
+ for ( png_uint_32 i = 0; i < end; i++ )
+ {
+ // all the previous pixels were opaque
+ *alpha++ = 0xff;
+ }
+
+ return alpha;
+}
+
+void
+FindMaskColour(unsigned char **lines, png_uint_32 width, png_uint_32 height,
+ unsigned char& rMask, unsigned char& gMask, unsigned char& bMask)