+#ifndef PNGLINKAGEMODE
+ #ifdef __WATCOMC__
+ // we need an explicit cdecl for Watcom, at least according to
+ //
+ // http://sf.net/tracker/index.php?func=detail&aid=651492&group_id=9863&atid=109863
+ //
+ // more testing is needed for this however, please remove this comment
+ // if you can confirm that my fix works with Watcom 11
+ #define PNGLINKAGEMODE cdecl
+ #else
+ #define PNGLINKAGEMODE LINKAGEMODE
+ #endif
+#endif
+
+
+// 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 _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 _PNG_stream_writer( png_structp png_ptr, png_bytep data, png_size_t length )
+{
+ WX_PNG_INFO(png_ptr)->stream.out->Write(data, length);
+}
+
+// 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)
+{
+ wxPNGInfoStruct *info = WX_PNG_INFO(png_ptr);
+ if (info->verbose)
+ wxLogError( wxString::FromAscii(message) );
+
+#ifdef USE_FAR_KEYWORD
+ {
+ jmp_buf jmpbuf;
+ png_memcpy(jmpbuf,info->jmpbuf,sizeof(jmp_buf));
+ longjmp(jmpbuf, 1);
+ }
+#else
+ longjmp(info->jmpbuf, 1);
+#endif
+}
+
+void
+PNGLINKAGEMODE wx_png_warning(png_structp png_ptr, png_const_charp message)
+{
+ wxPNGInfoStruct *info = WX_PNG_INFO(png_ptr);
+ if (info->verbose)
+ wxLogWarning( wxString::FromAscii(message) );
+}
+
+} // extern "C"
+
+// ----------------------------------------------------------------------------
+// LoadFile() helpers
+// ----------------------------------------------------------------------------
+
+Transparency
+CheckTransparency(const unsigned char *ptr,
+ png_uint_32 x, png_uint_32 y, png_uint_32 w, png_uint_32 h)
+{
+ // suppose that a mask will suffice
+ Transparency transparency = Transparency_Mask;
+
+ // and check all the remaining alpha values to see if it does
+ unsigned char a2;
+ unsigned const char *ptr2 = ptr;
+ for ( png_uint_32 y2 = y; y2 < h; y2++ )
+ {
+ for ( png_uint_32 x2 = x + 1; x2 < w; x2++ )
+ {
+ // skip the grey byte
+ a2 = *++ptr2;
+
+ if ( a2 && a2 != 0xff )
+ {
+ // not fully opeaque nor fully transparent, hence need alpha
+ transparency = Transparency_Alpha;
+ break;
+ }
+
+ ++ptr2;
+ }
+
+ if ( transparency == Transparency_Alpha )
+ {
+ // no need to continue
+ break;
+ }
+ }
+
+ return transparency;
+}
+
+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
+ for ( png_uint_32 y2 = 0; y2 <= y; y2++ )
+ {
+ for ( png_uint_32 x2 = 0; x2 < x; x2++ )
+ {
+ // 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)
+{
+ // choosing the colour for the mask is more
+ // difficult: we need to iterate over the entire
+ // image for this in order to choose an unused
+ // colour (this is not very efficient but what else
+ // can we do?)
+ wxImageHistogram h;
+ unsigned nentries = 0;
+ unsigned char r2, g2, b2;
+ for ( png_uint_32 y2 = 0; y2 < height; y2++ )
+ {
+ const unsigned char *p = lines[y2];
+ for ( png_uint_32 x2 = 0; x2 < width; x2++ )
+ {
+ r2 = *p++;
+ g2 = *p++;
+ b2 = *p++;
+
+ wxImageHistogramEntry&
+ entry = h[wxImageHistogram:: MakeKey(r2, g2, b2)];
+
+ if ( entry.value++ == 0 )
+ entry.index = nentries++;
+ }
+ }
+
+ if ( !h.FindFirstUnusedColour(&rMask, &gMask, &bMask) )
+ {
+ wxLogWarning(_("Too many colours in PNG, the image may be slightly blurred."));
+
+ // use a fixed mask colour and we'll fudge
+ // the real pixels with this colour (see
+ // below)
+ rMask = 0xfe;
+ gMask = 0;
+ bMask = 0xff;
+ }
+}
+
+// ----------------------------------------------------------------------------
+// reading PNGs
+// ----------------------------------------------------------------------------
+
+bool wxPNGHandler::DoCanRead( wxInputStream& stream )