]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/imagpng.cpp
fix for always terminating intermediate UniChar String for 4 bytes wchar_t
[wxWidgets.git] / src / common / imagpng.cpp
index 1b8e2017189f27a9ed42cc7f0c1b2dd6e26baf46..250fac506acf6d28c269c35c0d0d6689b403ef6b 100644 (file)
@@ -11,7 +11,7 @@
 // declarations
 // ============================================================================
 
-#ifdef __GNUG__
+#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
 #pragma implementation "imagpng.h"
 #endif
 
@@ -74,8 +74,9 @@ enum Transparency
 // return the kind of transparency needed for this image assuming that it does
 // have transparent pixels, i.e. either Transparency_Alpha or Transparency_Mask
 static Transparency
-CheckTransparency(const unsigned char *ptr,
-                  png_uint_32 x, png_uint_32 y, png_uint_32 w, png_uint_32 h);
+CheckTransparency(unsigned char **lines,
+                  png_uint_32 x, png_uint_32 y, png_uint_32 w, png_uint_32 h,
+                  size_t numColBytes);
 
 // init the alpha channel for the image and fill it with 1s up to (x, y)
 static unsigned char *InitAlpha(wxImage *image, png_uint_32 x, png_uint_32 y);
@@ -85,6 +86,20 @@ static void
 FindMaskColour(unsigned char **lines, png_uint_32 width, png_uint_32 height,
                unsigned char& rMask, unsigned char& gMask, unsigned char& bMask);
 
+// is the pixel with this value of alpha a fully opaque one?
+static inline
+bool IsOpaque(unsigned char a)
+{
+    return a == 0xff;
+}
+
+// is the pixel with this value of alpha a fully transparent one?
+static inline
+bool IsTransparent(unsigned char a)
+{
+    return !a;
+}
+
 // ============================================================================
 // wxPNGHandler implementation
 // ============================================================================
@@ -145,12 +160,14 @@ struct wxPNGInfoStruct
 extern "C"
 {
 
-void PNGLINKAGEMODE _PNG_stream_reader( png_structp png_ptr, png_bytep data, png_size_t length )
+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 _PNG_stream_writer( png_structp png_ptr, png_bytep data, png_size_t 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);
 }
@@ -189,41 +206,50 @@ PNGLINKAGEMODE wx_png_warning(png_structp png_ptr, png_const_charp message)
 // 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(const unsigned char *ptr,
-                  png_uint_32 x, png_uint_32 y, png_uint_32 w, png_uint_32 h)
+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
-    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++ )
+    // suppose that a mask will suffice and check all the remaining alpha
+    // values to see if it does
+    for ( ; y < h; y++ )
     {
-        for ( png_uint_32 x2 = x + 1; x2 < w; x2++ )
+        // 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 byte
-            a2 = *++ptr2;
+            // skip the grey or colour byte(s)
+            ptr += numColBytes;
+
+            unsigned char a2 = *ptr++;
 
-            if ( a2 && a2 != 0xff )
+            if ( !IsTransparent(a2) && !IsOpaque(a2) )
             {
-                // not fully opeaque nor fully transparent, hence need alpha
-                transparency = Transparency_Alpha;
-                break;
+                // not fully opaque nor fully transparent, hence need alpha
+                return Transparency_Alpha;
             }
-
-            ++ptr2;
         }
 
-        if ( transparency == Transparency_Alpha )
-        {
-            // no need to continue
-            break;
-        }
+        // during the next loop iteration check all the pixels in the row
+        x = 0;
     }
 
-    return transparency;
+    // mask will be enough
+    return Transparency_Mask;
 }
 
 unsigned char *InitAlpha(wxImage *image, png_uint_32 x, png_uint_32 y)
@@ -234,13 +260,11 @@ unsigned char *InitAlpha(wxImage *image, png_uint_32 x, png_uint_32 y)
     unsigned char *alpha = image->GetAlpha();
 
     // set alpha for the pixels we had so far
-    for ( png_uint_32 y2 = 0; y2 <= y; y2++ )
+    png_uint_32 end = y * image->GetWidth() + x;
+    for ( png_uint_32 i = 0; i < end; i++ )
     {
-        for ( png_uint_32 x2 = 0; x2 < x; x2++ )
-        {
-            // all the previous pixels were opaque
-            *alpha++ = 0xff;
-        }
+        // all the previous pixels were opaque
+        *alpha++ = 0xff;
     }
 
     return alpha;
