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 #define TIFFLINKAGEMODE LINKAGEMODE
54 // ============================================================================
56 // ============================================================================
58 // ----------------------------------------------------------------------------
59 // TIFF library error/warning handlers
60 // ----------------------------------------------------------------------------
63 FormatTiffMessage(const char *module, const char *fmt
, va_list ap
)
66 if ( wxCRT_VsnprintfA(buf
, WXSIZEOF(buf
), fmt
, ap
) <= 0 )
68 // this isn't supposed to happen, but if it does, it's better
70 strcpy(buf
, "Incorrectly formatted TIFF message");
72 buf
[WXSIZEOF(buf
)-1] = 0; // make sure it is always NULL-terminated
76 msg
+= wxString::Format(_(" (in module \"%s\")"), module);
85 TIFFwxWarningHandler(const char* module, const char *fmt
, va_list ap
)
87 wxLogWarning("%s", FormatTiffMessage(module, fmt
, ap
));
91 TIFFwxErrorHandler(const char* module, const char *fmt
, va_list ap
)
93 wxLogError("%s", FormatTiffMessage(module, fmt
, ap
));
98 //-----------------------------------------------------------------------------
100 //-----------------------------------------------------------------------------
102 IMPLEMENT_DYNAMIC_CLASS(wxTIFFHandler
,wxImageHandler
)
104 wxTIFFHandler::wxTIFFHandler()
106 m_name
= wxT("TIFF file");
107 m_extension
= wxT("tif");
108 m_altExtensions
.Add(wxT("tiff"));
109 m_type
= wxBITMAP_TYPE_TIFF
;
110 m_mime
= wxT("image/tiff");
111 TIFFSetWarningHandler((TIFFErrorHandler
) TIFFwxWarningHandler
);
112 TIFFSetErrorHandler((TIFFErrorHandler
) TIFFwxErrorHandler
);
117 // helper to translate our, possibly 64 bit, wxFileOffset to TIFF, always 32
119 static toff_t
wxFileOffsetToTIFF(wxFileOffset ofs
)
121 if ( ofs
== wxInvalidOffset
)
124 toff_t tofs
= wx_truncate_cast(toff_t
, ofs
);
125 wxCHECK_MSG( (wxFileOffset
)tofs
== ofs
, (toff_t
)-1,
126 wxT("TIFF library doesn't support large files") );
131 // another helper to convert standard seek mode to our
132 static wxSeekMode
wxSeekModeFromTIFF(int whence
)
140 return wxFromCurrent
;
146 return wxFromCurrent
;
153 tsize_t TIFFLINKAGEMODE
154 wxTIFFNullProc(thandle_t
WXUNUSED(handle
),
155 tdata_t
WXUNUSED(buf
),
156 tsize_t
WXUNUSED(size
))
161 tsize_t TIFFLINKAGEMODE
162 wxTIFFReadProc(thandle_t handle
, tdata_t buf
, tsize_t size
)
164 wxInputStream
*stream
= (wxInputStream
*) handle
;
165 stream
->Read( (void*) buf
, (size_t) size
);
166 return wx_truncate_cast(tsize_t
, stream
->LastRead());
169 tsize_t TIFFLINKAGEMODE
170 wxTIFFWriteProc(thandle_t handle
, tdata_t buf
, tsize_t size
)
172 wxOutputStream
*stream
= (wxOutputStream
*) handle
;
173 stream
->Write( (void*) buf
, (size_t) size
);
174 return wx_truncate_cast(tsize_t
, stream
->LastWrite());
177 toff_t TIFFLINKAGEMODE
178 wxTIFFSeekIProc(thandle_t handle
, toff_t off
, int whence
)
180 wxInputStream
*stream
= (wxInputStream
*) handle
;
182 return wxFileOffsetToTIFF(stream
->SeekI((wxFileOffset
)off
,
183 wxSeekModeFromTIFF(whence
)));
186 toff_t TIFFLINKAGEMODE
187 wxTIFFSeekOProc(thandle_t handle
, toff_t off
, int whence
)
189 wxOutputStream
*stream
= (wxOutputStream
*) handle
;
191 toff_t offset
= wxFileOffsetToTIFF(
192 stream
->SeekO((wxFileOffset
)off
, wxSeekModeFromTIFF(whence
)) );
194 if (offset
!= (toff_t
) -1 || whence
!= SEEK_SET
)
201 Try to workaround problems with libtiff seeking past the end of streams.
203 This occurs when libtiff is writing tag entries past the end of a
204 stream but hasn't written the directory yet (which will be placed
205 before the tags and contain offsets to the just written tags).
206 The behaviour for seeking past the end of a stream is not consistent
207 and doesn't work with for example wxMemoryOutputStream. When this type
208 of seeking fails (with SEEK_SET), fill in the gap with zeroes and try
212 wxFileOffset streamLength
= stream
->GetLength();
213 if (streamLength
!= wxInvalidOffset
&& (wxFileOffset
) off
> streamLength
)
215 if (stream
->SeekO(streamLength
, wxFromStart
) == wxInvalidOffset
)
220 for (wxFileOffset i
= 0; i
< (wxFileOffset
) off
- streamLength
; ++i
)
226 return wxFileOffsetToTIFF( stream
->TellO() );
230 wxTIFFCloseIProc(thandle_t
WXUNUSED(handle
))
232 // there is no need to close the input stream
237 wxTIFFCloseOProc(thandle_t handle
)
239 wxOutputStream
*stream
= (wxOutputStream
*) handle
;
241 return stream
->Close() ? 0 : -1;
244 toff_t TIFFLINKAGEMODE
245 wxTIFFSizeProc(thandle_t handle
)
247 wxStreamBase
*stream
= (wxStreamBase
*) handle
;
248 return (toff_t
) stream
->GetSize();
252 wxTIFFMapProc(thandle_t
WXUNUSED(handle
),
253 tdata_t
* WXUNUSED(pbase
),
254 toff_t
* WXUNUSED(psize
))
260 wxTIFFUnmapProc(thandle_t
WXUNUSED(handle
),
261 tdata_t
WXUNUSED(base
),
262 toff_t
WXUNUSED(size
))
269 TIFFwxOpen(wxInputStream
&stream
, const char* name
, const char* mode
)
271 TIFF
* tif
= TIFFClientOpen(name
, mode
,
273 wxTIFFReadProc
, wxTIFFNullProc
,
274 wxTIFFSeekIProc
, wxTIFFCloseIProc
, wxTIFFSizeProc
,
275 wxTIFFMapProc
, wxTIFFUnmapProc
);
281 TIFFwxOpen(wxOutputStream
&stream
, const char* name
, const char* mode
)
283 TIFF
* tif
= TIFFClientOpen(name
, mode
,
285 wxTIFFNullProc
, wxTIFFWriteProc
,
286 wxTIFFSeekOProc
, wxTIFFCloseOProc
, wxTIFFSizeProc
,
287 wxTIFFMapProc
, wxTIFFUnmapProc
);
292 bool wxTIFFHandler::LoadFile( wxImage
*image
, wxInputStream
& stream
, bool verbose
, int index
)
299 TIFF
*tif
= TIFFwxOpen( stream
, "image", "r" );
305 wxLogError( _("TIFF: Error loading image.") );
311 if (!TIFFSetDirectory( tif
, (tdir_t
)index
))
315 wxLogError( _("Invalid TIFF image index.") );
326 TIFFGetField( tif
, TIFFTAG_IMAGEWIDTH
, &w
);
327 TIFFGetField( tif
, TIFFTAG_IMAGELENGTH
, &h
);
329 uint16 samplesPerPixel
= 0;
330 (void) TIFFGetFieldDefaulted(tif
, TIFFTAG_SAMPLESPERPIXEL
, &samplesPerPixel
);
332 uint16 bitsPerSample
= 0;
333 (void) TIFFGetFieldDefaulted(tif
, TIFFTAG_BITSPERSAMPLE
, &bitsPerSample
);
337 TIFFGetFieldDefaulted(tif
, TIFFTAG_EXTRASAMPLES
,
338 &extraSamples
, &samplesInfo
);
341 if (!TIFFGetField(tif
, TIFFTAG_PHOTOMETRIC
, &photometric
))
343 photometric
= PHOTOMETRIC_MINISWHITE
;
345 const bool hasAlpha
= (extraSamples
>= 1
346 && ((samplesInfo
[0] == EXTRASAMPLE_UNSPECIFIED
)
347 || samplesInfo
[0] == EXTRASAMPLE_ASSOCALPHA
348 || samplesInfo
[0] == EXTRASAMPLE_UNASSALPHA
))
349 || (extraSamples
== 0 && samplesPerPixel
== 4
350 && photometric
== PHOTOMETRIC_RGB
);
352 // guard against integer overflow during multiplication which could result
353 // in allocating a too small buffer and then overflowing it
354 const double bytesNeeded
= (double)w
* (double)h
* sizeof(uint32
);
355 if ( bytesNeeded
>= wxUINT32_MAX
)
359 wxLogError( _("TIFF: Image size is abnormally big.") );
367 raster
= (uint32
*) _TIFFmalloc( (uint32
)bytesNeeded
);
373 wxLogError( _("TIFF: Couldn't allocate memory.") );
381 image
->Create( (int)w
, (int)h
);
386 wxLogError( _("TIFF: Couldn't allocate memory.") );
398 uint16 planarConfig
= PLANARCONFIG_CONTIG
;
399 (void) TIFFGetField(tif
, TIFFTAG_PLANARCONFIG
, &planarConfig
);
405 (planarConfig
== PLANARCONFIG_CONTIG
&& samplesPerPixel
== 2
406 && extraSamples
== 1)
409 ( !TIFFRGBAImageOK(tif
, msg
) )
410 || (bitsPerSample
== 8)
414 const bool isGreyScale
= (bitsPerSample
== 8);
415 unsigned char *buf
= (unsigned char *)_TIFFmalloc(TIFFScanlineSize(tif
));
417 const bool minIsWhite
= (photometric
== PHOTOMETRIC_MINISWHITE
);
418 const int minValue
= minIsWhite
? 255 : 0;
419 const int maxValue
= 255 - minValue
;
422 Decode to ABGR format as that is what the code, that converts to
423 wxImage, later on expects (normally TIFFReadRGBAImageOriented is
424 used to decode which uses an ABGR layout).
426 for (uint32 y
= 0; y
< h
; ++y
)
428 if (TIFFReadScanline(tif
, buf
, y
, 0) != 1)
436 for (uint32 x
= 0; x
< w
; ++x
)
438 uint8 val
= minIsWhite
? 255 - buf
[x
*2] : buf
[x
*2];
439 uint8 alpha
= minIsWhite
? 255 - buf
[x
*2+1] : buf
[x
*2+1];
440 raster
[pos
] = val
+ (val
<< 8) + (val
<< 16)
447 for (uint32 x
= 0; x
< w
; ++x
)
449 int mask
= buf
[x
*2/8] << ((x
*2)%8
);
451 uint8 val
= mask
& 128 ? maxValue
: minValue
;
452 raster
[pos
] = val
+ (val
<< 8) + (val
<< 16)
453 + ((mask
& 64 ? maxValue
: minValue
) << 24);
463 ok
= TIFFReadRGBAImageOriented( tif
, w
, h
, raster
,
464 ORIENTATION_TOPLEFT
, 0 ) != 0;
472 wxLogError( _("TIFF: Error reading image.") );
482 unsigned char *ptr
= image
->GetData();
484 unsigned char *alpha
= image
->GetAlpha();
488 for (uint32 i
= 0; i
< h
; i
++)
490 for (uint32 j
= 0; j
< w
; j
++)
492 *(ptr
++) = (unsigned char)TIFFGetR(raster
[pos
]);
493 *(ptr
++) = (unsigned char)TIFFGetG(raster
[pos
]);
494 *(ptr
++) = (unsigned char)TIFFGetB(raster
[pos
]);
496 *(alpha
++) = (unsigned char)TIFFGetA(raster
[pos
]);
503 image
->SetOption(wxIMAGE_OPTION_TIFF_PHOTOMETRIC
, photometric
);
507 Copy some baseline TIFF tags which helps when re-saving a TIFF
508 to be similar to the original image.
512 image
->SetOption(wxIMAGE_OPTION_TIFF_SAMPLESPERPIXEL
, samplesPerPixel
);
517 image
->SetOption(wxIMAGE_OPTION_TIFF_BITSPERSAMPLE
, bitsPerSample
);
520 if ( TIFFGetFieldDefaulted(tif
, TIFFTAG_COMPRESSION
, &compression
) )
522 image
->SetOption(wxIMAGE_OPTION_TIFF_COMPRESSION
, compression
);
525 // Set the resolution unit.
526 wxImageResolution resUnit
= wxIMAGE_RESOLUTION_NONE
;
528 if ( TIFFGetFieldDefaulted(tif
, TIFFTAG_RESOLUTIONUNIT
, &tiffRes
) )
533 wxLogWarning(_("Unknown TIFF resolution unit %d ignored"),
538 resUnit
= wxIMAGE_RESOLUTION_NONE
;
542 resUnit
= wxIMAGE_RESOLUTION_INCHES
;
545 case RESUNIT_CENTIMETER
:
546 resUnit
= wxIMAGE_RESOLUTION_CM
;
551 image
->SetOption(wxIMAGE_OPTION_RESOLUTIONUNIT
, resUnit
);
554 Set the image resolution if it's available. Resolution tag is not
555 dependant on RESOLUTIONUNIT != RESUNIT_NONE (according to TIFF spec).
559 if ( TIFFGetField(tif
, TIFFTAG_XRESOLUTION
, &resX
) )
562 Use a string value to not lose precision.
563 rounding to int as cm and then converting to inch may
564 result in whole integer rounding error, eg. 201 instead of 200 dpi.
565 If an app wants an int, GetOptionInt will convert and round down.
567 image
->SetOption(wxIMAGE_OPTION_RESOLUTIONX
,
568 wxString::FromCDouble((double) resX
));
571 if ( TIFFGetField(tif
, TIFFTAG_YRESOLUTION
, &resY
) )
573 image
->SetOption(wxIMAGE_OPTION_RESOLUTIONY
,
574 wxString::FromCDouble((double) resY
));
584 int wxTIFFHandler::DoGetImageCount( wxInputStream
& stream
)
586 TIFF
*tif
= TIFFwxOpen( stream
, "image", "r" );
591 int dircount
= 0; // according to the libtiff docs, dircount should be set to 1 here???
594 } while (TIFFReadDirectory(tif
));
598 // NOTE: this function modifies the current stream position but it's ok
599 // (see wxImageHandler::GetImageCount)
604 bool wxTIFFHandler::SaveFile( wxImage
*image
, wxOutputStream
& stream
, bool verbose
)
606 TIFF
*tif
= TIFFwxOpen( stream
, "image", "w" );
612 wxLogError( _("TIFF: Error saving image.") );
618 const int imageWidth
= image
->GetWidth();
619 TIFFSetField(tif
, TIFFTAG_IMAGEWIDTH
, (uint32
) imageWidth
);
620 TIFFSetField(tif
, TIFFTAG_IMAGELENGTH
, (uint32
)image
->GetHeight());
621 TIFFSetField(tif
, TIFFTAG_ORIENTATION
, ORIENTATION_TOPLEFT
);
622 TIFFSetField(tif
, TIFFTAG_PLANARCONFIG
, PLANARCONFIG_CONTIG
);
624 // save the image resolution if we have it
626 const wxImageResolution res
= GetResolutionFromOptions(*image
, &xres
, &yres
);
631 wxFAIL_MSG( wxT("unknown image resolution units") );
634 case wxIMAGE_RESOLUTION_NONE
:
635 tiffRes
= RESUNIT_NONE
;
638 case wxIMAGE_RESOLUTION_INCHES
:
639 tiffRes
= RESUNIT_INCH
;
642 case wxIMAGE_RESOLUTION_CM
:
643 tiffRes
= RESUNIT_CENTIMETER
;
647 if ( tiffRes
!= RESUNIT_NONE
)
649 TIFFSetField(tif
, TIFFTAG_RESOLUTIONUNIT
, tiffRes
);
650 TIFFSetField(tif
, TIFFTAG_XRESOLUTION
, (float)xres
);
651 TIFFSetField(tif
, TIFFTAG_YRESOLUTION
, (float)yres
);
655 int spp
= image
->GetOptionInt(wxIMAGE_OPTION_TIFF_SAMPLESPERPIXEL
);
659 int bps
= image
->GetOptionInt(wxIMAGE_OPTION_TIFF_BITSPERSAMPLE
);
666 // One bit per sample combined with 3 samples per pixel is
667 // not allowed and crashes libtiff.
671 int photometric
= PHOTOMETRIC_RGB
;
673 if ( image
->HasOption(wxIMAGE_OPTION_TIFF_PHOTOMETRIC
) )
675 photometric
= image
->GetOptionInt(wxIMAGE_OPTION_TIFF_PHOTOMETRIC
);
676 if (photometric
== PHOTOMETRIC_MINISWHITE
677 || photometric
== PHOTOMETRIC_MINISBLACK
)
679 // either b/w or greyscale
685 photometric
= PHOTOMETRIC_MINISWHITE
;
688 const bool hasAlpha
= image
->HasAlpha();
690 int compression
= image
->GetOptionInt(wxIMAGE_OPTION_TIFF_COMPRESSION
);
691 if ( !compression
|| (compression
== COMPRESSION_JPEG
&& hasAlpha
) )
693 // We can't use COMPRESSION_LZW because current version of libtiff
694 // doesn't implement it ("no longer implemented due to Unisys patent
695 // enforcement") and other compression methods are lossy so we
696 // shouldn't use them by default -- and the only remaining one is none.
697 // Also JPEG compression for alpha images is not a good idea (viewers
698 // not opening the image properly).
699 compression
= COMPRESSION_NONE
;
704 (photometric
== PHOTOMETRIC_RGB
&& spp
== 4)
705 || (photometric
<= PHOTOMETRIC_MINISBLACK
&& spp
== 2)
708 // Compensate for user passing a SamplesPerPixel that includes
709 // the alpha channel.
714 int extraSamples
= hasAlpha
? 1 : 0;
716 TIFFSetField(tif
, TIFFTAG_SAMPLESPERPIXEL
, spp
+ extraSamples
);
717 TIFFSetField(tif
, TIFFTAG_BITSPERSAMPLE
, bps
);
718 TIFFSetField(tif
, TIFFTAG_PHOTOMETRIC
, photometric
);
719 TIFFSetField(tif
, TIFFTAG_COMPRESSION
, compression
);
723 uint16 extra
[] = { EXTRASAMPLE_UNSPECIFIED
};
724 TIFFSetField(tif
, TIFFTAG_EXTRASAMPLES
, (long) 1, &extra
);
727 // scanlinesize is determined by spp+extraSamples and bps
728 const tsize_t linebytes
=
729 (tsize_t
)((imageWidth
* (spp
+ extraSamples
) * bps
+ 7) / 8);
733 const bool isColouredImage
= (spp
> 1)
734 && (photometric
!= PHOTOMETRIC_MINISWHITE
)
735 && (photometric
!= PHOTOMETRIC_MINISBLACK
);
738 if (TIFFScanlineSize(tif
) > linebytes
|| !isColouredImage
|| hasAlpha
)
740 buf
= (unsigned char *)_TIFFmalloc(TIFFScanlineSize(tif
));
745 wxLogError( _("TIFF: Couldn't allocate memory.") );
758 TIFFSetField(tif
, TIFFTAG_ROWSPERSTRIP
,TIFFDefaultStripSize(tif
, (uint32
) -1));
760 const int bitsPerPixel
= (spp
+ extraSamples
) * bps
;
761 const int bytesPerPixel
= (bitsPerPixel
+ 7) / 8;
762 const int pixelsPerByte
= 8 / bitsPerPixel
;
763 int remainingPixelCount
= 0;
767 // How many pixels to write in the last byte column?
768 remainingPixelCount
= imageWidth
% pixelsPerByte
;
769 if (!remainingPixelCount
) remainingPixelCount
= pixelsPerByte
;
772 const bool minIsWhite
= (photometric
== PHOTOMETRIC_MINISWHITE
);
773 unsigned char *ptr
= image
->GetData();
774 for ( int row
= 0; row
< image
->GetHeight(); row
++ )
783 for ( int column
= 0; column
< imageWidth
; column
++ )
785 buf
[column
*4 ] = ptr
[column
*3 ];
786 buf
[column
*4 + 1] = ptr
[column
*3 + 1];
787 buf
[column
*4 + 2] = ptr
[column
*3 + 2];
788 buf
[column
*4 + 3] = image
->GetAlpha(column
, row
);
793 memcpy(buf
, ptr
, imageWidth
* 3);
796 else if (spp
* bps
== 8) // greyscale image
798 for ( int column
= 0; column
< imageWidth
; column
++ )
800 uint8 value
= ptr
[column
*3 + 1];
806 buf
[column
* bytesPerPixel
] = value
;
810 value
= image
->GetAlpha(column
, row
);
811 buf
[column
*bytesPerPixel
+1]
812 = minIsWhite
? 255 - value
: value
;
816 else // black and white image
818 for ( int column
= 0; column
< linebytes
; column
++ )
821 int pixelsPerByteCount
= (column
+ 1 != linebytes
)
823 : remainingPixelCount
;
824 for ( int bp
= 0; bp
< pixelsPerByteCount
; bp
++ )
826 if ( (ptr
[column
* 3 * pixelsPerByte
+ bp
*3 + 1] <=127)
829 // check only green as this is sufficient
830 reverse
|= (uint8
) (128 >> (bp
* bitsPerPixel
));
834 && (image
->GetAlpha(column
* pixelsPerByte
+ bp
,
835 row
) <= 127) == minIsWhite
)
837 reverse
|= (uint8
) (64 >> (bp
* bitsPerPixel
));
841 buf
[column
] = reverse
;
846 if ( TIFFWriteScanline(tif
, buf
? buf
: ptr
, (uint32
)row
, 0) < 0 )
850 wxLogError( _("TIFF: Error writing image.") );
860 ptr
+= imageWidth
* 3;
863 (void) TIFFClose(tif
);
871 bool wxTIFFHandler::DoCanRead( wxInputStream
& stream
)
873 unsigned char hdr
[2];
875 if ( !stream
.Read(&hdr
[0], WXSIZEOF(hdr
)) ) // it's ok to modify the stream position here
878 return (hdr
[0] == 'I' && hdr
[1] == 'I') ||
879 (hdr
[0] == 'M' && hdr
[1] == 'M');
882 #endif // wxUSE_STREAMS
884 /*static*/ wxVersionInfo
wxTIFFHandler::GetLibraryVersionInfo()
890 const wxString
ver(::TIFFGetVersion());
891 if ( wxSscanf(ver
, "LIBTIFF, Version %d.%d.%d", &major
, &minor
, µ
) != 3 )
893 wxLogDebug("Unrecognized libtiff version string \"%s\"", ver
);
901 const wxString desc
= ver
.BeforeFirst('\n', ©right
);
902 copyright
.Replace("\n", "");
904 return wxVersionInfo("libtiff", major
, minor
, micro
, desc
, copyright
);
907 #endif // wxUSE_LIBTIFF