+ 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 )
+{
+ unsigned char hdr[4];
+
+ if ( !stream.Read(hdr, WXSIZEOF(hdr)) )
+ return FALSE;
+
+ return memcmp(hdr, "\211PNG", WXSIZEOF(hdr)) == 0;
+}
+
+// convert data from RGB to wxImage format
+static
+void CopyDataFromPNG(wxImage *image,
+ unsigned char **lines,
+ png_uint_32 width,
+ png_uint_32 height,
+ int color_type)
+{
+ Transparency transparency = Transparency_None;
+
+ // only non NULL if transparency == Transparency_Alpha
+ unsigned char *alpha = NULL;
+
+ // RGB of the mask colour if transparency == Transparency_Mask
+ // (but init them anyhow to avoid compiler warnings)
+ unsigned char rMask = 0,
+ gMask = 0,
+ bMask = 0;
+
+ unsigned char *ptrDst = image->GetData();
+ if ( !(color_type & PNG_COLOR_MASK_COLOR) )
+ {
+ // grey image: GAGAGA... where G == grey component and A == alpha
+ for ( png_uint_32 y = 0; y < height; y++ )
+ {
+ const unsigned char *ptrSrc = lines[y];
+ for ( png_uint_32 x = 0; x < width; x++ )
+ {
+ unsigned char g = *ptrSrc++;
+ unsigned char a = *ptrSrc++;
+
+ // the first time we encounter a transparent pixel we must
+ // decide about what to do about them
+ if ( a != 0xff && transparency == Transparency_None )
+ {
+ // we'll need at least the mask for this image and
+ // maybe even full alpha channel info: the former is
+ // only enough if we have alpha values of 0 and 0xff
+ // only, otherwisewe need the latter
+ transparency = CheckTransparency(ptrSrc, x, y,
+ width, height);
+
+ if ( transparency == Transparency_Mask )
+ {
+ // let's choose this colour for the mask: this is
+ // not a problem here as all the other pixels are
+ // grey, i.e. R == G == B which is not the case for
+ // this one so no confusion is possible
+ rMask = 0xff;
+ gMask = 0;
+ bMask = 0xff;
+ }
+ else // transparency == Transparency_Alpha
+ {
+ alpha = InitAlpha(image, x, y);
+ }
+ }
+
+ switch ( transparency )
+ {
+ case Transparency_Alpha:
+ *alpha++ = a;
+ // fall through
+
+ case Transparency_None:
+ *ptrDst++ = g;
+ *ptrDst++ = g;
+ *ptrDst++ = g;
+ break;
+
+ case Transparency_Mask:
+ *ptrDst++ = rMask;
+ *ptrDst++ = bMask;
+ *ptrDst++ = gMask;
+ }
+ }
+ }
+ }
+ else // colour image: RGBRGB...
+ {
+ for ( png_uint_32 y = 0; y < height; y++ )
+ {
+ const unsigned char *ptrSrc = lines[y];
+ for ( png_uint_32 x = 0; x < width; x++ )
+ {
+ unsigned char r = *ptrSrc++;
+ unsigned char g = *ptrSrc++;
+ unsigned char b = *ptrSrc++;
+ unsigned char a = *ptrSrc++;
+
+ // the logic here is the same as for the grey case except
+ // where noted
+ if ( a != 0xff && transparency == Transparency_None )
+ {
+ transparency = CheckTransparency(ptrSrc, x, y,
+ width, height);
+
+ if ( transparency == Transparency_Mask )
+ {
+ FindMaskColour(lines, width, height,
+ rMask, gMask, bMask);
+ }
+ else // transparency == Transparency_Alpha
+ {
+ alpha = InitAlpha(image, x, y);
+ }
+
+ }
+
+ switch ( transparency )
+ {
+ case Transparency_Alpha:
+ *alpha++ = a;
+ // fall through
+
+ case Transparency_None:
+ *ptrDst++ = r;
+ *ptrDst++ = g;
+ *ptrDst++ = b;
+ break;
+
+ case Transparency_Mask:
+ // if we couldn't find a unique colour for the mask, we
+ // can have real pixels with the same value as the mask
+ // and it's better to slightly change their colour than
+ // to make them transparent
+ if ( r == rMask && g == gMask && b == bMask )
+ {
+ r++;
+ }
+
+ *ptrDst++ = rMask;
+ *ptrDst++ = bMask;
+ *ptrDst++ = gMask;
+ }
+ }
+ }
+ }
+
+ if ( transparency == Transparency_Mask )
+ {
+ image->SetMaskColour(rMask, gMask, bMask);
+ }