+ TIFF *tif = TIFFwxOpen( stream, "image", "w" );
+
+ if (!tif)
+ {
+ if (verbose)
+ {
+ wxLogError( _("TIFF: Error saving image.") );
+ }
+
+ return false;
+ }
+
+ 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);
+
+ // save the image resolution if we have it
+ int xres, yres;
+ const wxImageResolution res = GetResolutionFromOptions(*image, &xres, &yres);
+ uint16 tiffRes;
+ switch ( res )
+ {
+ default:
+ wxFAIL_MSG( wxT("unknown image resolution units") );
+ // fall through
+
+ case wxIMAGE_RESOLUTION_NONE:
+ tiffRes = RESUNIT_NONE;
+ break;
+
+ case wxIMAGE_RESOLUTION_INCHES:
+ tiffRes = RESUNIT_INCH;
+ break;
+
+ case wxIMAGE_RESOLUTION_CM:
+ tiffRes = RESUNIT_CENTIMETER;
+ break;
+ }
+
+ if ( tiffRes != RESUNIT_NONE )
+ {
+ TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, tiffRes);
+ TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float)xres);
+ TIFFSetField(tif, TIFFTAG_YRESOLUTION, (float)yres);
+ }
+
+
+ int spp = image->GetOptionInt(wxIMAGE_OPTION_TIFF_SAMPLESPERPIXEL);
+ if ( !spp )
+ spp = 3;
+
+ int bps = image->GetOptionInt(wxIMAGE_OPTION_TIFF_BITSPERSAMPLE);
+ if ( !bps )
+ {
+ bps = 8;
+ }
+ else if (bps == 1)
+ {
+ // One bit per sample combined with 3 samples per pixel is
+ // not allowed and crashes libtiff.
+ spp = 1;
+ }
+
+ int photometric = PHOTOMETRIC_RGB;
+
+ if ( image->HasOption(wxIMAGE_OPTION_TIFF_PHOTOMETRIC) )
+ {
+ photometric = image->GetOptionInt(wxIMAGE_OPTION_TIFF_PHOTOMETRIC);
+ if (photometric == PHOTOMETRIC_MINISWHITE
+ || photometric == PHOTOMETRIC_MINISBLACK)
+ {
+ // either b/w or greyscale
+ 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);
+
+
+ if (TIFFScanlineSize(tif) > linebytes || !isColouredImage || hasAlpha)
+ {
+ buf = (unsigned char *)_TIFFmalloc(TIFFScanlineSize(tif));
+ if (!buf)
+ {
+ if (verbose)
+ {
+ wxLogError( _("TIFF: Couldn't allocate memory.") );
+ }
+
+ TIFFClose( tif );
+
+ return false;
+ }
+ }
+ else
+ {
+ buf = NULL;
+ }
+
+ TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP,TIFFDefaultStripSize(tif, (uint32) -1));
+
+ 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 = imageWidth % pixelsPerByte;
+ if (!remainingPixelCount) remainingPixelCount = pixelsPerByte;
+ }
+
+ const bool minIsWhite = (photometric == PHOTOMETRIC_MINISWHITE);
+ unsigned char *ptr = image->GetData();
+ for ( int row = 0; row < image->GetHeight(); row++ )
+ {
+ if ( buf )
+ {
+ if (isColouredImage)
+ {
+ // 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 < imageWidth; column++ )
+ {
+ uint8 value = ptr[column*3 + 1];
+ if (minIsWhite)
+ {
+ value = 255 - 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
+ {
+ for ( int column = 0; column < linebytes; column++ )
+ {
+ uint8 reverse = 0;
+ int pixelsPerByteCount = (column + 1 != linebytes)
+ ? pixelsPerByte
+ : remainingPixelCount;
+ for ( int bp = 0; bp < pixelsPerByteCount; bp++ )
+ {
+ if ( (ptr[column * 3 * pixelsPerByte + bp*3 + 1] <=127)
+ == minIsWhite )
+ {
+ // check only green as this is sufficient
+ reverse |= (uint8) (128 >> (bp * bitsPerPixel));
+ }
+
+ if (hasAlpha
+ && (image->GetAlpha(column * pixelsPerByte + bp,
+ row) <= 127) == minIsWhite)
+ {
+ reverse |= (uint8) (64 >> (bp * bitsPerPixel));
+ }
+ }
+
+ buf[column] = reverse;
+ }
+ }
+ }
+
+ if ( TIFFWriteScanline(tif, buf ? buf : ptr, (uint32)row, 0) < 0 )
+ {
+ if (verbose)
+ {
+ wxLogError( _("TIFF: Error writing image.") );
+ }
+
+ TIFFClose( tif );
+ if (buf)
+ _TIFFfree(buf);
+
+ return false;
+ }
+
+ ptr += imageWidth * 3;
+ }
+
+ (void) TIFFClose(tif);
+
+ if (buf)
+ _TIFFfree(buf);
+
+ return true;