1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/imagtiff.cpp
3 // Purpose: wxImage TIFF handler
4 // Author: Robert Roebling
6 // Copyright: (c) Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
10 // ============================================================================
12 // ============================================================================
14 // ----------------------------------------------------------------------------
16 // ----------------------------------------------------------------------------
18 // For compilers that support precompilation, includes "wx.h".
19 #include "wx/wxprec.h"
25 #if wxUSE_IMAGE && wxUSE_LIBTIFF
27 #include "wx/imagtiff.h"
28 #include "wx/versioninfo.h"
34 #include "wx/bitmap.h"
35 #include "wx/module.h"
36 #include "wx/wxcrtvararg.h"
42 #include "tif_config.h"
47 #include "wx/filefn.h"
48 #include "wx/wfstream.h"
50 #ifndef TIFFLINKAGEMODE
51 #if defined(__WATCOMC__) && defined(__WXMGL__)
52 #define TIFFLINKAGEMODE cdecl
54 #define TIFFLINKAGEMODE LINKAGEMODE
58 // ============================================================================
60 // ============================================================================
62 // ----------------------------------------------------------------------------
63 // TIFF library error/warning handlers
64 // ----------------------------------------------------------------------------
67 FormatTiffMessage(const char *module, const char *fmt
, va_list ap
)
70 if ( wxCRT_VsnprintfA(buf
, WXSIZEOF(buf
), fmt
, ap
) <= 0 )
72 // this isn't supposed to happen, but if it does, it's better
74 strcpy(buf
, "Incorrectly formatted TIFF message");
76 buf
[WXSIZEOF(buf
)-1] = 0; // make sure it is always NULL-terminated
80 msg
+= wxString::Format(_(" (in module \"%s\")"), module);
89 TIFFwxWarningHandler(const char* module, const char *fmt
, va_list ap
)
91 wxLogWarning("%s", FormatTiffMessage(module, fmt
, ap
));
95 TIFFwxErrorHandler(const char* module, const char *fmt
, va_list ap
)
97 wxLogError("%s", FormatTiffMessage(module, fmt
, ap
));
102 //-----------------------------------------------------------------------------
104 //-----------------------------------------------------------------------------
106 IMPLEMENT_DYNAMIC_CLASS(wxTIFFHandler
,wxImageHandler
)
108 wxTIFFHandler::wxTIFFHandler()
110 m_name
= wxT("TIFF file");
111 m_extension
= wxT("tif");
112 m_altExtensions
.Add(wxT("tiff"));
113 m_type
= wxBITMAP_TYPE_TIF
;
114 m_mime
= wxT("image/tiff");
115 TIFFSetWarningHandler((TIFFErrorHandler
) TIFFwxWarningHandler
);
116 TIFFSetErrorHandler((TIFFErrorHandler
) TIFFwxErrorHandler
);
121 // helper to translate our, possibly 64 bit, wxFileOffset to TIFF, always 32
123 static toff_t
wxFileOffsetToTIFF(wxFileOffset ofs
)
125 if ( ofs
== wxInvalidOffset
)
128 toff_t tofs
= wx_truncate_cast(toff_t
, ofs
);
129 wxCHECK_MSG( (wxFileOffset
)tofs
== ofs
, (toff_t
)-1,
130 wxT("TIFF library doesn't support large files") );
135 // another helper to convert standard seek mode to our
136 static wxSeekMode
wxSeekModeFromTIFF(int whence
)
144 return wxFromCurrent
;
150 return wxFromCurrent
;
157 tsize_t TIFFLINKAGEMODE
158 wxTIFFNullProc(thandle_t
WXUNUSED(handle
),
159 tdata_t
WXUNUSED(buf
),
160 tsize_t
WXUNUSED(size
))
165 tsize_t TIFFLINKAGEMODE
166 wxTIFFReadProc(thandle_t handle
, tdata_t buf
, tsize_t size
)
168 wxInputStream
*stream
= (wxInputStream
*) handle
;
169 stream
->Read( (void*) buf
, (size_t) size
);
170 return wx_truncate_cast(tsize_t
, stream
->LastRead());
173 tsize_t TIFFLINKAGEMODE
174 wxTIFFWriteProc(thandle_t handle
, tdata_t buf
, tsize_t size
)
176 wxOutputStream
*stream
= (wxOutputStream
*) handle
;
177 stream
->Write( (void*) buf
, (size_t) size
);
178 return wx_truncate_cast(tsize_t
, stream
->LastWrite());
181 toff_t TIFFLINKAGEMODE
182 wxTIFFSeekIProc(thandle_t handle
, toff_t off
, int whence
)
184 wxInputStream
*stream
= (wxInputStream
*) handle
;
186 return wxFileOffsetToTIFF(stream
->SeekI((wxFileOffset
)off
,
187 wxSeekModeFromTIFF(whence
)));
190 toff_t TIFFLINKAGEMODE
191 wxTIFFSeekOProc(thandle_t handle
, toff_t off
, int whence
)
193 wxOutputStream
*stream
= (wxOutputStream
*) handle
;
195 return wxFileOffsetToTIFF(stream
->SeekO((wxFileOffset
)off
,
196 wxSeekModeFromTIFF(whence
)));
200 wxTIFFCloseIProc(thandle_t
WXUNUSED(handle
))
202 // there is no need to close the input stream
207 wxTIFFCloseOProc(thandle_t handle
)
209 wxOutputStream
*stream
= (wxOutputStream
*) handle
;
211 return stream
->Close() ? 0 : -1;
214 toff_t TIFFLINKAGEMODE
215 wxTIFFSizeProc(thandle_t handle
)
217 wxStreamBase
*stream
= (wxStreamBase
*) handle
;
218 return (toff_t
) stream
->GetSize();
222 wxTIFFMapProc(thandle_t
WXUNUSED(handle
),
223 tdata_t
* WXUNUSED(pbase
),
224 toff_t
* WXUNUSED(psize
))
230 wxTIFFUnmapProc(thandle_t
WXUNUSED(handle
),
231 tdata_t
WXUNUSED(base
),
232 toff_t
WXUNUSED(size
))
239 TIFFwxOpen(wxInputStream
&stream
, const char* name
, const char* mode
)
241 TIFF
* tif
= TIFFClientOpen(name
, mode
,
243 wxTIFFReadProc
, wxTIFFNullProc
,
244 wxTIFFSeekIProc
, wxTIFFCloseIProc
, wxTIFFSizeProc
,
245 wxTIFFMapProc
, wxTIFFUnmapProc
);
251 TIFFwxOpen(wxOutputStream
&stream
, const char* name
, const char* mode
)
253 TIFF
* tif
= TIFFClientOpen(name
, mode
,
255 wxTIFFNullProc
, wxTIFFWriteProc
,
256 wxTIFFSeekOProc
, wxTIFFCloseOProc
, wxTIFFSizeProc
,
257 wxTIFFMapProc
, wxTIFFUnmapProc
);
262 bool wxTIFFHandler::LoadFile( wxImage
*image
, wxInputStream
& stream
, bool verbose
, int index
)
269 TIFF
*tif
= TIFFwxOpen( stream
, "image", "r" );
275 wxLogError( _("TIFF: Error loading image.") );
281 if (!TIFFSetDirectory( tif
, (tdir_t
)index
))
285 wxLogError( _("Invalid TIFF image index.") );
296 TIFFGetField( tif
, TIFFTAG_IMAGEWIDTH
, &w
);
297 TIFFGetField( tif
, TIFFTAG_IMAGELENGTH
, &h
);
300 uint16 samplesPerPixel
;
303 TIFFGetFieldDefaulted(tif
, TIFFTAG_SAMPLESPERPIXEL
, &samplesPerPixel
);
304 TIFFGetFieldDefaulted(tif
, TIFFTAG_EXTRASAMPLES
,
305 &extraSamples
, &samplesInfo
);
306 if (!TIFFGetField(tif
, TIFFTAG_PHOTOMETRIC
, &photometric
))
308 photometric
= PHOTOMETRIC_MINISWHITE
;
310 const bool hasAlpha
= (extraSamples
>= 1
311 && ((samplesInfo
[0] == EXTRASAMPLE_UNSPECIFIED
&& samplesPerPixel
> 3)
312 || samplesInfo
[0] == EXTRASAMPLE_ASSOCALPHA
313 || samplesInfo
[0] == EXTRASAMPLE_UNASSALPHA
))
314 || (extraSamples
== 0 && samplesPerPixel
== 4
315 && photometric
== PHOTOMETRIC_RGB
);
317 // guard against integer overflow during multiplication which could result
318 // in allocating a too small buffer and then overflowing it
319 const double bytesNeeded
= (double)w
* (double)h
* sizeof(uint32
);
320 if ( bytesNeeded
>= wxUINT32_MAX
)
324 wxLogError( _("TIFF: Image size is abnormally big.") );
332 raster
= (uint32
*) _TIFFmalloc( (uint32
)bytesNeeded
);
338 wxLogError( _("TIFF: Couldn't allocate memory.") );
346 image
->Create( (int)w
, (int)h
);
351 wxLogError( _("TIFF: Couldn't allocate memory.") );
363 if (!TIFFReadRGBAImage( tif
, w
, h
, raster
, 0 ))
367 wxLogError( _("TIFF: Error reading image.") );
377 unsigned char *ptr
= image
->GetData();
380 unsigned char *alpha
= hasAlpha
? image
->GetAlpha() : NULL
;
386 for (uint32 i
= 0; i
< h
; i
++)
388 for (uint32 j
= 0; j
< w
; j
++)
390 *(ptr
++) = (unsigned char)TIFFGetR(raster
[pos
]);
391 *(ptr
++) = (unsigned char)TIFFGetG(raster
[pos
]);
392 *(ptr
++) = (unsigned char)TIFFGetB(raster
[pos
]);
394 *(alpha
++) = (unsigned char)TIFFGetA(raster
[pos
]);
399 // subtract line we just added plus one line:
406 uint16 spp
, bpp
, compression
;
408 Read some baseline TIFF tags which helps when re-saving a TIFF
409 to be similar to the original image.
411 if ( TIFFGetFieldDefaulted(tif
, TIFFTAG_SAMPLESPERPIXEL
, &spp
) )
413 image
->SetOption(wxIMAGE_OPTION_SAMPLESPERPIXEL
, spp
);
416 if ( TIFFGetFieldDefaulted(tif
, TIFFTAG_BITSPERSAMPLE
, &bpp
) )
418 image
->SetOption(wxIMAGE_OPTION_BITSPERSAMPLE
, bpp
);
421 if ( TIFFGetFieldDefaulted(tif
, TIFFTAG_COMPRESSION
, &compression
) )
423 image
->SetOption(wxIMAGE_OPTION_COMPRESSION
, compression
);
426 // Set the resolution unit.
427 wxImageResolution resUnit
= wxIMAGE_RESOLUTION_NONE
;
429 if ( TIFFGetFieldDefaulted(tif
, TIFFTAG_RESOLUTIONUNIT
, &tiffRes
) )
434 wxLogWarning(_("Unknown TIFF resolution unit %d ignored"),
439 resUnit
= wxIMAGE_RESOLUTION_NONE
;
443 resUnit
= wxIMAGE_RESOLUTION_INCHES
;
446 case RESUNIT_CENTIMETER
:
447 resUnit
= wxIMAGE_RESOLUTION_CM
;
452 image
->SetOption(wxIMAGE_OPTION_RESOLUTIONUNIT
, resUnit
);
455 Set the image resolution if it's available. Resolution tag is not
456 dependant on RESOLUTIONUNIT != RESUNIT_NONE (according to TIFF spec).
460 if ( TIFFGetField(tif
, TIFFTAG_XRESOLUTION
, &resX
) )
463 Use a string value to not lose precision.
464 rounding to int as cm and then converting to inch may
465 result in whole integer rounding error, eg. 201 instead of 200 dpi.
466 If an app wants an int, GetOptionInt will convert and round down.
468 image
->SetOption(wxIMAGE_OPTION_RESOLUTIONX
,
469 wxString::FromCDouble((double) resX
));
472 if ( TIFFGetField(tif
, TIFFTAG_YRESOLUTION
, &resY
) )
474 image
->SetOption(wxIMAGE_OPTION_RESOLUTIONY
,
475 wxString::FromCDouble((double) resY
));
485 int wxTIFFHandler::DoGetImageCount( wxInputStream
& stream
)
487 TIFF
*tif
= TIFFwxOpen( stream
, "image", "r" );
492 int dircount
= 0; // according to the libtiff docs, dircount should be set to 1 here???
495 } while (TIFFReadDirectory(tif
));
499 // NOTE: this function modifies the current stream position but it's ok
500 // (see wxImageHandler::GetImageCount)
505 bool wxTIFFHandler::SaveFile( wxImage
*image
, wxOutputStream
& stream
, bool verbose
)
507 TIFF
*tif
= TIFFwxOpen( stream
, "image", "w" );
513 wxLogError( _("TIFF: Error saving image.") );
519 TIFFSetField(tif
, TIFFTAG_ORIENTATION
, ORIENTATION_TOPLEFT
);
520 TIFFSetField(tif
, TIFFTAG_IMAGEWIDTH
, (uint32
)image
->GetWidth());
521 TIFFSetField(tif
, TIFFTAG_IMAGELENGTH
, (uint32
)image
->GetHeight());
522 TIFFSetField(tif
, TIFFTAG_ORIENTATION
, ORIENTATION_TOPLEFT
);
523 TIFFSetField(tif
, TIFFTAG_PLANARCONFIG
, PLANARCONFIG_CONTIG
);
525 // save the image resolution if we have it
527 const wxImageResolution res
= GetResolutionFromOptions(*image
, &xres
, &yres
);
532 wxFAIL_MSG( wxT("unknown image resolution units") );
535 case wxIMAGE_RESOLUTION_NONE
:
536 tiffRes
= RESUNIT_NONE
;
539 case wxIMAGE_RESOLUTION_INCHES
:
540 tiffRes
= RESUNIT_INCH
;
543 case wxIMAGE_RESOLUTION_CM
:
544 tiffRes
= RESUNIT_CENTIMETER
;
548 if ( tiffRes
!= RESUNIT_NONE
)
550 TIFFSetField(tif
, TIFFTAG_RESOLUTIONUNIT
, tiffRes
);
551 TIFFSetField(tif
, TIFFTAG_XRESOLUTION
, (float)xres
);
552 TIFFSetField(tif
, TIFFTAG_YRESOLUTION
, (float)yres
);
556 int spp
= image
->GetOptionInt(wxIMAGE_OPTION_SAMPLESPERPIXEL
);
560 int bpp
= image
->GetOptionInt(wxIMAGE_OPTION_BITSPERSAMPLE
);
564 int compression
= image
->GetOptionInt(wxIMAGE_OPTION_COMPRESSION
);
567 // we can't use COMPRESSION_LZW because current version of libtiff
568 // doesn't implement it ("no longer implemented due to Unisys patent
569 // enforcement") and other compression methods are lossy so we
570 // shouldn't use them by default -- and the only remaining one is none
571 compression
= COMPRESSION_NONE
;
574 TIFFSetField(tif
, TIFFTAG_SAMPLESPERPIXEL
, spp
);
575 TIFFSetField(tif
, TIFFTAG_BITSPERSAMPLE
, bpp
);
576 TIFFSetField(tif
, TIFFTAG_PHOTOMETRIC
, spp
*bpp
== 1 ? PHOTOMETRIC_MINISBLACK
578 TIFFSetField(tif
, TIFFTAG_COMPRESSION
, compression
);
580 // scanlinesize if determined by spp and bpp
581 tsize_t linebytes
= (tsize_t
)image
->GetWidth() * spp
* bpp
/ 8;
583 if ( (image
->GetWidth() % 8 > 0) && (spp
* bpp
< 8) )
588 if (TIFFScanlineSize(tif
) > linebytes
|| (spp
* bpp
< 24))
590 buf
= (unsigned char *)_TIFFmalloc(TIFFScanlineSize(tif
));
595 wxLogError( _("TIFF: Couldn't allocate memory.") );
608 TIFFSetField(tif
, TIFFTAG_ROWSPERSTRIP
,TIFFDefaultStripSize(tif
, (uint32
) -1));
610 unsigned char *ptr
= image
->GetData();
611 for ( int row
= 0; row
< image
->GetHeight(); row
++ )
618 memcpy(buf
, ptr
, image
->GetWidth());
620 else // black and white image
622 for ( int column
= 0; column
< linebytes
; column
++ )
625 for ( int bp
= 0; bp
< 8; bp
++ )
627 if ( ptr
[column
*24 + bp
*3] > 0 )
629 // check only red as this is sufficient
630 reverse
= (uint8
)(reverse
| 128 >> bp
);
634 buf
[column
] = reverse
;
639 if ( TIFFWriteScanline(tif
, buf
? buf
: ptr
, (uint32
)row
, 0) < 0 )
643 wxLogError( _("TIFF: Error writing image.") );
653 ptr
+= image
->GetWidth()*3;
656 (void) TIFFClose(tif
);
664 bool wxTIFFHandler::DoCanRead( wxInputStream
& stream
)
666 unsigned char hdr
[2];
668 if ( !stream
.Read(&hdr
[0], WXSIZEOF(hdr
)) ) // it's ok to modify the stream position here
671 return (hdr
[0] == 'I' && hdr
[1] == 'I') ||
672 (hdr
[0] == 'M' && hdr
[1] == 'M');
675 #endif // wxUSE_STREAMS
677 /*static*/ wxVersionInfo
wxTIFFHandler::GetLibraryVersionInfo()
683 const wxString
ver(::TIFFGetVersion());
684 if ( wxSscanf(ver
, "LIBTIFF, Version %d.%d.%d", &major
, &minor
, µ
) != 3 )
686 wxLogDebug("Unrecognized libtiff version string \"%s\"", ver
);
694 const wxString desc
= ver
.BeforeFirst('\n', ©right
);
695 copyright
.Replace("\n", "");
697 return wxVersionInfo("libtiff", major
, minor
, micro
, desc
, copyright
);
700 #endif // wxUSE_LIBTIFF