X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/426272a35e5727f8c2ca147a6b1eb865cc5e34d6..eea4d01c65f9b29baa1193db762b4c6b8144af24:/src/common/imagtiff.cpp diff --git a/src/common/imagtiff.cpp b/src/common/imagtiff.cpp index 3a5e63fb5e..2501e91f3e 100644 --- a/src/common/imagtiff.cpp +++ b/src/common/imagtiff.cpp @@ -25,6 +25,7 @@ #if wxUSE_IMAGE && wxUSE_LIBTIFF #include "wx/imagtiff.h" +#include "wx/versioninfo.h" #ifndef WX_PRECOMP #include "wx/log.h" @@ -32,6 +33,7 @@ #include "wx/intl.h" #include "wx/bitmap.h" #include "wx/module.h" + #include "wx/wxcrtvararg.h" #endif extern "C" @@ -61,39 +63,38 @@ extern "C" // 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* WXUNUSED_IN_UNICODE(fmt), - va_list WXUNUSED_IN_UNICODE(ap)) +TIFFwxWarningHandler(const char* module, const char *fmt, va_list 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 + wxLogWarning("%s", FormatTiffMessage(module, fmt, ap)); } static void -TIFFwxErrorHandler(const char* module, - const char* WXUNUSED_IN_UNICODE(fmt), - va_list WXUNUSED_IN_UNICODE(ap)) +TIFFwxErrorHandler(const char* module, const char *fmt, va_list 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 + wxLogError("%s", FormatTiffMessage(module, fmt, ap)); } } // extern "C" @@ -108,6 +109,7 @@ 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); @@ -125,7 +127,7 @@ static toff_t wxFileOffsetToTIFF(wxFileOffset ofs) 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; } @@ -269,7 +271,9 @@ bool wxTIFFHandler::LoadFile( wxImage *image, wxInputStream& stream, bool verbos if (!tif) { if (verbose) + { wxLogError( _("TIFF: Error loading image.") ); + } return false; } @@ -277,7 +281,9 @@ bool wxTIFFHandler::LoadFile( wxImage *image, wxInputStream& stream, bool verbos if (!TIFFSetDirectory( tif, (tdir_t)index )) { if (verbose) + { wxLogError( _("Invalid TIFF image index.") ); + } TIFFClose( tif ); @@ -285,28 +291,52 @@ bool wxTIFFHandler::LoadFile( wxImage *image, wxInputStream& stream, bool verbos } uint32 w, h; - uint32 npixels; uint32 *raster; TIFFGetField( tif, TIFFTAG_IMAGEWIDTH, &w ); TIFFGetField( tif, TIFFTAG_IMAGELENGTH, &h ); + uint16 photometric; + uint16 samplesPerPixel; uint16 extraSamples; uint16* samplesInfo; + TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &samplesPerPixel); TIFFGetFieldDefaulted(tif, TIFFTAG_EXTRASAMPLES, &extraSamples, &samplesInfo); - const bool hasAlpha = (extraSamples == 1 && - (samplesInfo[0] == EXTRASAMPLE_ASSOCALPHA || - samplesInfo[0] == EXTRASAMPLE_UNASSALPHA)); + 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); - npixels = w * h; + 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 ); @@ -314,10 +344,12 @@ bool wxTIFFHandler::LoadFile( wxImage *image, wxInputStream& stream, bool verbos } image->Create( (int)w, (int)h ); - if (!image->Ok()) + if (!image->IsOk()) { if (verbose) + { wxLogError( _("TIFF: Couldn't allocate memory.") ); + } _TIFFfree( raster ); TIFFClose( tif ); @@ -331,7 +363,9 @@ bool wxTIFFHandler::LoadFile( wxImage *image, wxInputStream& stream, bool verbos if (!TIFFReadRGBAImage( tif, w, h, raster, 0 )) { if (verbose) + { wxLogError( _("TIFF: Error reading image.") ); + } _TIFFfree( raster ); image->Destroy(); @@ -368,42 +402,78 @@ bool wxTIFFHandler::LoadFile( wxImage *image, wxInputStream& stream, bool verbos alpha -= 2*w; } - // set the image resolution if it's available + + 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 ( TIFFGetField(tif, TIFFTAG_RESOLUTIONUNIT, &tiffRes) ) + if ( TIFFGetFieldDefaulted(tif, TIFFTAG_RESOLUTIONUNIT, &tiffRes) ) { - wxImageResolution res; - switch ( tiffRes ) + switch (tiffRes) { default: wxLogWarning(_("Unknown TIFF resolution unit %d ignored"), - tiffRes); + tiffRes); // fall through case RESUNIT_NONE: - res = wxIMAGE_RESOLUTION_NONE; + resUnit = wxIMAGE_RESOLUTION_NONE; break; case RESUNIT_INCH: - res = wxIMAGE_RESOLUTION_INCHES; + resUnit = wxIMAGE_RESOLUTION_INCHES; break; case RESUNIT_CENTIMETER: - res = wxIMAGE_RESOLUTION_CM; + resUnit = wxIMAGE_RESOLUTION_CM; break; } + } - if ( res != wxIMAGE_RESOLUTION_NONE ) - { - float xres, yres; - if ( TIFFGetField(tif, TIFFTAG_XRESOLUTION, &xres) ) - image->SetOption(wxIMAGE_OPTION_RESOLUTIONX, wxRound(xres)); + image->SetOption(wxIMAGE_OPTION_RESOLUTIONUNIT, resUnit); - if ( TIFFGetField(tif, TIFFTAG_YRESOLUTION, &yres) ) - image->SetOption(wxIMAGE_OPTION_RESOLUTIONY, wxRound(yres)); - } + /* + 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 ); @@ -412,7 +482,7 @@ bool wxTIFFHandler::LoadFile( wxImage *image, wxInputStream& stream, bool verbos return true; } -int wxTIFFHandler::GetImageCount( wxInputStream& stream ) +int wxTIFFHandler::DoGetImageCount( wxInputStream& stream ) { TIFF *tif = TIFFwxOpen( stream, "image", "r" ); @@ -426,6 +496,9 @@ int wxTIFFHandler::GetImageCount( wxInputStream& stream ) TIFFClose( tif ); + // NOTE: this function modifies the current stream position but it's ok + // (see wxImageHandler::GetImageCount) + return dircount; } @@ -436,7 +509,9 @@ bool wxTIFFHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbo if (!tif) { if (verbose) + { wxLogError( _("TIFF: Error saving image.") ); + } return false; } @@ -454,7 +529,7 @@ bool wxTIFFHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbo switch ( res ) { default: - wxFAIL_MSG( _T("unknown image resolution units") ); + wxFAIL_MSG( wxT("unknown image resolution units") ); // fall through case wxIMAGE_RESOLUTION_NONE: @@ -516,7 +591,9 @@ bool wxTIFFHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbo if (!buf) { if (verbose) + { wxLogError( _("TIFF: Couldn't allocate memory.") ); + } TIFFClose( tif ); @@ -562,7 +639,9 @@ bool wxTIFFHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbo if ( TIFFWriteScanline(tif, buf ? buf : ptr, (uint32)row, 0) < 0 ) { if (verbose) + { wxLogError( _("TIFF: Error writing image.") ); + } TIFFClose( tif ); if (buf) @@ -586,7 +665,7 @@ bool wxTIFFHandler::DoCanRead( wxInputStream& stream ) { 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') || @@ -595,4 +674,27 @@ bool wxTIFFHandler::DoCanRead( wxInputStream& stream ) #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