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_TIFF
;
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 toff_t offset
= wxFileOffsetToTIFF(
196 stream
->SeekO((wxFileOffset
)off
, wxSeekModeFromTIFF(whence
)) );
198 if (offset
!= (toff_t
) -1 || whence
!= SEEK_SET
)
205 Try to workaround problems with libtiff seeking past the end of streams.
207 This occurs when libtiff is writing tag entries past the end of a
208 stream but hasn't written the directory yet (which will be placed
209 before the tags and contain offsets to the just written tags).
210 The behaviour for seeking past the end of a stream is not consistent
211 and doesn't work with for example wxMemoryOutputStream. When this type
212 of seeking fails (with SEEK_SET), fill in the gap with zeroes and try
216 wxFileOffset streamLength
= stream
->GetLength();
217 if (streamLength
!= wxInvalidOffset
&& (wxFileOffset
) off
> streamLength
)
219 if (stream
->SeekO(streamLength
, wxFromStart
) == wxInvalidOffset
)
224 for (wxFileOffset i
= 0; i
< (wxFileOffset
) off
- streamLength
; ++i
)
230 return wxFileOffsetToTIFF( stream
->TellO() );
234 wxTIFFCloseIProc(thandle_t
WXUNUSED(handle
))
236 // there is no need to close the input stream
241 wxTIFFCloseOProc(thandle_t handle
)
243 wxOutputStream
*stream
= (wxOutputStream
*) handle
;
245 return stream
->Close() ? 0 : -1;
248 toff_t TIFFLINKAGEMODE
249 wxTIFFSizeProc(thandle_t handle
)
251 wxStreamBase
*stream
= (wxStreamBase
*) handle
;
252 return (toff_t
) stream
->GetSize();
256 wxTIFFMapProc(thandle_t
WXUNUSED(handle
),
257 tdata_t
* WXUNUSED(pbase
),
258 toff_t
* WXUNUSED(psize
))
264 wxTIFFUnmapProc(thandle_t
WXUNUSED(handle
),
265 tdata_t
WXUNUSED(base
),
266 toff_t
WXUNUSED(size
))
273 TIFFwxOpen(wxInputStream
&stream
, const char* name
, const char* mode
)
275 TIFF
* tif
= TIFFClientOpen(name
, mode
,
277 wxTIFFReadProc
, wxTIFFNullProc
,
278 wxTIFFSeekIProc
, wxTIFFCloseIProc
, wxTIFFSizeProc
,
279 wxTIFFMapProc
, wxTIFFUnmapProc
);
285 TIFFwxOpen(wxOutputStream
&stream
, const char* name
, const char* mode
)
287 TIFF
* tif
= TIFFClientOpen(name
, mode
,
289 wxTIFFNullProc
, wxTIFFWriteProc
,
290 wxTIFFSeekOProc
, wxTIFFCloseOProc
, wxTIFFSizeProc
,
291 wxTIFFMapProc
, wxTIFFUnmapProc
);
296 bool wxTIFFHandler::LoadFile( wxImage
*image
, wxInputStream
& stream
, bool verbose
, int index
)
303 TIFF
*tif
= TIFFwxOpen( stream
, "image", "r" );
309 wxLogError( _("TIFF: Error loading image.") );
315 if (!TIFFSetDirectory( tif
, (tdir_t
)index
))
319 wxLogError( _("Invalid TIFF image index.") );
330 TIFFGetField( tif
, TIFFTAG_IMAGEWIDTH
, &w
);
331 TIFFGetField( tif
, TIFFTAG_IMAGELENGTH
, &h
);
333 uint16 samplesPerPixel
= 0;
334 (void) TIFFGetFieldDefaulted(tif
, TIFFTAG_SAMPLESPERPIXEL
, &samplesPerPixel
);
336 uint16 bitsPerSample
= 0;
337 (void) TIFFGetFieldDefaulted(tif
, TIFFTAG_BITSPERSAMPLE
, &bitsPerSample
);
341 TIFFGetFieldDefaulted(tif
, TIFFTAG_EXTRASAMPLES
,
342 &extraSamples
, &samplesInfo
);
345 if (!TIFFGetField(tif
, TIFFTAG_PHOTOMETRIC
, &photometric
))
347 photometric
= PHOTOMETRIC_MINISWHITE
;
349 const bool hasAlpha
= (extraSamples
>= 1
350 && ((samplesInfo
[0] == EXTRASAMPLE_UNSPECIFIED
)
351 || samplesInfo
[0] == EXTRASAMPLE_ASSOCALPHA
352 || samplesInfo
[0] == EXTRASAMPLE_UNASSALPHA
))
353 || (extraSamples
== 0 && samplesPerPixel
== 4
354 && photometric
== PHOTOMETRIC_RGB
);
356 // guard against integer overflow during multiplication which could result
357 // in allocating a too small buffer and then overflowing it
358 const double bytesNeeded
= (double)w
* (double)h
* sizeof(uint32
);
359 if ( bytesNeeded
>= wxUINT32_MAX
)
363 wxLogError( _("TIFF: Image size is abnormally big.") );
371 raster
= (uint32
*) _TIFFmalloc( (uint32
)bytesNeeded
);
377 wxLogError( _("TIFF: Couldn't allocate memory.") );
385 image
->Create( (int)w
, (int)h
);
390 wxLogError( _("TIFF: Couldn't allocate memory.") );
402 uint16 planarConfig
= PLANARCONFIG_CONTIG
;
403 (void) TIFFGetField(tif
, TIFFTAG_PLANARCONFIG
, &planarConfig
);
409 (planarConfig
== PLANARCONFIG_CONTIG
&& samplesPerPixel
== 2
410 && extraSamples
== 1)
413 ( !TIFFRGBAImageOK(tif
, msg
) )
414 || (bitsPerSample
== 8)
418 const bool isGreyScale
= (bitsPerSample
== 8);
419 unsigned char *buf
= (unsigned char *)_TIFFmalloc(TIFFScanlineSize(tif
));
421 const bool minIsWhite
= (photometric
== PHOTOMETRIC_MINISWHITE
);
422 const int minValue
= minIsWhite
? 255 : 0;
423 const int maxValue
= 255 - minValue
;
426 Decode to ABGR format as that is what the code, that converts to
427 wxImage, later on expects (normally TIFFReadRGBAImageOriented is
428 used to decode which uses an ABGR layout).
430 for (uint32 y
= 0; y
< h
; ++y
)
432 if (TIFFReadScanline(tif
, buf
, y
, 0) != 1)
440 for (uint32 x
= 0; x
< w
; ++x
)
442 uint8 val
= minIsWhite
? 255 - buf
[x
*2] : buf
[x
*2];
443 uint8 alpha
= minIsWhite
? 255 - buf
[x
*2+1] : buf
[x
*2+1];
444 raster
[pos
] = val
+ (val
<< 8) + (val
<< 16)
451 for (uint32 x
= 0; x
< w
; ++x
)
453 int mask
= buf
[x
*2/8] << ((x
*2)%8
);
455 uint8 val
= mask
& 128 ? maxValue
: minValue
;
456 raster
[pos
] = val
+ (val
<< 8) + (val
<< 16)
457 + ((mask
& 64 ? maxValue
: minValue
) << 24);
467 ok
= TIFFReadRGBAImageOriented( tif
, w
, h
, raster
,
468 ORIENTATION_TOPLEFT
, 0 ) != 0;
476 wxLogError( _("TIFF: Error reading image.") );
486 unsigned char *ptr
= image
->GetData();
488 unsigned char *alpha
= image
->GetAlpha();
492 for (uint32 i
= 0; i
< h
; i
++)
494 for (uint32 j
= 0; j
< w
; j
++)
496 *(ptr
++) = (unsigned char)TIFFGetR(raster
[pos
]);
497 *(ptr
++) = (unsigned char)TIFFGetG(raster
[pos
]);
498 *(ptr
++) = (unsigned char)TIFFGetB(raster
[pos
]);
500 *(alpha
++) = (unsigned char)TIFFGetA(raster
[pos
]);
507 image
->SetOption(wxIMAGE_OPTION_TIFF_PHOTOMETRIC
, photometric
);
511 Copy some baseline TIFF tags which helps when re-saving a TIFF
512 to be similar to the original image.
516 image
->SetOption(wxIMAGE_OPTION_TIFF_SAMPLESPERPIXEL
, samplesPerPixel
);
521 image
->SetOption(wxIMAGE_OPTION_TIFF_BITSPERSAMPLE
, bitsPerSample
);
524 if ( TIFFGetFieldDefaulted(tif
, TIFFTAG_COMPRESSION
, &compression
) )
526 image
->SetOption(wxIMAGE_OPTION_TIFF_COMPRESSION
, compression
);
529 // Set the resolution unit.
530 wxImageResolution resUnit
= wxIMAGE_RESOLUTION_NONE
;
532 if ( TIFFGetFieldDefaulted(tif
, TIFFTAG_RESOLUTIONUNIT
, &tiffRes
) )
537 wxLogWarning(_("Unknown TIFF resolution unit %d ignored"),
542 resUnit
= wxIMAGE_RESOLUTION_NONE
;
546 resUnit
= wxIMAGE_RESOLUTION_INCHES
;
549 case RESUNIT_CENTIMETER
:
550 resUnit
= wxIMAGE_RESOLUTION_CM
;
555 image
->SetOption(wxIMAGE_OPTION_RESOLUTIONUNIT
, resUnit
);
558 Set the image resolution if it's available. Resolution tag is not
559 dependant on RESOLUTIONUNIT != RESUNIT_NONE (according to TIFF spec).
563 if ( TIFFGetField(tif
, TIFFTAG_XRESOLUTION
, &resX
) )
566 Use a string value to not lose precision.
567 rounding to int as cm and then converting to inch may
568 result in whole integer rounding error, eg. 201 instead of 200 dpi.
569 If an app wants an int, GetOptionInt will convert and round down.
571 image
->SetOption(wxIMAGE_OPTION_RESOLUTIONX
,
572 wxString::FromCDouble((double) resX
));
575 if ( TIFFGetField(tif
, TIFFTAG_YRESOLUTION
, &resY
) )
577 image
->SetOption(wxIMAGE_OPTION_RESOLUTIONY
,
578 wxString::FromCDouble((double) resY
));
588 int wxTIFFHandler::DoGetImageCount( wxInputStream
& stream
)
590 TIFF
*tif
= TIFFwxOpen( stream
, "image", "r" );
595 int dircount
= 0; // according to the libtiff docs, dircount should be set to 1 here???
598 } while (TIFFReadDirectory(tif
));
602 // NOTE: this function modifies the current stream position but it's ok
603 // (see wxImageHandler::GetImageCount)
608 bool wxTIFFHandler::SaveFile( wxImage
*image
, wxOutputStream
& stream
, bool verbose
)
610 TIFF
*tif
= TIFFwxOpen( stream
, "image", "w" );
616 wxLogError( _("TIFF: Error saving image.") );
622 const int imageWidth
= image
->GetWidth();
623 TIFFSetField(tif
, TIFFTAG_IMAGEWIDTH
, (uint32
) imageWidth
);
624 TIFFSetField(tif
, TIFFTAG_IMAGELENGTH
, (uint32
)image
->GetHeight());
625 TIFFSetField(tif
, TIFFTAG_ORIENTATION
, ORIENTATION_TOPLEFT
);
626 TIFFSetField(tif
, TIFFTAG_PLANARCONFIG
, PLANARCONFIG_CONTIG
);
628 // save the image resolution if we have it
630 const wxImageResolution res
= GetResolutionFromOptions(*image
, &xres
, &yres
);
635 wxFAIL_MSG( wxT("unknown image resolution units") );
638 case wxIMAGE_RESOLUTION_NONE
:
639 tiffRes
= RESUNIT_NONE
;
642 case wxIMAGE_RESOLUTION_INCHES
:
643 tiffRes
= RESUNIT_INCH
;
646 case wxIMAGE_RESOLUTION_CM
:
647 tiffRes
= RESUNIT_CENTIMETER
;
651 if ( tiffRes
!= RESUNIT_NONE
)
653 TIFFSetField(tif
, TIFFTAG_RESOLUTIONUNIT
, tiffRes
);
654 TIFFSetField(tif
, TIFFTAG_XRESOLUTION
, (float)xres
);
655 TIFFSetField(tif
, TIFFTAG_YRESOLUTION
, (float)yres
);
659 int spp
= image
->GetOptionInt(wxIMAGE_OPTION_TIFF_SAMPLESPERPIXEL
);
663 int bps
= image
->GetOptionInt(wxIMAGE_OPTION_TIFF_BITSPERSAMPLE
);
670 // One bit per sample combined with 3 samples per pixel is
671 // not allowed and crashes libtiff.
675 int photometric
= PHOTOMETRIC_RGB
;
677 if ( image
->HasOption(wxIMAGE_OPTION_TIFF_PHOTOMETRIC
) )
679 photometric
= image
->GetOptionInt(wxIMAGE_OPTION_TIFF_PHOTOMETRIC
);
680 if (photometric
== PHOTOMETRIC_MINISWHITE
681 || photometric
== PHOTOMETRIC_MINISBLACK
)
683 // either b/w or greyscale
689 photometric
= PHOTOMETRIC_MINISWHITE
;
692 const bool hasAlpha
= image
->HasAlpha();
694 int compression
= image
->GetOptionInt(wxIMAGE_OPTION_TIFF_COMPRESSION
);
695 if ( !compression
|| (compression
== COMPRESSION_JPEG
&& hasAlpha
) )
697 // We can't use COMPRESSION_LZW because current version of libtiff
698 // doesn't implement it ("no longer implemented due to Unisys patent
699 // enforcement") and other compression methods are lossy so we
700 // shouldn't use them by default -- and the only remaining one is none.
701 // Also JPEG compression for alpha images is not a good idea (viewers
702 // not opening the image properly).
703 compression
= COMPRESSION_NONE
;
708 (photometric
== PHOTOMETRIC_RGB
&& spp
== 4)
709 || (photometric
<= PHOTOMETRIC_MINISBLACK
&& spp
== 2)
712 // Compensate for user passing a SamplesPerPixel that includes
713 // the alpha channel.
718 int extraSamples
= hasAlpha
? 1 : 0;
720 TIFFSetField(tif
, TIFFTAG_SAMPLESPERPIXEL
, spp
+ extraSamples
);
721 TIFFSetField(tif
, TIFFTAG_BITSPERSAMPLE
, bps
);
722 TIFFSetField(tif
, TIFFTAG_PHOTOMETRIC
, photometric
);
723 TIFFSetField(tif
, TIFFTAG_COMPRESSION
, compression
);
727 uint16 extra
[] = { EXTRASAMPLE_UNSPECIFIED
};
728 TIFFSetField(tif
, TIFFTAG_EXTRASAMPLES
, (long) 1, &extra
);
731 // scanlinesize is determined by spp+extraSamples and bps
732 const tsize_t linebytes
=
733 (tsize_t
)((imageWidth
* (spp
+ extraSamples
) * bps
+ 7) / 8);
737 const bool isColouredImage
= (spp
> 1)
738 && (photometric
!= PHOTOMETRIC_MINISWHITE
)
739 && (photometric
!= PHOTOMETRIC_MINISBLACK
);
742 if (TIFFScanlineSize(tif
) > linebytes
|| !isColouredImage
|| hasAlpha
)
744 buf
= (unsigned char *)_TIFFmalloc(TIFFScanlineSize(tif
));
749 wxLogError( _("TIFF: Couldn't allocate memory.") );
762 TIFFSetField(tif
, TIFFTAG_ROWSPERSTRIP
,TIFFDefaultStripSize(tif
, (uint32
) -1));
764 const int bitsPerPixel
= (spp
+ extraSamples
) * bps
;
765 const int bytesPerPixel
= (bitsPerPixel
+ 7) / 8;
766 const int pixelsPerByte
= 8 / bitsPerPixel
;
767 int remainingPixelCount
= 0;
771 // How many pixels to write in the last byte column?
772 remainingPixelCount
= imageWidth
% pixelsPerByte
;
773 if (!remainingPixelCount
) remainingPixelCount
= pixelsPerByte
;
776 const bool minIsWhite
= (photometric
== PHOTOMETRIC_MINISWHITE
);
777 unsigned char *ptr
= image
->GetData();
778 for ( int row
= 0; row
< image
->GetHeight(); row
++ )
787 for ( int column
= 0; column
< imageWidth
; column
++ )
789 buf
[column
*4 ] = ptr
[column
*3 ];
790 buf
[column
*4 + 1] = ptr
[column
*3 + 1];
791 buf
[column
*4 + 2] = ptr
[column
*3 + 2];
792 buf
[column
*4 + 3] = image
->GetAlpha(column
, row
);
797 memcpy(buf
, ptr
, imageWidth
* 3);
800 else if (spp
* bps
== 8) // greyscale image
802 for ( int column
= 0; column
< imageWidth
; column
++ )
804 uint8 value
= ptr
[column
*3 + 1];
810 buf
[column
* bytesPerPixel
] = value
;
814 value
= image
->GetAlpha(column
, row
);
815 buf
[column
*bytesPerPixel
+1]
816 = minIsWhite
? 255 - value
: value
;
820 else // black and white image
822 for ( int column
= 0; column
< linebytes
; column
++ )
825 int pixelsPerByteCount
= (column
+ 1 != linebytes
)
827 : remainingPixelCount
;
828 for ( int bp
= 0; bp
< pixelsPerByteCount
; bp
++ )
830 if ( (ptr
[column
* 3 * pixelsPerByte
+ bp
*3 + 1] <=127)
833 // check only green as this is sufficient
834 reverse
|= (uint8
) (128 >> (bp
* bitsPerPixel
));
838 && (image
->GetAlpha(column
* pixelsPerByte
+ bp
,
839 row
) <= 127) == minIsWhite
)
841 reverse
|= (uint8
) (64 >> (bp
* bitsPerPixel
));
845 buf
[column
] = reverse
;
850 if ( TIFFWriteScanline(tif
, buf
? buf
: ptr
, (uint32
)row
, 0) < 0 )
854 wxLogError( _("TIFF: Error writing image.") );
864 ptr
+= imageWidth
* 3;
867 (void) TIFFClose(tif
);
875 bool wxTIFFHandler::DoCanRead( wxInputStream
& stream
)
877 unsigned char hdr
[2];
879 if ( !stream
.Read(&hdr
[0], WXSIZEOF(hdr
)) ) // it's ok to modify the stream position here
882 return (hdr
[0] == 'I' && hdr
[1] == 'I') ||
883 (hdr
[0] == 'M' && hdr
[1] == 'M');
886 #endif // wxUSE_STREAMS
888 /*static*/ wxVersionInfo
wxTIFFHandler::GetLibraryVersionInfo()
894 const wxString
ver(::TIFFGetVersion());
895 if ( wxSscanf(ver
, "LIBTIFF, Version %d.%d.%d", &major
, &minor
, µ
) != 3 )
897 wxLogDebug("Unrecognized libtiff version string \"%s\"", ver
);
905 const wxString desc
= ver
.BeforeFirst('\n', ©right
);
906 copyright
.Replace("\n", "");
908 return wxVersionInfo("libtiff", major
, minor
, micro
, desc
, copyright
);
911 #endif // wxUSE_LIBTIFF