]> git.saurik.com Git - wxWidgets.git/commitdiff
Added alpha saving support to TIFF image handler.
authorDimitri Schoolwerth <dimitri.schoolwerth@gmail.com>
Mon, 29 Aug 2011 01:13:06 +0000 (01:13 +0000)
committerDimitri Schoolwerth <dimitri.schoolwerth@gmail.com>
Mon, 29 Aug 2011 01:13:06 +0000 (01:13 +0000)
Added support for saving alpha with RGB, greyscale, and black and white images.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@68948 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

docs/changes.txt
interface/wx/image.h
src/common/imagtiff.cpp
tests/image/image.cpp
tests/testimage.h

index 6fb7429ec8bf6b43c9fae612a616df6949840c0e..2b489f80581e62dd0c9c44b98e037444889c5fe8 100644 (file)
@@ -466,6 +466,7 @@ All (GUI):
 - Added wxComboBox::IsListEmpty() and IsTextEmpty().
 - Added wxDataViewCtrl::GetSelectedItemsCount() and HasSelection().
 - Added wxFLP_SMALL and wxDIRP_SMALL styles.
+- Added support for saving alpha with TIFF images.
 
 OSX:
 
index c7eb7732e5a6d382dd9f9d3b4670b13f35821be0..cc0e1abe7836341dd076d9f67f33fa0066b03c74 100644 (file)
@@ -346,8 +346,8 @@ const unsigned char wxIMAGE_ALPHA_OPAQUE = 0xff;
     handlers have full alpha channel support for loading so if you want to use
     alpha you have to use one of these formats. If you initialize the image
     alpha channel yourself using wxImage::SetAlpha, you should save it in
-    either PNG or TGA format to avoid losing it as these are the only handlers
-    that currently support saving with alpha.
+    either PNG, TGA, or TIFF format to avoid losing it as these are the only
+    handlers that currently support saving with alpha.
 
 
     @section image_handlers Available image handlers
@@ -363,7 +363,7 @@ const unsigned char wxIMAGE_ALPHA_OPAQUE = 0xff;
     - wxGIFHandler: For loading and saving (see below).
     - wxPCXHandler: For loading and saving (see below).
     - wxPNMHandler: For loading and saving (see below).
-    - wxTIFFHandler: For loading (including alpha support) and saving.
+    - wxTIFFHandler: For loading and saving. Includes alpha support.
     - wxTGAHandler: For loading and saving. Includes alpha support.
     - wxIFFHandler: For loading only.
     - wxXPMHandler: For loading and saving.
index d522f56e936b48bfb9e07432f00e6ec20525ad7a..3dccff96183fd570dadf5bbc442f36a5213f74bd 100644 (file)
@@ -619,7 +619,8 @@ bool wxTIFFHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbo
         return false;
     }
 
-    TIFFSetField(tif, TIFFTAG_IMAGEWIDTH,  (uint32)image->GetWidth());
+    const int imageWidth = image->GetWidth();
+    TIFFSetField(tif, TIFFTAG_IMAGEWIDTH,  (uint32) imageWidth);
     TIFFSetField(tif, TIFFTAG_IMAGELENGTH, (uint32)image->GetHeight());
     TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
     TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
@@ -683,37 +684,62 @@ bool wxTIFFHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbo
             spp = 1;
         }
     }
-    else if (spp == 1)
+    else if (spp <= 2)
     {
         photometric = PHOTOMETRIC_MINISWHITE;
     }
 
-    const bool isColouredImage = (spp > 1)
-        && (photometric != PHOTOMETRIC_MINISWHITE)
-        && (photometric != PHOTOMETRIC_MINISBLACK);
+    const bool hasAlpha = image->HasAlpha();
 
     int compression = image->GetOptionInt(wxIMAGE_OPTION_TIFF_COMPRESSION);
-    if ( !compression )
+    if ( !compression || (compression == COMPRESSION_JPEG && hasAlpha) )
     {
-        // we can't use COMPRESSION_LZW because current version of libtiff
+        // We can't use COMPRESSION_LZW because current version of libtiff
         // doesn't implement it ("no longer implemented due to Unisys patent
         // enforcement") and other compression methods are lossy so we
-        // shouldn't use them by default -- and the only remaining one is none
+        // shouldn't use them by default -- and the only remaining one is none.
+        // Also JPEG compression for alpha images is not a good idea (viewers
+        // not opening the image properly).
         compression = COMPRESSION_NONE;
     }
 
-    TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, spp);
+    if
+    (
+        (photometric == PHOTOMETRIC_RGB && spp == 4)
+        || (photometric <= PHOTOMETRIC_MINISBLACK && spp == 2)
+    )
+    {
+        // Compensate for user passing a SamplesPerPixel that includes
+        // the alpha channel.
+        spp--;
+    }
+
+
+    int extraSamples = hasAlpha ? 1 : 0;
+
+    TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, spp + extraSamples);
     TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bps);
     TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, photometric);
     TIFFSetField(tif, TIFFTAG_COMPRESSION, compression);
 
