]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/imagpng.cpp
Add wxFont::Underlined() and MakeUnderlined() methods.
[wxWidgets.git] / src / common / imagpng.cpp
index abe155c880bd0174dfacc9138bc8ea397fd682ad..41b13785285edd78ea8ac33f8654613cb6c6b94e 100644 (file)
@@ -25,6 +25,7 @@
 #if wxUSE_IMAGE && wxUSE_LIBPNG
 
 #include "wx/imagpng.h"
+#include "wx/versioninfo.h"
 
 #ifndef WX_PRECOMP
     #include "wx/log.h"
@@ -387,7 +388,7 @@ void CopyDataFromPNG(wxImage *image,
 
                         // must be opaque then as otherwise we shouldn't be
                         // using the mask at all
-                        wxASSERT_MSG( IsOpaque(a), _T("logic error") );
+                        wxASSERT_MSG( IsOpaque(a), wxT("logic error") );
 
                         // fall through
 
@@ -455,7 +456,7 @@ void CopyDataFromPNG(wxImage *image,
                         {
                             // must be opaque then as otherwise we shouldn't be
                             // using the mask at all
-                            wxASSERT_MSG( IsOpaque(a), _T("logic error") );
+                            wxASSERT_MSG( IsOpaque(a), wxT("logic error") );
 
                             // if we couldn't find a unique colour for the
                             // mask, we can have real pixels with the same
@@ -521,7 +522,7 @@ wxPNGHandler::LoadFile(wxImage *image,
     png_structp png_ptr = png_create_read_struct
                           (
                             PNG_LIBPNG_VER_STRING,
-                            (voidp) NULL,
+                            NULL,
                             wx_png_error,
                             wx_png_warning
                           );
@@ -578,19 +579,23 @@ wxPNGHandler::LoadFile(wxImage *image,
 #if wxUSE_PALETTE
     if (color_type == PNG_COLOR_TYPE_PALETTE)
     {
-        const size_t ncolors = info_ptr->num_palette;
-        unsigned char* r = new unsigned char[ncolors];
-        unsigned char* g = new unsigned char[ncolors];
-        unsigned char* b = new unsigned char[ncolors];
+        png_colorp palette = NULL;
+        int numPalette = 0;
+
+        (void) png_get_PLTE(png_ptr, info_ptr, &palette, &numPalette);
+
+        unsigned char* r = new unsigned char[numPalette];
+        unsigned char* g = new unsigned char[numPalette];
+        unsigned char* b = new unsigned char[numPalette];
 
-        for (size_t j = 0; j < ncolors; j++)
+        for (int j = 0; j < numPalette; j++)
         {
-            r[j] = info_ptr->palette[j].red;
-            g[j] = info_ptr->palette[j].green;
-            b[j] = info_ptr->palette[j].blue;
+            r[j] = palette[j].red;
+            g[j] = palette[j].green;
+            b[j] = palette[j].blue;
         }
 
-        image->SetPalette(wxPalette(ncolors, r, g, b));
+        image->SetPalette(wxPalette(numPalette, r, g, b));
         delete[] r;
         delete[] g;
         delete[] b;
@@ -640,6 +645,29 @@ error:
     return false;
 }
 
+// ----------------------------------------------------------------------------
+// SaveFile() helpers
+// ----------------------------------------------------------------------------
+
+#if wxUSE_PALETTE
+
+static int PaletteFind(const png_color& clr, const png_color *pal, int palCount)
+{
+   for (int i = 0; i < palCount; ++i)
+   {
+      if (    (clr.red   == pal[i].red)
+           && (clr.green == pal[i].green)
+           && (clr.blue  == pal[i].blue))
+      {
+         return i;
+      }
+   }
+
+   return wxNOT_FOUND;
+}
+
+#endif // wxUSE_PALETTE
+
 // ----------------------------------------------------------------------------
 // writing PNGs
 // ----------------------------------------------------------------------------
@@ -692,18 +720,90 @@ bool wxPNGHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbos
     //     explanation why this line is mandatory
     png_set_write_fn( png_ptr, &wxinfo, wx_PNG_stream_writer, NULL);
 
-    const int iColorType = image->HasOption(wxIMAGE_OPTION_PNG_FORMAT)
+    const bool bHasPngFormatOption
+        = image->HasOption(wxIMAGE_OPTION_PNG_FORMAT);
+
+    int iColorType = bHasPngFormatOption
                             ? image->GetOptionInt(wxIMAGE_OPTION_PNG_FORMAT)
                             : wxPNG_TYPE_COLOUR;
-    const int iBitDepth = image->HasOption(wxIMAGE_OPTION_PNG_BITDEPTH)
-                            ? image->GetOptionInt(wxIMAGE_OPTION_PNG_BITDEPTH)
-                            : 8;
 
     bool bHasAlpha = image->HasAlpha();
     bool bHasMask = image->HasMask();
-    bool bUseAlpha = bHasAlpha || bHasMask;
+
+#if wxUSE_PALETTE
+    /*
+    Only save as an indexed image if the number of palette entries does not
+    exceed libpng's limit (256).
+    We assume here that we will need an extra palette entry if there's an
+    alpha or mask, regardless of whether a possibly needed conversion from
+    alpha to a mask fails (unlikely), or whether the mask colour already
+    can be found in the palette (more likely). In the latter case an extra
+    palette entry would not be required later on and the image could actually
+    be saved as a palettised PNG (instead now it will be saved as true colour).
+    A little bit of precision is lost, but at the benefit of a lot more
+    simplified code.
+    */
+    bool bUsePalette =
+        (!bHasPngFormatOption || iColorType == wxPNG_TYPE_PALETTE)
+        && image->HasPalette()
+        && image->GetPalette().GetColoursCount()
+            + ((bHasAlpha || bHasMask) ? 1 : 0) <= PNG_MAX_PALETTE_LENGTH;
+
+    wxImage temp_image(*image);
+    if (bUsePalette && image->HasAlpha() && !bHasMask)
+    {
+        /*
+        Only convert alpha to mask if saving as a palettised image was
+        explicitly requested. We don't want to lose alpha's precision
+        by converting to a mask just to be able to save palettised.
+        */
+        if (iColorType == wxPNG_TYPE_PALETTE
+            && temp_image.ConvertAlphaToMask())
+        {
+            image = &temp_image;
+            bHasMask = true;
+            bHasAlpha = image->HasAlpha();
+        }
+        else
+        {
+            bUsePalette = false;
+            iColorType = wxPNG_TYPE_COLOUR;
+        }
+    }
+#else
+    bool bUsePalette = false;
+#endif // wxUSE_PALETTE
+
+    /*
+    If saving palettised was requested but it was decided we can't use a
+    palette then reset the colour type to RGB.
+    */
+    if (!bUsePalette && iColorType == wxPNG_TYPE_PALETTE)
+    {
+        iColorType = wxPNG_TYPE_COLOUR;
+    }
+
+    bool bUseAlpha = !bUsePalette && (bHasAlpha || bHasMask);
+
+    png_color mask;
+    if (bHasMask)
+    {
+        mask.red   = image->GetMaskRed();
+        mask.green = image->GetMaskGreen();
+        mask.blue  = image->GetMaskBlue();
+    }
+
 
     int iPngColorType;
+
+#if wxUSE_PALETTE
+    if (bUsePalette)
+    {
+        iPngColorType = PNG_COLOR_TYPE_PALETTE;
+        iColorType = wxPNG_TYPE_PALETTE;
+    }
+    else
+#endif // wxUSE_PALETTE
     if ( iColorType==wxPNG_TYPE_COLOUR )
     {
         iPngColorType = bUseAlpha ? PNG_COLOR_TYPE_RGB_ALPHA
@@ -730,11 +830,71 @@ bool wxPNGHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbos
     if (image->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_BUFFER_SIZE))
         png_set_compression_buffer_size( png_ptr, image->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_BUFFER_SIZE) );
 
+    int iBitDepth = !bUsePalette && image->HasOption(wxIMAGE_OPTION_PNG_BITDEPTH)
+                            ? image->GetOptionInt(wxIMAGE_OPTION_PNG_BITDEPTH)
+                            : 8;
+
     png_set_IHDR( png_ptr, info_ptr, image->GetWidth(), image->GetHeight(),
                   iBitDepth, iPngColorType,
                   PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
                   PNG_FILTER_TYPE_BASE);
 
+#if wxUSE_PALETTE
+    png_colorp palette = NULL;
+    int numPalette = 0;
+
+    if (bUsePalette)
+    {
+        const wxPalette& pal = image->GetPalette();
+        const int palCount = pal.GetColoursCount();
+        palette = (png_colorp) malloc(
+            (palCount + 1 /*headroom for trans */) * sizeof(png_color));
+
+        if (!palette)
+        {
+            png_destroy_write_struct( &png_ptr, (png_infopp)NULL );
+            if (verbose)
+            {
+               wxLogError(_("Couldn't save PNG image."));
+            }
+            return false;
+        }
+
+        for (int i = 0; i < palCount; ++i)
+        {
+            pal.GetRGB(i, &palette[i].red, &palette[i].green, &palette[i].blue);
+        }
+
+        numPalette = palCount;
+        if (bHasMask)
+        {
+            int index = PaletteFind(mask, palette, numPalette);
+
+            if (index)
+            {
+                if (index == wxNOT_FOUND)
+                {
+                    numPalette++;
+                    index = palCount;
+                    palette[index] = mask;
+                }
+
+                wxSwap(palette[0], palette[index]);
+            }
+
+            png_byte trans = 0;
+            png_set_tRNS(png_ptr, info_ptr, &trans, 1, NULL);
+        }
+
+        png_set_PLTE(png_ptr, info_ptr, palette, numPalette);
+        free (palette);
+        palette = NULL;
+
+        // Let palette point to libpng's copy of the palette.
+        (void) png_get_PLTE(png_ptr, info_ptr, &palette, &numPalette);
+    }
+#endif // wxUSE_PALETTE
+
     int iElements;
     png_color_8 sig_bit;
 
@@ -781,7 +941,7 @@ bool wxPNGHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbos
             break;
 
         default:
-            wxFAIL_MSG( _T("unsupported image resolution units") );
+            wxFAIL_MSG( wxT("unsupported image resolution units") );
     }
 
     if ( resX && resY )
@@ -805,15 +965,6 @@ bool wxPNGHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbos
     int iHeight = image->GetHeight();
     int iWidth = image->GetWidth();
 
-    unsigned char uchMaskRed = 0, uchMaskGreen = 0, uchMaskBlue = 0;
-
-    if ( bHasMask )
-    {
-        uchMaskRed = image->GetMaskRed();
-        uchMaskGreen = image->GetMaskGreen();
-        uchMaskBlue = image->GetMaskBlue();
-    }
-
     unsigned char *pColors = image->GetData();
 
     for (int y = 0; y != iHeight; ++y)
@@ -821,24 +972,25 @@ bool wxPNGHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbos
         unsigned char *pData = data;
         for (int x = 0; x != iWidth; x++)
         {
-            unsigned char uchRed = *pColors++;
-            unsigned char uchGreen = *pColors++;
-            unsigned char uchBlue = *pColors++;
+            png_color clr;
+            clr.red   = *pColors++;
+            clr.green = *pColors++;
+            clr.blue  = *pColors++;
 
             switch ( iColorType )
             {
                 default:
-                    wxFAIL_MSG( _T("unknown wxPNG_TYPE_XXX") );
+                    wxFAIL_MSG( wxT("unknown wxPNG_TYPE_XXX") );
                     // fall through
 
                 case wxPNG_TYPE_COLOUR:
-                    *pData++ = uchRed;
+                    *pData++ = clr.red;
                     if ( iBitDepth == 16 )
                         *pData++ = 0;
-                    *pData++ = uchGreen;
+                    *pData++ = clr.green;
                     if ( iBitDepth == 16 )
                         *pData++ = 0;
-                    *pData++ = uchBlue;
+                    *pData++ = clr.blue;
                     if ( iBitDepth == 16 )
                         *pData++ = 0;
                     break;
@@ -848,9 +1000,9 @@ bool wxPNGHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbos
                         // where do these coefficients come from? maybe we
                         // should have image options for them as well?
                         unsigned uiColor =
-                            (unsigned) (76.544*(unsigned)uchRed +
-                                        150.272*(unsigned)uchGreen +
-                                        36.864*(unsigned)uchBlue);
+                            (unsigned) (76.544*(unsigned)clr.red +
+                                        150.272*(unsigned)clr.green +
+                                        36.864*(unsigned)clr.blue);
 
                         *pData++ = (unsigned char)((uiColor >> 8) & 0xFF);
                         if ( iBitDepth == 16 )
@@ -859,10 +1011,17 @@ bool wxPNGHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbos
                     break;
 
                 case wxPNG_TYPE_GREY_RED:
-                    *pData++ = uchRed;
+                    *pData++ = clr.red;
                     if ( iBitDepth == 16 )
                         *pData++ = 0;
                     break;
+
+#if wxUSE_PALETTE
+                case wxPNG_TYPE_PALETTE:
+                    *pData++ = (unsigned char) PaletteFind(clr,
+                        palette, numPalette);
+                    break;
+#endif // wxUSE_PALETTE
             }
 
             if ( bUseAlpha )
@@ -873,9 +1032,9 @@ bool wxPNGHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbos
 
                 if ( bHasMask )
                 {
-                    if ( (uchRed == uchMaskRed)
-                            && (uchGreen == uchMaskGreen)
-                                && (uchBlue == uchMaskBlue) )
+                    if ( (clr.red == mask.red)
+                            && (clr.green == mask.green)
+                                && (clr.blue == mask.blue) )
                         uchAlpha = 0;
                 }
 
@@ -902,4 +1061,18 @@ bool wxPNGHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbos
 
 #endif  // wxUSE_STREAMS
 
+/*static*/ wxVersionInfo wxPNGHandler::GetLibraryVersionInfo()
+{
+    // The version string seems to always have a leading space and a trailing
+    // new line, get rid of them both.
+    wxString str = png_get_header_version(NULL) + 1;
+    str.Replace("\n", "");
+
+    return wxVersionInfo("libpng",
+                         PNG_LIBPNG_VER_MAJOR,
+                         PNG_LIBPNG_VER_MINOR,
+                         PNG_LIBPNG_VER_RELEASE,
+                         str);
+}
+
 #endif  // wxUSE_LIBPNG