// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
+// ============================================================================
+// declarations
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// headers
+// ----------------------------------------------------------------------------
+
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#if wxUSE_IMAGE && wxUSE_LIBTIFF
#include "wx/imagtiff.h"
+#include "wx/versioninfo.h"
#ifndef WX_PRECOMP
#include "wx/log.h"
#include "wx/intl.h"
#include "wx/bitmap.h"
#include "wx/module.h"
+ #include "wx/wxcrtvararg.h"
#endif
extern "C"
{
+#ifdef __DMC__
+ #include "tif_config.h"
+#endif
#include "tiff.h"
#include "tiffio.h"
}
#endif
#endif
+// ============================================================================
+// implementation
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// TIFF library error/warning handlers
+// ----------------------------------------------------------------------------
+
+static wxString
+FormatTiffMessage(const char *module, const char *fmt, va_list ap)
+{
+ char buf[512];
+ if ( wxCRT_VsnprintfA(buf, WXSIZEOF(buf), fmt, ap) <= 0 )
+ {
+ // this isn't supposed to happen, but if it does, it's better
+ // than nothing
+ strcpy(buf, "Incorrectly formatted TIFF message");
+ }
+ buf[WXSIZEOF(buf)-1] = 0; // make sure it is always NULL-terminated
+
+ wxString msg(buf);
+ if ( module )
+ msg += wxString::Format(_(" (in module \"%s\")"), module);
+
+ return msg;
+}
+
+extern "C"
+{
+
+static void
+TIFFwxWarningHandler(const char* module, const char *fmt, va_list ap)
+{
+ wxLogWarning("%s", FormatTiffMessage(module, fmt, ap));
+}
+
+static void
+TIFFwxErrorHandler(const char* module, const char *fmt, va_list ap)
+{
+ wxLogError("%s", FormatTiffMessage(module, fmt, ap));
+}
+
+} // extern "C"
+
//-----------------------------------------------------------------------------
// wxTIFFHandler
//-----------------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS(wxTIFFHandler,wxImageHandler)
+wxTIFFHandler::wxTIFFHandler()
+{
+ m_name = wxT("TIFF file");
+ m_extension = wxT("tif");
+ m_altExtensions.Add(wxT("tiff"));
+ m_type = wxBITMAP_TYPE_TIF;
+ m_mime = wxT("image/tiff");
+ TIFFSetWarningHandler((TIFFErrorHandler) TIFFwxWarningHandler);
+ TIFFSetErrorHandler((TIFFErrorHandler) TIFFwxErrorHandler);
+}
+
#if wxUSE_STREAMS
// helper to translate our, possibly 64 bit, wxFileOffset to TIFF, always 32
toff_t tofs = wx_truncate_cast(toff_t, ofs);
wxCHECK_MSG( (wxFileOffset)tofs == ofs, (toff_t)-1,
- _T("TIFF library doesn't support large files") );
+ wxT("TIFF library doesn't support large files") );
return tofs;
}
{
}
-static void
-TIFFwxWarningHandler(const char* module,
- const char* WXUNUSED_IN_UNICODE(fmt),
- va_list WXUNUSED_IN_UNICODE(ap))
-{
- if (module != NULL)
- wxLogWarning(_("tiff module: %s"), wxString::FromAscii(module).c_str());
-
- // FIXME: this is not terrible informative but better than crashing!
-#if wxUSE_UNICODE
- wxLogWarning(_("TIFF library warning."));
-#else
- wxVLogWarning(fmt, ap);
-#endif
-}
-
-static void
-TIFFwxErrorHandler(const char* module,
- const char* WXUNUSED_IN_UNICODE(fmt),
- va_list WXUNUSED_IN_UNICODE(ap))
-{
- if (module != NULL)
- wxLogError(_("tiff module: %s"), wxString::FromAscii(module).c_str());
-
- // FIXME: as above
-#if wxUSE_UNICODE
- wxLogError(_("TIFF library error."));
-#else
- wxVLogError(fmt, ap);
-#endif
-}
-
} // extern "C"
TIFF*
return tif;
}
-wxTIFFHandler::wxTIFFHandler()
-{
- m_name = wxT("TIFF file");
- m_extension = wxT("tif");
- m_type = wxBITMAP_TYPE_TIF;
- m_mime = wxT("image/tiff");
- TIFFSetWarningHandler((TIFFErrorHandler) TIFFwxWarningHandler);
- TIFFSetErrorHandler((TIFFErrorHandler) TIFFwxErrorHandler);
-}
-
bool wxTIFFHandler::LoadFile( wxImage *image, wxInputStream& stream, bool verbose, int index )
{
if (index == -1)
if (!tif)
{
if (verbose)
+ {
wxLogError( _("TIFF: Error loading image.") );
+ }
return false;
}
if (!TIFFSetDirectory( tif, (tdir_t)index ))
{
if (verbose)
+ {
wxLogError( _("Invalid TIFF image index.") );
+ }
TIFFClose( tif );
}
uint32 w, h;
- uint32 npixels;
uint32 *raster;
TIFFGetField( tif, TIFFTAG_IMAGEWIDTH, &w );
TIFFGetField( tif, TIFFTAG_IMAGELENGTH, &h );
- npixels = w * h;
+ uint16 photometric;
+ uint16 samplesPerPixel;
+ uint16 extraSamples;
+ uint16* samplesInfo;
+ TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &samplesPerPixel);
+ TIFFGetFieldDefaulted(tif, TIFFTAG_EXTRASAMPLES,
+ &extraSamples, &samplesInfo);
+ if (!TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric))
+ {
+ photometric = PHOTOMETRIC_MINISWHITE;
+ }
+ const bool hasAlpha = (extraSamples >= 1
+ && ((samplesInfo[0] == EXTRASAMPLE_UNSPECIFIED && samplesPerPixel > 3)
+ || samplesInfo[0] == EXTRASAMPLE_ASSOCALPHA
+ || samplesInfo[0] == EXTRASAMPLE_UNASSALPHA))
+ || (extraSamples == 0 && samplesPerPixel == 4
+ && photometric == PHOTOMETRIC_RGB);
+
+ // guard against integer overflow during multiplication which could result
+ // in allocating a too small buffer and then overflowing it
+ const double bytesNeeded = (double)w * (double)h * sizeof(uint32);
+ if ( bytesNeeded >= wxUINT32_MAX )
+ {
+ if ( verbose )
+ {
+ wxLogError( _("TIFF: Image size is abnormally big.") );
+ }
+
+ TIFFClose(tif);
+
+ return false;
+ }
- raster = (uint32*) _TIFFmalloc( npixels * sizeof(uint32) );
+ raster = (uint32*) _TIFFmalloc( (uint32)bytesNeeded );
if (!raster)
{
if (verbose)
+ {
wxLogError( _("TIFF: Couldn't allocate memory.") );
+ }
TIFFClose( tif );
if (!image->Ok())
{
if (verbose)
+ {
wxLogError( _("TIFF: Couldn't allocate memory.") );
+ }
_TIFFfree( raster );
TIFFClose( tif );
return false;
}
+ if ( hasAlpha )
+ image->SetAlpha();
+
if (!TIFFReadRGBAImage( tif, w, h, raster, 0 ))
{
if (verbose)
+ {
wxLogError( _("TIFF: Error reading image.") );
+ }
_TIFFfree( raster );
image->Destroy();
return false;
}
- bool hasmask = false;
-
unsigned char *ptr = image->GetData();
ptr += w*3*(h-1);
+
+ unsigned char *alpha = hasAlpha ? image->GetAlpha() : NULL;
+ if ( hasAlpha )
+ alpha += w*(h-1);
+
uint32 pos = 0;
for (uint32 i = 0; i < h; i++)
{
for (uint32 j = 0; j < w; j++)
{
- unsigned char alpha = (unsigned char)TIFFGetA(raster[pos]);
- if (alpha < 127)
- {
- hasmask = true;
- ptr[0] = image->GetMaskRed();
- ptr++;
- ptr[0] = image->GetMaskGreen();
- ptr++;
- ptr[0] = image->GetMaskBlue();
- ptr++;
- }
- else
- {
- ptr[0] = (unsigned char)TIFFGetR(raster[pos]);
- ptr++;
- ptr[0] = (unsigned char)TIFFGetG(raster[pos]);
- ptr++;
- ptr[0] = (unsigned char)TIFFGetB(raster[pos]);
- ptr++;
- }
+ *(ptr++) = (unsigned char)TIFFGetR(raster[pos]);
+ *(ptr++) = (unsigned char)TIFFGetG(raster[pos]);
+ *(ptr++) = (unsigned char)TIFFGetB(raster[pos]);
+ if ( hasAlpha )
+ *(alpha++) = (unsigned char)TIFFGetA(raster[pos]);
+
pos++;
}
- ptr -= 2*w*3; // subtract line we just added plus one line
+
+ // subtract line we just added plus one line:
+ ptr -= 2*w*3;
+ if ( hasAlpha )
+ alpha -= 2*w;
+ }
+
+
+ uint16 spp, bpp, compression;
+ /*
+ Read some baseline TIFF tags which helps when re-saving a TIFF
+ to be similar to the original image.
+ */
+ if ( TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &spp) )
+ {
+ image->SetOption(wxIMAGE_OPTION_SAMPLESPERPIXEL, spp);
+ }
+
+ if ( TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bpp) )
+ {
+ image->SetOption(wxIMAGE_OPTION_BITSPERSAMPLE, bpp);
+ }
+
+ if ( TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &compression) )
+ {
+ image->SetOption(wxIMAGE_OPTION_COMPRESSION, compression);
+ }
+
+ // Set the resolution unit.
+ wxImageResolution resUnit = wxIMAGE_RESOLUTION_NONE;
+ uint16 tiffRes;
+ if ( TIFFGetFieldDefaulted(tif, TIFFTAG_RESOLUTIONUNIT, &tiffRes) )
+ {
+ switch (tiffRes)
+ {
+ default:
+ wxLogWarning(_("Unknown TIFF resolution unit %d ignored"),
+ tiffRes);
+ // fall through
+
+ case RESUNIT_NONE:
+ resUnit = wxIMAGE_RESOLUTION_NONE;
+ break;
+
+ case RESUNIT_INCH:
+ resUnit = wxIMAGE_RESOLUTION_INCHES;
+ break;
+
+ case RESUNIT_CENTIMETER:
+ resUnit = wxIMAGE_RESOLUTION_CM;
+ break;
+ }
+ }
+
+ image->SetOption(wxIMAGE_OPTION_RESOLUTIONUNIT, resUnit);
+
+ /*
+ Set the image resolution if it's available. Resolution tag is not
+ dependant on RESOLUTIONUNIT != RESUNIT_NONE (according to TIFF spec).
+ */
+ float resX, resY;
+
+ if ( TIFFGetField(tif, TIFFTAG_XRESOLUTION, &resX) )
+ {
+ /*
+ Use a string value to not lose precision.
+ rounding to int as cm and then converting to inch may
+ result in whole integer rounding error, eg. 201 instead of 200 dpi.
+ If an app wants an int, GetOptionInt will convert and round down.
+ */
+ image->SetOption(wxIMAGE_OPTION_RESOLUTIONX,
+ wxString::FromCDouble((double) resX));
+ }
+
+ if ( TIFFGetField(tif, TIFFTAG_YRESOLUTION, &resY) )
+ {
+ image->SetOption(wxIMAGE_OPTION_RESOLUTIONY,
+ wxString::FromCDouble((double) resY));
}
_TIFFfree( raster );
TIFFClose( tif );
- image->SetMask( hasmask );
-
return true;
}
-int wxTIFFHandler::GetImageCount( wxInputStream& stream )
+int wxTIFFHandler::DoGetImageCount( wxInputStream& stream )
{
TIFF *tif = TIFFwxOpen( stream, "image", "r" );
TIFFClose( tif );
+ // NOTE: this function modifies the current stream position but it's ok
+ // (see wxImageHandler::GetImageCount)
+
return dircount;
}
if (!tif)
{
if (verbose)
+ {
wxLogError( _("TIFF: Error saving image.") );
+ }
return false;
}
TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
- if ( image->HasOption(wxIMAGE_OPTION_RESOLUTIONX) &&
- image->HasOption(wxIMAGE_OPTION_RESOLUTIONY) )
+ // save the image resolution if we have it
+ int xres, yres;
+ const wxImageResolution res = GetResolutionFromOptions(*image, &xres, &yres);
+ uint16 tiffRes;
+ switch ( res )
{
- TIFFSetField(tif, TIFFTAG_XRESOLUTION,
- (float)image->GetOptionInt(wxIMAGE_OPTION_RESOLUTIONX));
- TIFFSetField(tif, TIFFTAG_YRESOLUTION,
- (float)image->GetOptionInt(wxIMAGE_OPTION_RESOLUTIONY));
+ 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_SAMPLESPERPIXEL);
if ( !spp )
spp = 3;
int bpp = image->GetOptionInt(wxIMAGE_OPTION_BITSPERSAMPLE);
if ( !bpp )
- bpp=8;
+ bpp = 8;
int compression = image->GetOptionInt(wxIMAGE_OPTION_COMPRESSION);
if ( !compression )
if (!buf)
{
if (verbose)
+ {
wxLogError( _("TIFF: Couldn't allocate memory.") );
+ }
TIFFClose( tif );
if ( TIFFWriteScanline(tif, buf ? buf : ptr, (uint32)row, 0) < 0 )
{
if (verbose)
+ {
wxLogError( _("TIFF: Error writing image.") );
+ }
TIFFClose( tif );
if (buf)
{
unsigned char hdr[2];
- if ( !stream.Read(&hdr[0], WXSIZEOF(hdr)) )
+ if ( !stream.Read(&hdr[0], WXSIZEOF(hdr)) ) // it's ok to modify the stream position here
return false;
return (hdr[0] == 'I' && hdr[1] == 'I') ||
#endif // wxUSE_STREAMS
+/*static*/ wxVersionInfo wxTIFFHandler::GetLibraryVersionInfo()
+{
+ int major,
+ minor,
+ micro;
+
+ const wxString ver(::TIFFGetVersion());
+ if ( wxSscanf(ver, "LIBTIFF, Version %d.%d.%d", &major, &minor, µ) != 3 )
+ {
+ wxLogDebug("Unrecognized libtiff version string \"%s\"", ver);
+
+ major =
+ minor =
+ micro = 0;
+ }
+
+ wxString copyright;
+ const wxString desc = ver.BeforeFirst('\n', ©right);
+ copyright.Replace("\n", "");
+
+ return wxVersionInfo("libtiff", major, minor, micro, desc, copyright);
+}
+
#endif // wxUSE_LIBTIFF