-    // scanlinesize is determined by spp and bps
+    if (extraSamples)
+    {
+        uint16 extra[] = { EXTRASAMPLE_UNSPECIFIED };
+        TIFFSetField(tif, TIFFTAG_EXTRASAMPLES, (long) 1, &extra);
+    }
+
+    // scanlinesize is determined by spp+extraSamples and bps
     const tsize_t linebytes =
-        (tsize_t)((image->GetWidth() * spp * bps + 7) / 8);
+        (tsize_t)((imageWidth * (spp + extraSamples) * bps + 7) / 8);
 
     unsigned char *buf;
 
-    if (TIFFScanlineSize(tif) > linebytes || !isColouredImage)
+    const bool isColouredImage = (spp > 1)
+        && (photometric != PHOTOMETRIC_MINISWHITE)
+        && (photometric != PHOTOMETRIC_MINISBLACK);
+
+
+    if (TIFFScanlineSize(tif) > linebytes || !isColouredImage || hasAlpha)
     {
         buf = (unsigned char *)_TIFFmalloc(TIFFScanlineSize(tif));
         if (!buf)
@@ -735,15 +761,16 @@ bool wxTIFFHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbo
 
     TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP,TIFFDefaultStripSize(tif, (uint32) -1));
 
-    const int bitsPerPixel = spp * bps;
+    const int bitsPerPixel = (spp + extraSamples) * bps;
+    const int bytesPerPixel = (bitsPerPixel + 7) / 8;
     const int pixelsPerByte = 8 / bitsPerPixel;
     int remainingPixelCount = 0;
 
     if (pixelsPerByte)
     {
         // How many pixels to write in the last byte column?
-        remainingPixelCount = image->GetWidth() % pixelsPerByte;
-        if (!remainingPixelCount) remainingPixelCount = 8;
+        remainingPixelCount = imageWidth % pixelsPerByte;
+        if (!remainingPixelCount) remainingPixelCount = pixelsPerByte;
     }
 
     const bool minIsWhite = (photometric == PHOTOMETRIC_MINISWHITE);
@@ -754,12 +781,25 @@ bool wxTIFFHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbo
         {
             if (isColouredImage)
             {
-                // color image
-                memcpy(buf, ptr, image->GetWidth() * 3);
+                // colour image
+                if (hasAlpha)
+                {
+                    for ( int column = 0; column < imageWidth; column++ )
+                    {
+                        buf[column*4    ] = ptr[column*3    ];
+                        buf[column*4 + 1] = ptr[column*3 + 1];
+                        buf[column*4 + 2] = ptr[column*3 + 2];
+                        buf[column*4 + 3] = image->GetAlpha(column, row);
+                    }
+                }
+                else
+                {
+                    memcpy(buf, ptr, imageWidth * 3);
+                }
             }
             else if (spp * bps == 8) // greyscale image
             {
-                for ( int column = 0; column < linebytes; column++ )
+                for ( int column = 0; column < imageWidth; column++ )
                 {
                     uint8 value = ptr[column*3 + 1];
                     if (minIsWhite)
@@ -767,7 +807,14 @@ bool wxTIFFHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbo
                         value = 255 - value;
                     }
 
-                    buf[column] = value;
+                    buf[column * bytesPerPixel] = value;
+
+                    if (hasAlpha)
+                    {
+                        value = image->GetAlpha(column, row);
+                        buf[column*bytesPerPixel+1]
+                            = minIsWhite ? 255 - value : value;
+                    }
                 }
             }
             else // black and white image
@@ -780,10 +827,18 @@ bool wxTIFFHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbo
                         : remainingPixelCount;
                     for ( int bp = 0; bp < pixelsPerByteCount; bp++ )
                     {
-                        if ( (ptr[column*24 + bp*3 + 1] <=127) == minIsWhite )
+                        if ( (ptr[column * 3 * pixelsPerByte + bp*3 + 1] <=127)
+                            == minIsWhite )
                         {
                             // check only green as this is sufficient
-                            reverse = (uint8)(reverse | 128 >> bp);
+                            reverse |= (uint8) (128 >> (bp * bitsPerPixel));
+                        }
+
+                        if (hasAlpha
+                            && (image->GetAlpha(column * pixelsPerByte + bp,
+                                    row) <= 127) == minIsWhite)
+                        {
+                            reverse |= (uint8) (64 >> (bp * bitsPerPixel));
                         }
                     }
 
@@ -806,7 +861,7 @@ bool wxTIFFHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbo
             return false;
         }
 
-        ptr += image->GetWidth()*3;
+        ptr += imageWidth * 3;
     }
 
     (void) TIFFClose(tif);
index d063621dcc1527b8922c96fbfe3e269c0f7ce317..f0f8ad2c4c6e8d846c2a6bf8e5e60dff31fdfd56 100644 (file)
@@ -908,7 +908,8 @@ void CompareImage(const wxImageHandler& handler, const wxImage& image,
 
     const bool testAlpha = (properties & wxIMAGE_HAVE_ALPHA) != 0;
     if (testAlpha
-        && !(type == wxBITMAP_TYPE_PNG || type == wxBITMAP_TYPE_TGA) )
+        && !(type == wxBITMAP_TYPE_PNG || type == wxBITMAP_TYPE_TGA
+            || type == wxBITMAP_TYPE_TIFF) )
     {
         // don't test images with alpha if this handler doesn't support alpha
         return;
@@ -965,12 +966,28 @@ void CompareImage(const wxImageHandler& handler, const wxImage& image,
     );
 }
 
