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
);
334 uint16 samplesPerPixel
;
337 TIFFGetFieldDefaulted(tif
, TIFFTAG_SAMPLESPERPIXEL
, &samplesPerPixel
);
338 TIFFGetFieldDefaulted(tif
, TIFFTAG_EXTRASAMPLES
,
339 &extraSamples
, &samplesInfo
);
340 if (!TIFFGetField(tif
, TIFFTAG_PHOTOMETRIC
, &photometric
))
342 photometric
= PHOTOMETRIC_MINISWHITE
;
344 const bool hasAlpha
= (extraSamples
>= 1
345 && ((samplesInfo
[0] == EXTRASAMPLE_UNSPECIFIED
&& samplesPerPixel
> 3)
346 || samplesInfo
[0] == EXTRASAMPLE_ASSOCALPHA
347 || samplesInfo
[0] == EXTRASAMPLE_UNASSALPHA
))
348 || (extraSamples
== 0 && samplesPerPixel
== 4
349 && photometric
== PHOTOMETRIC_RGB
);
351 // guard against integer overflow during multiplication which could result
352 // in allocating a too small buffer and then overflowing it
353 const double bytesNeeded
= (double)w
* (double)h
* sizeof(uint32
);
354 if ( bytesNeeded
>= wxUINT32_MAX
)
358 wxLogError( _("TIFF: Image size is abnormally big.") );
366 raster
= (uint32
*) _TIFFmalloc( (uint32
)bytesNeeded
);
372 wxLogError( _("TIFF: Couldn't allocate memory.") );
380 image
->Create( (int)w
, (int)h
);
385 wxLogError( _("TIFF: Couldn't allocate memory.") );
397 uint16 planarConfig
= PLANARCONFIG_CONTIG
;
398 (void) TIFFGetField(tif
, TIFFTAG_PLANARCONFIG
, &planarConfig
);
402 if ( !TIFFRGBAImageOK(tif
, msg
)
403 && planarConfig
== PLANARCONFIG_CONTIG
404 && samplesPerPixel
== 2 && extraSamples
== 1)
406 unsigned char *buf
= (unsigned char *)_TIFFmalloc(TIFFScanlineSize(tif
));
408 const int minValue
= (photometric
== PHOTOMETRIC_MINISWHITE
) ? 255 : 0;
409 const int maxValue
= 255 - minValue
;
412 Decode to ABGR format as that is what the code, that converts to
413 wxImage, later on expects (normally TIFFReadRGBAImageOriented is
414 used to decode which uses an ABGR layout).
416 for (uint32 y
= 0; y
< h
; ++y
)
418 if (TIFFReadScanline(tif
, buf
, y
, 0) != 1)
424 for (uint32 x
= 0; x
< w
; ++x
)
426 int mask
= buf
[x
*2/8] << ((x
*2)%8
);
428 uint8 val
= mask
& 128 ? maxValue
: minValue
;
429 raster
[pos
] = val
+ (val
<< 8) + (val
<< 16)
430 + ((mask
& 64 ? maxValue
: minValue
) << 24);
439 ok
= TIFFReadRGBAImageOriented( tif
, w
, h
, raster
,
440 ORIENTATION_TOPLEFT
, 0 ) != 0;
448 wxLogError( _("TIFF: Error reading image.") );
458 unsigned char *ptr
= image
->GetData();
460 unsigned char *alpha
= image
->GetAlpha();
464 for (uint32 i
= 0; i
< h
; i
++)
466 for (uint32 j
= 0; j
< w
; j
++)
468 *(ptr
++) = (unsigned char)TIFFGetR(raster
[pos
]);
469 *(ptr
++) = (unsigned char)TIFFGetG(raster
[pos
]);
470 *(ptr
++) = (unsigned char)TIFFGetB(raster
[pos
]);
472 *(alpha
++) = (unsigned char)TIFFGetA(raster
[pos
]);
479 image
->SetOption(wxIMAGE_OPTION_TIFF_PHOTOMETRIC
, photometric
);
481 uint16 spp
, bps
, compression
;
483 Read some baseline TIFF tags which helps when re-saving a TIFF
484 to be similar to the original image.
486 if ( TIFFGetFieldDefaulted(tif
, TIFFTAG_SAMPLESPERPIXEL
, &spp
) )
488 image
->SetOption(wxIMAGE_OPTION_TIFF_SAMPLESPERPIXEL
, spp
);
491 if ( TIFFGetFieldDefaulted(tif
, TIFFTAG_BITSPERSAMPLE
, &bps
) )
493 image
->SetOption(wxIMAGE_OPTION_TIFF_BITSPERSAMPLE
, bps
);
496 if ( TIFFGetFieldDefaulted(tif
, TIFFTAG_COMPRESSION
, &compression
) )
498 image
->SetOption(wxIMAGE_OPTION_TIFF_COMPRESSION
, compression
);
501 // Set the resolution unit.
502 wxImageResolution resUnit
= wxIMAGE_RESOLUTION_NONE
;
504 if ( TIFFGetFieldDefaulted(tif
, TIFFTAG_RESOLUTIONUNIT
, &tiffRes
) )
509 wxLogWarning(_("Unknown TIFF resolution unit %d ignored"),
514 resUnit
= wxIMAGE_RESOLUTION_NONE
;
518 resUnit
= wxIMAGE_RESOLUTION_INCHES
;
521 case RESUNIT_CENTIMETER
:
522 resUnit
= wxIMAGE_RESOLUTION_CM
;
527 image
->SetOption(wxIMAGE_OPTION_RESOLUTIONUNIT
, resUnit
);
530 Set the image resolution if it's available. Resolution tag is not
531 dependant on RESOLUTIONUNIT != RESUNIT_NONE (according to TIFF spec).
535 if ( TIFFGetField(tif
, TIFFTAG_XRESOLUTION
, &resX
) )
538 Use a string value to not lose precision.
539 rounding to int as cm and then converting to inch may
540 result in whole integer rounding error, eg. 201 instead of 200 dpi.
541 If an app wants an int, GetOptionInt will convert and round down.
543 image
->SetOption(wxIMAGE_OPTION_RESOLUTIONX
,
544 wxString::FromCDouble((double) resX
));
547 if ( TIFFGetField(tif
, TIFFTAG_YRESOLUTION
, &resY
) )
549 image
->SetOption(wxIMAGE_OPTION_RESOLUTIONY
,
550 wxString::FromCDouble((double) resY
));
560 int wxTIFFHandler::DoGetImageCount( wxInputStream
& stream
)
562 TIFF
*tif
= TIFFwxOpen( stream
, "image", "r" );
567 int dircount
= 0; // according to the libtiff docs, dircount should be set to 1 here???
570 } while (TIFFReadDirectory(tif
));
574 // NOTE: this function modifies the current stream position but it's ok
575 // (see wxImageHandler::GetImageCount)
580 bool wxTIFFHandler::SaveFile( wxImage
*image
, wxOutputStream
& stream
, bool verbose
)
582 TIFF
*tif
= TIFFwxOpen( stream
, "image", "w" );
588 wxLogError( _("TIFF: Error saving image.") );
594 TIFFSetField(tif
, TIFFTAG_IMAGEWIDTH
, (uint32
)image
->GetWidth());
595 TIFFSetField(tif
, TIFFTAG_IMAGELENGTH
, (uint32
)image
->GetHeight());
596 TIFFSetField(tif
, TIFFTAG_ORIENTATION
, ORIENTATION_TOPLEFT
);
597 TIFFSetField(tif
, TIFFTAG_PLANARCONFIG
, PLANARCONFIG_CONTIG
);
599 // save the image resolution if we have it
601 const wxImageResolution res
= GetResolutionFromOptions(*image
, &xres
, &yres
);
606 wxFAIL_MSG( wxT("unknown image resolution units") );
609 case wxIMAGE_RESOLUTION_NONE
:
610 tiffRes
= RESUNIT_NONE
;
613 case wxIMAGE_RESOLUTION_INCHES
:
614 tiffRes
= RESUNIT_INCH
;
617 case wxIMAGE_RESOLUTION_CM
:
618 tiffRes
= RESUNIT_CENTIMETER
;
622 if ( tiffRes
!= RESUNIT_NONE
)
624 TIFFSetField(tif
, TIFFTAG_RESOLUTIONUNIT
, tiffRes
);
625 TIFFSetField(tif
, TIFFTAG_XRESOLUTION
, (float)xres
);
626 TIFFSetField(tif
, TIFFTAG_YRESOLUTION
, (float)yres
);
630 int spp
= image
->GetOptionInt(wxIMAGE_OPTION_TIFF_SAMPLESPERPIXEL
);
634 int bps
= image
->GetOptionInt(wxIMAGE_OPTION_TIFF_BITSPERSAMPLE
);
641 // One bit per sample combined with 3 samples per pixel is
642 // not allowed and crashes libtiff.
646 int photometric
= PHOTOMETRIC_RGB
;
648 if ( image
->HasOption(wxIMAGE_OPTION_TIFF_PHOTOMETRIC
) )
650 photometric
= image
->GetOptionInt(wxIMAGE_OPTION_TIFF_PHOTOMETRIC
);
651 if (photometric
== PHOTOMETRIC_MINISWHITE
652 || photometric
== PHOTOMETRIC_MINISBLACK
)
654 // either b/w or greyscale
660 photometric
= PHOTOMETRIC_MINISWHITE
;
663 const bool isColouredImage
= (spp
> 1)
664 && (photometric
!= PHOTOMETRIC_MINISWHITE
)
665 && (photometric
!= PHOTOMETRIC_MINISBLACK
);
667 int compression
= image
->GetOptionInt(wxIMAGE_OPTION_TIFF_COMPRESSION
);
670 // we can't use COMPRESSION_LZW because current version of libtiff
671 // doesn't implement it ("no longer implemented due to Unisys patent
672 // enforcement") and other compression methods are lossy so we
673 // shouldn't use them by default -- and the only remaining one is none
674 compression
= COMPRESSION_NONE
;
677 TIFFSetField(tif
, TIFFTAG_SAMPLESPERPIXEL
, spp
);
678 TIFFSetField(tif
, TIFFTAG_BITSPERSAMPLE
, bps
);
679 TIFFSetField(tif
, TIFFTAG_PHOTOMETRIC
, photometric
);
680 TIFFSetField(tif
, TIFFTAG_COMPRESSION
, compression
);
682 // scanlinesize is determined by spp and bps
683 const tsize_t linebytes
=
684 (tsize_t
)((image
->GetWidth() * spp
* bps
+ 7) / 8);
688 if (TIFFScanlineSize(tif
) > linebytes
|| !isColouredImage
)
690 buf
= (unsigned char *)_TIFFmalloc(TIFFScanlineSize(tif
));
695 wxLogError( _("TIFF: Couldn't allocate memory.") );
708 TIFFSetField(tif
, TIFFTAG_ROWSPERSTRIP
,TIFFDefaultStripSize(tif
, (uint32
) -1));
710 const int bitsPerPixel
= spp
* bps
;
711 const int pixelsPerByte
= 8 / bitsPerPixel
;
712 int remainingPixelCount
= 0;
716 // How many pixels to write in the last byte column?
717 remainingPixelCount
= image
->GetWidth() % pixelsPerByte
;
718 if (!remainingPixelCount
) remainingPixelCount
= 8;
721 const bool minIsWhite
= (photometric
== PHOTOMETRIC_MINISWHITE
);
722 unsigned char *ptr
= image
->GetData();
723 for ( int row
= 0; row
< image
->GetHeight(); row
++ )
730 memcpy(buf
, ptr
, image
->GetWidth());
732 else if (spp
* bps
== 8) // greyscale image
734 for ( int column
= 0; column
< linebytes
; column
++ )
736 uint8 value
= ptr
[column
*3 + 1];
745 else // black and white image
747 for ( int column
= 0; column
< linebytes
; column
++ )
750 int pixelsPerByteCount
= (column
+ 1 != linebytes
)
752 : remainingPixelCount
;
753 for ( int bp
= 0; bp
< pixelsPerByteCount
; bp
++ )
755 if ( (ptr
[column
*24 + bp
*3 + 1] <=127) == minIsWhite
)
757 // check only green as this is sufficient
758 reverse
= (uint8
)(reverse
| 128 >> bp
);
762 buf
[column
] = reverse
;
767 if ( TIFFWriteScanline(tif
, buf
? buf
: ptr
, (uint32
)row
, 0) < 0 )
771 wxLogError( _("TIFF: Error writing image.") );
781 ptr
+= image
->GetWidth()*3;
784 (void) TIFFClose(tif
);
792 bool wxTIFFHandler::DoCanRead( wxInputStream
& stream
)
794 unsigned char hdr
[2];
796 if ( !stream
.Read(&hdr
[0], WXSIZEOF(hdr
)) ) // it's ok to modify the stream position here
799 return (hdr
[0] == 'I' && hdr
[1] == 'I') ||
800 (hdr
[0] == 'M' && hdr
[1] == 'M');
803 #endif // wxUSE_STREAMS
805 /*static*/ wxVersionInfo
wxTIFFHandler::GetLibraryVersionInfo()
811 const wxString
ver(::TIFFGetVersion());
812 if ( wxSscanf(ver
, "LIBTIFF, Version %d.%d.%d", &major
, &minor
, µ
) != 3 )
814 wxLogDebug("Unrecognized libtiff version string \"%s\"", ver
);
822 const wxString desc
= ver
.BeforeFirst('\n', ©right
);
823 copyright
.Replace("\n", "");
825 return wxVersionInfo("libtiff", major
, minor
, micro
, desc
, copyright
);
828 #endif // wxUSE_LIBTIFF