@@ -335,14 +359,19 @@ void CopyDataFromPNG(wxImage *image,
 
                 // the first time we encounter a transparent pixel we must
                 // decide about what to do about them
-                if ( a != 0xff && transparency == Transparency_None )
+                if ( !IsOpaque(a) && 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);
+                    transparency = CheckTransparency
+                                   (
+                                        lines,
+                                        x, y,
+                                        width, height,
+                                        1
+                                   );
 
                     if ( transparency == Transparency_Mask )
                     {
@@ -362,8 +391,25 @@ void CopyDataFromPNG(wxImage *image,
 
                 switch ( transparency )
                 {
+                    case Transparency_Mask:
+                        if ( IsTransparent(a) )
+                        {
+                            *ptrDst++ = rMask;
+                            *ptrDst++ = bMask;
+                            *ptrDst++ = gMask;
+                            break;
+                        }
+                        // else: !transparent
+
+                        // must be opaque then as otherwise we shouldn't be
+                        // using the mask at all
+                        wxASSERT_MSG( IsOpaque(a), _T("logic error") );
+
+                        // fall through
+
                     case Transparency_Alpha:
-                        *alpha++ = a;
+                        if ( alpha )
+                            *alpha++ = a;
                         // fall through
 
                     case Transparency_None:
@@ -371,11 +417,6 @@ void CopyDataFromPNG(wxImage *image,
                         *ptrDst++ = g;
                         *ptrDst++ = g;
                         break;
-
-                    case Transparency_Mask:
-                        *ptrDst++ = rMask;
-                        *ptrDst++ = bMask;
-                        *ptrDst++ = gMask;
                 }
             }
         }
@@ -394,10 +435,15 @@ void CopyDataFromPNG(wxImage *image,
 
                 // the logic here is the same as for the grey case except
                 // where noted
-                if ( a != 0xff && transparency == Transparency_None )
+                if ( !IsOpaque(a) && transparency == Transparency_None )
                 {
-                    transparency = CheckTransparency(ptrSrc, x, y,
-                                                     width, height);
+                    transparency = CheckTransparency
+                                   (
+                                        lines,
+                                        x, y,
+                                        width, height,
+                                        3
+                                   );
 
                     if ( transparency == Transparency_Mask )
                     {
@@ -413,8 +459,36 @@ void CopyDataFromPNG(wxImage *image,
 
                 switch ( transparency )
                 {
+                    case Transparency_Mask:
+                        if ( IsTransparent(a) )
+                        {
+                            *ptrDst++ = rMask;
+                            *ptrDst++ = bMask;
+                            *ptrDst++ = gMask;
+                            break;
+                        }
+                        else // !transparent
+                        {
+                            // must be opaque then as otherwise we shouldn't be
+                            // using the mask at all
+                            wxASSERT_MSG( IsOpaque(a), _T("logic error") );
+
+                            // 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++;
+                            }
+                        }
+
+                        // fall through
+
                     case Transparency_Alpha:
-                        *alpha++ = a;
+                        if ( alpha )
+                            *alpha++ = a;
                         // fall through
 
                     case Transparency_None:
@@ -422,20 +496,6 @@ void CopyDataFromPNG(wxImage *image,
                         *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;
                 }
             }
         }
@@ -482,7 +542,7 @@ wxPNGHandler::LoadFile(wxImage *image,
 
     // NB: please see the comment near wxPNGInfoStruct declaration for
     //     explanation why this line is mandatory
-    png_set_read_fn( png_ptr, &wxinfo, _PNG_stream_reader);
+    png_set_read_fn( png_ptr, &wxinfo, wx_PNG_stream_reader);
 
     info_ptr = png_create_info_struct( png_ptr );
     if (!info_ptr)
@@ -609,7 +669,7 @@ bool wxPNGHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbos
 
     // NB: please see the comment near wxPNGInfoStruct declaration for
     //     explanation why this line is mandatory
-    png_set_write_fn( png_ptr, &wxinfo, _PNG_stream_writer, NULL);
+    png_set_write_fn( png_ptr, &wxinfo, wx_PNG_stream_writer, NULL);
 
     png_set_IHDR( png_ptr, info_ptr, image->GetWidth(), image->GetHeight(), 8,
         PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,