+static void SetAlpha(wxImage *image)
+{
+    image->SetAlpha();
+
+    unsigned char *ptr = image->GetAlpha();
+    const int width = image->GetWidth();
+    const int height = image->GetHeight();
+    for (int y = 0; y < height; ++y)
+    {
+        for (int x = 0; x < width; ++x)
+        {
+            ptr[y*width + x] = (x*y) & wxIMAGE_ALPHA_OPAQUE;
+        }
+    }
+}
+
 void ImageTestCase::CompareSavedImage()
 {
     // FIXME-VC6: Pre-declare the loop variables for compatibility with
     // pre-standard compilers such as MSVC6 that don't implement proper scope
     // for the variables declared in the for loops.
-    int i, x, y;
+    int i;
 
     wxImage expected24("horse.png");
     CPPUNIT_ASSERT( expected24.IsOk() );
@@ -992,17 +1009,8 @@ void ImageTestCase::CompareSavedImage()
 
     // Create an image with alpha based on the loaded image
     wxImage expected32(expected24);
-    expected32.SetAlpha();
 
-    int width = expected32.GetWidth();
-    int height = expected32.GetHeight();
-    for (y = 0; y < height; ++y)
-    {
-        for (x = 0; x < width; ++x)
-        {
-            expected32.SetAlpha(x, y, (x*y) & wxIMAGE_ALPHA_OPAQUE);
-        }
-    }
+    SetAlpha(&expected32);
 
     const wxList& list = wxImage::GetHandlers();
     for ( wxList::compatibility_iterator node = list.GetFirst();
@@ -1090,9 +1098,19 @@ void ImageTestCase::SavePNG()
 
 }
 
-static void TestTIFFImage(const wxString& option, int value)
+static void TestTIFFImage(const wxString& option, int value,
+    const wxImage *compareImage = NULL)
 {
-    wxImage image("horse.png");
+    wxImage image;
+    if (compareImage)
+    {
+        image = *compareImage;
+    }
+    else
+    {
+        (void) image.LoadFile("horse.png");
+    }
+    CPPUNIT_ASSERT( image.IsOk() );
 
     wxMemoryOutputStream memOut;
     image.SetOption(option, value);
@@ -1110,6 +1128,8 @@ static void TestTIFFImage(const wxString& option, int value)
 
     WX_ASSERT_EQUAL_MESSAGE(("While testing for %s", option),
         value, savedImage.GetOptionInt(option));
+
+    WX_ASSERT_EQUAL_MESSAGE(("HasAlpha() not equal"), image.HasAlpha(), savedImage.HasAlpha());
 }
 
 void ImageTestCase::SaveTIFF()
@@ -1118,6 +1138,20 @@ void ImageTestCase::SaveTIFF()
     TestTIFFImage(wxIMAGE_OPTION_TIFF_SAMPLESPERPIXEL, 1);
     TestTIFFImage(wxIMAGE_OPTION_TIFF_PHOTOMETRIC, 0/*PHOTOMETRIC_MINISWHITE*/);
     TestTIFFImage(wxIMAGE_OPTION_TIFF_PHOTOMETRIC, 1/*PHOTOMETRIC_MINISBLACK*/);
+
+    wxImage alphaImage("horse.png");
+    CPPUNIT_ASSERT( alphaImage.IsOk() );
+    SetAlpha(&alphaImage);
+
+    // RGB with alpha
+    TestTIFFImage(wxIMAGE_OPTION_TIFF_SAMPLESPERPIXEL, 4, &alphaImage);
+
+    // Grey with alpha
+    TestTIFFImage(wxIMAGE_OPTION_TIFF_SAMPLESPERPIXEL, 2, &alphaImage);
+
+    // B/W with alpha
+    alphaImage.SetOption(wxIMAGE_OPTION_TIFF_BITSPERSAMPLE, 1);
+    TestTIFFImage(wxIMAGE_OPTION_TIFF_SAMPLESPERPIXEL, 2, &alphaImage);
 }
 
 void ImageTestCase::SaveAnimatedGIF()
index 4c84b5cf51b0ea3021df516289dc628ccde18402..ceb4ff2f7622742a41a8f63445fa3a450724ded8 100644 (file)
@@ -31,9 +31,10 @@ struct assertion_traits<wxImage>
 
     static std::string toString(const wxImage& image)
     {
-        return wxString::Format("image of size %d*%d",
+        return wxString::Format("image of size %d*%d with%s alpha",
                                 image.GetWidth(),
-                                image.GetHeight())
+                                image.GetHeight(),
+                                image.HasAlpha() ? "" : "out")
                 .ToStdString();
     }
 };