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
This commit is contained in:
Dimitri Schoolwerth 2011-08-29 01:13:06 +00:00
parent ebbaec82fa
commit ab176b4b50
5 changed files with 143 additions and 52 deletions

View 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:

View 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.

View 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 hasAlpha = image->HasAlpha();
int compression = image->GetOptionInt(wxIMAGE_OPTION_TIFF_COMPRESSION);
if ( !compression || (compression == COMPRESSION_JPEG && hasAlpha) )
{
// 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.
// Also JPEG compression for alpha images is not a good idea (viewers
// not opening the image properly).
compression = COMPRESSION_NONE;
}
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);
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)((imageWidth * (spp + extraSamples) * bps + 7) / 8);
unsigned char *buf;
const bool isColouredImage = (spp > 1)
&& (photometric != PHOTOMETRIC_MINISWHITE)
&& (photometric != PHOTOMETRIC_MINISBLACK);
int compression = image->GetOptionInt(wxIMAGE_OPTION_TIFF_COMPRESSION);
if ( !compression )
{
// 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
compression = COMPRESSION_NONE;
}
TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, spp);
TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bps);
TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, photometric);
TIFFSetField(tif, TIFFTAG_COMPRESSION, compression);
// scanlinesize is determined by spp and bps
const tsize_t linebytes =
(tsize_t)((image->GetWidth() * spp * bps + 7) / 8);
unsigned char *buf;
if (TIFFScanlineSize(tif) > linebytes || !isColouredImage)
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);

View 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()

View 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();
}
};