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:
parent
ebbaec82fa
commit
ab176b4b50
@ -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:
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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()
|
||||
|
@ -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();
|
||||
}
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user