// Name: src/common/imagtiff.cpp
// Purpose: wxImage TIFF handler
// Author: Robert Roebling
-// RCS-ID: $Id$
// Copyright: (c) Robert Roebling
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
#include "wx/wfstream.h"
#ifndef TIFFLINKAGEMODE
- #if defined(__WATCOMC__) && defined(__WXMGL__)
- #define TIFFLINKAGEMODE cdecl
- #else
- #define TIFFLINKAGEMODE LINKAGEMODE
- #endif
+ #define TIFFLINKAGEMODE LINKAGEMODE
#endif
// ============================================================================
TIFFGetField( tif, TIFFTAG_IMAGEWIDTH, &w );
TIFFGetField( tif, TIFFTAG_IMAGELENGTH, &h );
- uint16 photometric;
- uint16 samplesPerPixel;
+ uint16 samplesPerPixel = 0;
+ (void) TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &samplesPerPixel);
+
+ uint16 bitsPerSample = 0;
+ (void) TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bitsPerSample);
+
uint16 extraSamples;
uint16* samplesInfo;
- TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &samplesPerPixel);
TIFFGetFieldDefaulted(tif, TIFFTAG_EXTRASAMPLES,
&extraSamples, &samplesInfo);
+
+ uint16 photometric;
if (!TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric))
{
photometric = PHOTOMETRIC_MINISWHITE;
}
const bool hasAlpha = (extraSamples >= 1
- && ((samplesInfo[0] == EXTRASAMPLE_UNSPECIFIED && samplesPerPixel > 3)
+ && ((samplesInfo[0] == EXTRASAMPLE_UNSPECIFIED)
|| samplesInfo[0] == EXTRASAMPLE_ASSOCALPHA
|| samplesInfo[0] == EXTRASAMPLE_UNASSALPHA))
|| (extraSamples == 0 && samplesPerPixel == 4
bool ok = true;
char msg[1024] = "";
- if ( !TIFFRGBAImageOK(tif, msg)
- && planarConfig == PLANARCONFIG_CONTIG
- && samplesPerPixel == 2 && extraSamples == 1)
- {
+ if
+ (
+ (planarConfig == PLANARCONFIG_CONTIG && samplesPerPixel == 2
+ && extraSamples == 1)
+ &&
+ (
+ ( !TIFFRGBAImageOK(tif, msg) )
+ || (bitsPerSample == 8)
+ )
+ )
+ {
+ const bool isGreyScale = (bitsPerSample == 8);
unsigned char *buf = (unsigned char *)_TIFFmalloc(TIFFScanlineSize(tif));
uint32 pos = 0;
- const int minValue = (photometric == PHOTOMETRIC_MINISWHITE) ? 255 : 0;
+ const bool minIsWhite = (photometric == PHOTOMETRIC_MINISWHITE);
+ const int minValue = minIsWhite ? 255 : 0;
const int maxValue = 255 - minValue;
/*
break;
}
- for (uint32 x = 0; x < w; ++x)
+ if (isGreyScale)
{
- int mask = buf[x*2/8] << ((x*2)%8);
+ for (uint32 x = 0; x < w; ++x)
+ {
+ uint8 val = minIsWhite ? 255 - buf[x*2] : buf[x*2];
+ uint8 alpha = minIsWhite ? 255 - buf[x*2+1] : buf[x*2+1];
+ raster[pos] = val + (val << 8) + (val << 16)
+ + (alpha << 24);
+ pos++;
+ }
+ }
+ else
+ {
+ for (uint32 x = 0; x < w; ++x)
+ {
+ int mask = buf[x*2/8] << ((x*2)%8);
- uint8 val = mask & 128 ? maxValue : minValue;
- raster[pos] = val + (val << 8) + (val << 16)
- + ((mask & 64 ? maxValue : minValue) << 24);
- pos++;
+ uint8 val = mask & 128 ? maxValue : minValue;
+ raster[pos] = val + (val << 8) + (val << 16)
+ + ((mask & 64 ? maxValue : minValue) << 24);
+ pos++;
+ }
}
}
image->SetOption(wxIMAGE_OPTION_TIFF_PHOTOMETRIC, photometric);
- uint16 spp, bps, compression;
+ uint16 compression;
/*
- Read some baseline TIFF tags which helps when re-saving a TIFF
+ Copy some baseline TIFF tags which helps when re-saving a TIFF
to be similar to the original image.
*/
- if ( TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &spp) )
+ if (samplesPerPixel)
{
- image->SetOption(wxIMAGE_OPTION_TIFF_SAMPLESPERPIXEL, spp);
+ image->SetOption(wxIMAGE_OPTION_TIFF_SAMPLESPERPIXEL, samplesPerPixel);
}
- if ( TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bps) )
+ if (bitsPerSample)
{
- image->SetOption(wxIMAGE_OPTION_TIFF_BITSPERSAMPLE, bps);
+ image->SetOption(wxIMAGE_OPTION_TIFF_BITSPERSAMPLE, bitsPerSample);
}
if ( TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &compression) )
/*
Set the image resolution if it's available. Resolution tag is not
- dependant on RESOLUTIONUNIT != RESUNIT_NONE (according to TIFF spec).
+ dependent on RESOLUTIONUNIT != RESUNIT_NONE (according to TIFF spec).
*/
float resX, resY;
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);
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)
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);
{
if (isColouredImage)
{
- // color image
- memcpy(buf, ptr, image->GetWidth());
+ // 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)
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
: 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));
}
}
return false;
}
- ptr += image->GetWidth()*3;
+ ptr += imageWidth * 3;
}
(void) TIFFClose(tif);