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 wxComboBox::IsListEmpty() and IsTextEmpty().
|
||||||
- Added wxDataViewCtrl::GetSelectedItemsCount() and HasSelection().
|
- Added wxDataViewCtrl::GetSelectedItemsCount() and HasSelection().
|
||||||
- Added wxFLP_SMALL and wxDIRP_SMALL styles.
|
- Added wxFLP_SMALL and wxDIRP_SMALL styles.
|
||||||
|
- Added support for saving alpha with TIFF images.
|
||||||
|
|
||||||
OSX:
|
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
|
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 you have to use one of these formats. If you initialize the image
|
||||||
alpha channel yourself using wxImage::SetAlpha, you should save it in
|
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
|
either PNG, TGA, or TIFF format to avoid losing it as these are the only
|
||||||
that currently support saving with alpha.
|
handlers that currently support saving with alpha.
|
||||||
|
|
||||||
|
|
||||||
@section image_handlers Available image handlers
|
@section image_handlers Available image handlers
|
||||||
@ -363,7 +363,7 @@ const unsigned char wxIMAGE_ALPHA_OPAQUE = 0xff;
|
|||||||
- wxGIFHandler: For loading and saving (see below).
|
- wxGIFHandler: For loading and saving (see below).
|
||||||
- wxPCXHandler: For loading and saving (see below).
|
- wxPCXHandler: For loading and saving (see below).
|
||||||
- wxPNMHandler: 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.
|
- wxTGAHandler: For loading and saving. Includes alpha support.
|
||||||
- wxIFFHandler: For loading only.
|
- wxIFFHandler: For loading only.
|
||||||
- wxXPMHandler: For loading and saving.
|
- wxXPMHandler: For loading and saving.
|
||||||
|
@ -619,7 +619,8 @@ bool wxTIFFHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbo
|
|||||||
return false;
|
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_IMAGELENGTH, (uint32)image->GetHeight());
|
||||||
TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
|
TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
|
||||||
TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
|
TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
|
||||||
@ -683,37 +684,62 @@ bool wxTIFFHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbo
|
|||||||
spp = 1;
|
spp = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (spp == 1)
|
else if (spp <= 2)
|
||||||
{
|
{
|
||||||
photometric = PHOTOMETRIC_MINISWHITE;
|
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)
|
const bool isColouredImage = (spp > 1)
|
||||||
&& (photometric != PHOTOMETRIC_MINISWHITE)
|
&& (photometric != PHOTOMETRIC_MINISWHITE)
|
||||||
&& (photometric != PHOTOMETRIC_MINISBLACK);
|
&& (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);
|
if (TIFFScanlineSize(tif) > linebytes || !isColouredImage || hasAlpha)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
buf = (unsigned char *)_TIFFmalloc(TIFFScanlineSize(tif));
|
buf = (unsigned char *)_TIFFmalloc(TIFFScanlineSize(tif));
|
||||||
if (!buf)
|
if (!buf)
|
||||||
@ -735,15 +761,16 @@ bool wxTIFFHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbo
|
|||||||
|
|
||||||
TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP,TIFFDefaultStripSize(tif, (uint32) -1));
|
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;
|
const int pixelsPerByte = 8 / bitsPerPixel;
|
||||||
int remainingPixelCount = 0;
|
int remainingPixelCount = 0;
|
||||||
|
|
||||||
if (pixelsPerByte)
|
if (pixelsPerByte)
|
||||||
{
|
{
|
||||||
// How many pixels to write in the last byte column?
|
// How many pixels to write in the last byte column?
|
||||||
remainingPixelCount = image->GetWidth() % pixelsPerByte;
|
remainingPixelCount = imageWidth % pixelsPerByte;
|
||||||
if (!remainingPixelCount) remainingPixelCount = 8;
|
if (!remainingPixelCount) remainingPixelCount = pixelsPerByte;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool minIsWhite = (photometric == PHOTOMETRIC_MINISWHITE);
|
const bool minIsWhite = (photometric == PHOTOMETRIC_MINISWHITE);
|
||||||
@ -754,12 +781,25 @@ bool wxTIFFHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbo
|
|||||||
{
|
{
|
||||||
if (isColouredImage)
|
if (isColouredImage)
|
||||||
{
|
{
|
||||||
// color image
|
// colour image
|
||||||
memcpy(buf, ptr, image->GetWidth() * 3);
|
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
|
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];
|
uint8 value = ptr[column*3 + 1];
|
||||||
if (minIsWhite)
|
if (minIsWhite)
|
||||||
@ -767,7 +807,14 @@ bool wxTIFFHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbo
|
|||||||
value = 255 - value;
|
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
|
else // black and white image
|
||||||
@ -780,10 +827,18 @@ bool wxTIFFHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbo
|
|||||||
: remainingPixelCount;
|
: remainingPixelCount;
|
||||||
for ( int bp = 0; bp < pixelsPerByteCount; bp++ )
|
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
|
// 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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr += image->GetWidth()*3;
|
ptr += imageWidth * 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
(void) TIFFClose(tif);
|
(void) TIFFClose(tif);
|
||||||
|
@ -908,7 +908,8 @@ void CompareImage(const wxImageHandler& handler, const wxImage& image,
|
|||||||
|
|
||||||
const bool testAlpha = (properties & wxIMAGE_HAVE_ALPHA) != 0;
|
const bool testAlpha = (properties & wxIMAGE_HAVE_ALPHA) != 0;
|
||||||
if (testAlpha
|
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
|
// don't test images with alpha if this handler doesn't support alpha
|
||||||
return;
|
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()
|
void ImageTestCase::CompareSavedImage()
|
||||||
{
|
{
|
||||||
// FIXME-VC6: Pre-declare the loop variables for compatibility with
|
// FIXME-VC6: Pre-declare the loop variables for compatibility with
|
||||||
// pre-standard compilers such as MSVC6 that don't implement proper scope
|
// pre-standard compilers such as MSVC6 that don't implement proper scope
|
||||||
// for the variables declared in the for loops.
|
// for the variables declared in the for loops.
|
||||||
int i, x, y;
|
int i;
|
||||||
|
|
||||||
wxImage expected24("horse.png");
|
wxImage expected24("horse.png");
|
||||||
CPPUNIT_ASSERT( expected24.IsOk() );
|
CPPUNIT_ASSERT( expected24.IsOk() );
|
||||||
@ -992,17 +1009,8 @@ void ImageTestCase::CompareSavedImage()
|
|||||||
|
|
||||||
// Create an image with alpha based on the loaded image
|
// Create an image with alpha based on the loaded image
|
||||||
wxImage expected32(expected24);
|
wxImage expected32(expected24);
|
||||||
expected32.SetAlpha();
|
|
||||||
|
|
||||||
int width = expected32.GetWidth();
|
SetAlpha(&expected32);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const wxList& list = wxImage::GetHandlers();
|
const wxList& list = wxImage::GetHandlers();
|
||||||
for ( wxList::compatibility_iterator node = list.GetFirst();
|
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;
|
wxMemoryOutputStream memOut;
|
||||||
image.SetOption(option, value);
|
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),
|
WX_ASSERT_EQUAL_MESSAGE(("While testing for %s", option),
|
||||||
value, savedImage.GetOptionInt(option));
|
value, savedImage.GetOptionInt(option));
|
||||||
|
|
||||||
|
WX_ASSERT_EQUAL_MESSAGE(("HasAlpha() not equal"), image.HasAlpha(), savedImage.HasAlpha());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageTestCase::SaveTIFF()
|
void ImageTestCase::SaveTIFF()
|
||||||
@ -1118,6 +1138,20 @@ void ImageTestCase::SaveTIFF()
|
|||||||
TestTIFFImage(wxIMAGE_OPTION_TIFF_SAMPLESPERPIXEL, 1);
|
TestTIFFImage(wxIMAGE_OPTION_TIFF_SAMPLESPERPIXEL, 1);
|
||||||
TestTIFFImage(wxIMAGE_OPTION_TIFF_PHOTOMETRIC, 0/*PHOTOMETRIC_MINISWHITE*/);
|
TestTIFFImage(wxIMAGE_OPTION_TIFF_PHOTOMETRIC, 0/*PHOTOMETRIC_MINISWHITE*/);
|
||||||
TestTIFFImage(wxIMAGE_OPTION_TIFF_PHOTOMETRIC, 1/*PHOTOMETRIC_MINISBLACK*/);
|
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()
|
void ImageTestCase::SaveAnimatedGIF()
|
||||||
|
@ -31,9 +31,10 @@ struct assertion_traits<wxImage>
|
|||||||
|
|
||||||
static std::string toString(const wxImage& image)
|
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.GetWidth(),
|
||||||
image.GetHeight())
|
image.GetHeight(),
|
||||||
|
image.HasAlpha() ? "" : "out")
|
||||||
.ToStdString();
|
.ToStdString();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user