From: Dimitri Schoolwerth Date: Mon, 29 Aug 2011 01:13:06 +0000 (+0000) Subject: Added alpha saving support to TIFF image handler. X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/ab176b4b503d67eb4ac4b45801d8e7c54502bbc9 Added alpha saving support to TIFF image handler. 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 --- diff --git a/docs/changes.txt b/docs/changes.txt index 6fb7429ec8..2b489f8058 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -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: diff --git a/interface/wx/image.h b/interface/wx/image.h index c7eb7732e5..cc0e1abe78 100644 --- a/interface/wx/image.h +++ b/interface/wx/image.h @@ -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. diff --git a/src/common/imagtiff.cpp b/src/common/imagtiff.cpp index d522f56e93..3dccff9618 100644 --- a/src/common/imagtiff.cpp +++ b/src/common/imagtiff.cpp @@ -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); diff --git a/tests/image/image.cpp b/tests/image/image.cpp index d063621dcc..f0f8ad2c4c 100644 --- a/tests/image/image.cpp +++ b/tests/image/image.cpp @@ -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() diff --git a/tests/testimage.h b/tests/testimage.h index 4c84b5cf51..ceb4ff2f76 100644 --- a/tests/testimage.h +++ b/tests/testimage.h @@ -31,9 +31,10 @@ struct assertion_traits 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(); } };