1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/imagtiff.cpp
3 // Purpose: wxImage TIFF handler
4 // Author: Robert Roebling
5 // Copyright: (c) Robert Roebling
6 // Licence: wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
9 // ============================================================================
11 // ============================================================================
13 // ----------------------------------------------------------------------------
15 // ----------------------------------------------------------------------------
17 // For compilers that support precompilation, includes "wx.h".
18 #include "wx/wxprec.h"
24 #if wxUSE_IMAGE && wxUSE_LIBTIFF
26 #include "wx/imagtiff.h"
27 #include "wx/versioninfo.h"
33 #include "wx/bitmap.h"
34 #include "wx/module.h"
35 #include "wx/wxcrtvararg.h"
41 #include "tif_config.h"
46 #include "wx/filefn.h"
47 #include "wx/wfstream.h"
49 #ifndef TIFFLINKAGEMODE
50 #define TIFFLINKAGEMODE LINKAGEMODE
53 // ============================================================================
55 // ============================================================================
57 // ----------------------------------------------------------------------------
58 // TIFF library error/warning handlers
59 // ----------------------------------------------------------------------------
62 FormatTiffMessage(const char *module, const char *fmt
, va_list ap
)
65 if ( wxCRT_VsnprintfA(buf
, WXSIZEOF(buf
), fmt
, ap
) <= 0 )
67 // this isn't supposed to happen, but if it does, it's better
69 strcpy(buf
, "Incorrectly formatted TIFF message");
71 buf
[WXSIZEOF(buf
)-1] = 0; // make sure it is always NULL-terminated
75 msg
+= wxString::Format(_(" (in module \"%s\")"), module);
84 TIFFwxWarningHandler(const char* module, const char *fmt
, va_list ap
)
86 wxLogWarning("%s", FormatTiffMessage(module, fmt
, ap
));
90 TIFFwxErrorHandler(const char* module, const char *fmt
, va_list ap
)
92 wxLogError("%s", FormatTiffMessage(module, fmt
, ap
));
97 //-----------------------------------------------------------------------------
99 //-----------------------------------------------------------------------------
101 IMPLEMENT_DYNAMIC_CLASS(wxTIFFHandler
,wxImageHandler
)
103 wxTIFFHandler::wxTIFFHandler()
105 m_name
= wxT("TIFF file");
106 m_extension
= wxT("tif");
107 m_altExtensions
.Add(wxT("tiff"));
108 m_type
= wxBITMAP_TYPE_TIFF
;
109 m_mime
= wxT("image/tiff");
110 TIFFSetWarningHandler((TIFFErrorHandler
) TIFFwxWarningHandler
);
111 TIFFSetErrorHandler((TIFFErrorHandler
) TIFFwxErrorHandler
);
116 // helper to translate our, possibly 64 bit, wxFileOffset to TIFF, always 32
118 static toff_t
wxFileOffsetToTIFF(wxFileOffset ofs
)
120 if ( ofs
== wxInvalidOffset
)
123 toff_t tofs
= wx_truncate_cast(toff_t
, ofs
);
124 wxCHECK_MSG( (wxFileOffset
)tofs
== ofs
, (toff_t
)-1,
125 wxT("TIFF library doesn't support large files") );
130 // another helper to convert standard seek mode to our
131 static wxSeekMode
wxSeekModeFromTIFF(int whence
)
139 return wxFromCurrent
;
145 return wxFromCurrent
;
152 tsize_t TIFFLINKAGEMODE
153 wxTIFFNullProc(thandle_t
WXUNUSED(handle
),
154 tdata_t
WXUNUSED(buf
),
155 tsize_t
WXUNUSED(size
))
160 tsize_t TIFFLINKAGEMODE
161 wxTIFFReadProc(thandle_t handle
, tdata_t buf
, tsize_t size
)
163 wxInputStream
*stream
= (wxInputStream
*) handle
;
164 stream
->Read( (void*) buf
, (size_t) size
);
165 return wx_truncate_cast(tsize_t
, stream
->LastRead());
168 tsize_t TIFFLINKAGEMODE
169 wxTIFFWriteProc(thandle_t handle
, tdata_t buf
, tsize_t size
)
171 wxOutputStream
*stream
= (wxOutputStream
*) handle
;
172 stream
->Write( (void*) buf
, (size_t) size
);
173 return wx_truncate_cast(tsize_t
, stream
->LastWrite());
176 toff_t TIFFLINKAGEMODE
177 wxTIFFSeekIProc(thandle_t handle
, toff_t off
, int whence
)
179 wxInputStream
*stream
= (wxInputStream
*) handle
;
181 return wxFileOffsetToTIFF(stream
->SeekI((wxFileOffset
)off
,
182 wxSeekModeFromTIFF(whence
)));
185 toff_t TIFFLINKAGEMODE
186 wxTIFFSeekOProc(thandle_t handle
, toff_t off
, int whence
)
188 wxOutputStream
*stream
= (wxOutputStream
*) handle
;
190 toff_t offset
= wxFileOffsetToTIFF(
191 stream
->SeekO((wxFileOffset
)off
, wxSeekModeFromTIFF(whence
)) );
193 if (offset
!= (toff_t
) -1 || whence
!= SEEK_SET
)
200 Try to workaround problems with libtiff seeking past the end of streams.
202 This occurs when libtiff is writing tag entries past the end of a
203 stream but hasn't written the directory yet (which will be placed
204 before the tags and contain offsets to the just written tags).
205 The behaviour for seeking past the end of a stream is not consistent
206 and doesn't work with for example wxMemoryOutputStream. When this type
207 of seeking fails (with SEEK_SET), fill in the gap with zeroes and try
211 wxFileOffset streamLength
= stream
->GetLength();
212 if (streamLength
!= wxInvalidOffset
&& (wxFileOffset
) off
> streamLength
)
214 if (stream
->SeekO(streamLength
, wxFromStart
) == wxInvalidOffset
)
219 for (wxFileOffset i
= 0; i
< (wxFileOffset
) off
- streamLength
; ++i
)
225 return wxFileOffsetToTIFF( stream
->TellO() );
229 wxTIFFCloseIProc(thandle_t
WXUNUSED(handle
))
231 // there is no need to close the input stream
236 wxTIFFCloseOProc(thandle_t handle
)
238 wxOutputStream
*stream
= (wxOutputStream
*) handle
;
240 return stream
->Close() ? 0 : -1;
243 toff_t TIFFLINKAGEMODE
244 wxTIFFSizeProc(thandle_t handle
)
246 wxStreamBase
*stream
= (wxStreamBase
*) handle
;
247 return (toff_t
) stream
->GetSize();
251 wxTIFFMapProc(thandle_t
WXUNUSED(handle
),
252 tdata_t
* WXUNUSED(pbase
),
253 toff_t
* WXUNUSED(psize
))
259 wxTIFFUnmapProc(thandle_t
WXUNUSED(handle
),
260 tdata_t
WXUNUSED(base
),
261 toff_t
WXUNUSED(size
))
268 TIFFwxOpen(wxInputStream
&stream
, const char* name
, const char* mode
)
270 TIFF
* tif
= TIFFClientOpen(name
, mode
,
272 wxTIFFReadProc
, wxTIFFNullProc
,
273 wxTIFFSeekIProc
, wxTIFFCloseIProc
, wxTIFFSizeProc
,
274 wxTIFFMapProc
, wxTIFFUnmapProc
);
280 TIFFwxOpen(wxOutputStream
&stream
, const char* name
, const char* mode
)
282 TIFF
* tif
= TIFFClientOpen(name
, mode
,
284 wxTIFFNullProc
, wxTIFFWriteProc
,
285 wxTIFFSeekOProc
, wxTIFFCloseOProc
, wxTIFFSizeProc
,
286 wxTIFFMapProc
, wxTIFFUnmapProc
);
291 bool wxTIFFHandler::LoadFile( wxImage
*image
, wxInputStream
& stream
, bool verbose
, int index
)
298 TIFF
*tif
= TIFFwxOpen( stream
, "image", "r" );
304 wxLogError( _("TIFF: Error loading image.") );
310 if (!TIFFSetDirectory( tif
, (tdir_t
)index
))
314 wxLogError( _("Invalid TIFF image index.") );
325 TIFFGetField( tif
, TIFFTAG_IMAGEWIDTH
, &w
);
326 TIFFGetField( tif
, TIFFTAG_IMAGELENGTH
, &h
);
328 uint16 samplesPerPixel
= 0;
329 (void) TIFFGetFieldDefaulted(tif
, TIFFTAG_SAMPLESPERPIXEL
, &samplesPerPixel
);
331 uint16 bitsPerSample
= 0;
332 (void) TIFFGetFieldDefaulted(tif
, TIFFTAG_BITSPERSAMPLE
, &bitsPerSample
);
336 TIFFGetFieldDefaulted(tif
, TIFFTAG_EXTRASAMPLES
,
337 &extraSamples
, &samplesInfo
);
340 if (!TIFFGetField(tif
, TIFFTAG_PHOTOMETRIC
, &photometric
))
342 photometric
= PHOTOMETRIC_MINISWHITE
;
344 const bool hasAlpha
= (extraSamples
>= 1
345 && ((samplesInfo
[0] == EXTRASAMPLE_UNSPECIFIED
)
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
);
404 (planarConfig
== PLANARCONFIG_CONTIG
&& samplesPerPixel
== 2
405 && extraSamples
== 1)
408 ( !TIFFRGBAImageOK(tif
, msg
) )
409 || (bitsPerSample
== 8)
413 const bool isGreyScale
= (bitsPerSample
== 8);
414 unsigned char *buf
= (unsigned char *)_TIFFmalloc(TIFFScanlineSize(tif
));
416 const bool minIsWhite
= (photometric
== PHOTOMETRIC_MINISWHITE
);
417 const int minValue
= minIsWhite
? 255 : 0;
418 const int maxValue
= 255 - minValue
;
421 Decode to ABGR format as that is what the code, that converts to
422 wxImage, later on expects (normally TIFFReadRGBAImageOriented is
423 used to decode which uses an ABGR layout).
425 for (uint32 y
= 0; y
< h
; ++y
)
427 if (TIFFReadScanline(tif
, buf
, y
, 0) != 1)
435 for (uint32 x
= 0; x
< w
; ++x
)
437 uint8 val
= minIsWhite
? 255 - buf
[x
*2] : buf
[x
*2];
438 uint8 alpha
= minIsWhite
? 255 - buf
[x
*2+1] : buf
[x
*2+1];
439 raster
[pos
] = val
+ (val
<< 8) + (val
<< 16)
446 for (uint32 x
= 0; x
< w
; ++x
)
448 int mask
= buf
[x
*2/8] << ((x
*2)%8
);
450 uint8 val
= mask
& 128 ? maxValue
: minValue
;
451 raster
[pos
] = val
+ (val
<< 8) + (val
<< 16)
452 + ((mask
& 64 ? maxValue
: minValue
) << 24);
462 ok
= TIFFReadRGBAImageOriented( tif
, w
, h
, raster
,
463 ORIENTATION_TOPLEFT
, 0 ) != 0;
471 wxLogError( _("TIFF: Error reading image.") );
481 unsigned char *ptr
= image
->GetData();
483 unsigned char *alpha
= image
->GetAlpha();
487 for (uint32 i
= 0; i
< h
; i
++)
489 for (uint32 j
= 0; j
< w
; j
++)
491 *(ptr
++) = (unsigned char)TIFFGetR(raster
[pos
]);
492 *(ptr
++) = (unsigned char)TIFFGetG(raster
[pos
]);
493 *(ptr
++) = (unsigned char)TIFFGetB(raster
[pos
]);
495 *(alpha
++) = (unsigned char)TIFFGetA(raster
[pos
]);
502 image
->SetOption(wxIMAGE_OPTION_TIFF_PHOTOMETRIC
, photometric
);
506 Copy some baseline TIFF tags which helps when re-saving a TIFF
507 to be similar to the original image.
511 image
->SetOption(wxIMAGE_OPTION_TIFF_SAMPLESPERPIXEL
, samplesPerPixel
);
516 image
->SetOption(wxIMAGE_OPTION_TIFF_BITSPERSAMPLE
, bitsPerSample
);
519 if ( TIFFGetFieldDefaulted(tif
, TIFFTAG_COMPRESSION
, &compression
) )
521 image
->SetOption(wxIMAGE_OPTION_TIFF_COMPRESSION
, compression
);
524 // Set the resolution unit.
525 wxImageResolution resUnit
= wxIMAGE_RESOLUTION_NONE
;
527 if ( TIFFGetFieldDefaulted(tif
, TIFFTAG_RESOLUTIONUNIT
, &tiffRes
) )
532 wxLogWarning(_("Unknown TIFF resolution unit %d ignored"),
537 resUnit
= wxIMAGE_RESOLUTION_NONE
;
541 resUnit
= wxIMAGE_RESOLUTION_INCHES
;
544 case RESUNIT_CENTIMETER
:
545 resUnit
= wxIMAGE_RESOLUTION_CM
;
550 image
->SetOption(wxIMAGE_OPTION_RESOLUTIONUNIT
, resUnit
);
553 Set the image resolution if it's available. Resolution tag is not
554 dependent on RESOLUTIONUNIT != RESUNIT_NONE (according to TIFF spec).
558 if ( TIFFGetField(tif
, TIFFTAG_XRESOLUTION
, &resX
) )
561 Use a string value to not lose precision.
562 rounding to int as cm and then converting to inch may
563 result in whole integer rounding error, eg. 201 instead of 200 dpi.
564 If an app wants an int, GetOptionInt will convert and round down.
566 image
->SetOption(wxIMAGE_OPTION_RESOLUTIONX
,
567 wxString::FromCDouble((double) resX
));
570 if ( TIFFGetField(tif
, TIFFTAG_YRESOLUTION
, &resY
) )
572 image
->SetOption(wxIMAGE_OPTION_RESOLUTIONY
,
573 wxString::FromCDouble((double) resY
));
583 int wxTIFFHandler::DoGetImageCount( wxInputStream
& stream
)
585 TIFF
*tif
= TIFFwxOpen( stream
, "image", "r" );
590 int dircount
= 0; // according to the libtiff docs, dircount should be set to 1 here???
593 } while (TIFFReadDirectory(tif
));
597 // NOTE: this function modifies the current stream position but it's ok
598 // (see wxImageHandler::GetImageCount)
603 bool wxTIFFHandler::SaveFile( wxImage
*image
, wxOutputStream
& stream
, bool verbose
)
605 TIFF
*tif
= TIFFwxOpen( stream
, "image", "w" );
611 wxLogError( _("TIFF: Error saving image.") );
617 const int imageWidth
= image
->GetWidth();
618 TIFFSetField(tif
, TIFFTAG_IMAGEWIDTH
, (uint32
) imageWidth
);
619 TIFFSetField(tif
, TIFFTAG_IMAGELENGTH
, (uint32
)image
->GetHeight());
620 TIFFSetField(tif
, TIFFTAG_ORIENTATION
, ORIENTATION_TOPLEFT
);
621 TIFFSetField(tif
, TIFFTAG_PLANARCONFIG
, PLANARCONFIG_CONTIG
);
623 // save the image resolution if we have it
625 const wxImageResolution res
= GetResolutionFromOptions(*image
, &xres
, &yres
);
630 wxFAIL_MSG( wxT("unknown image resolution units") );
633 case wxIMAGE_RESOLUTION_NONE
:
634 tiffRes
= RESUNIT_NONE
;
637 case wxIMAGE_RESOLUTION_INCHES
:
638 tiffRes
= RESUNIT_INCH
;
641 case wxIMAGE_RESOLUTION_CM
:
642 tiffRes
= RESUNIT_CENTIMETER
;
646 if ( tiffRes
!= RESUNIT_NONE
)
648 TIFFSetField(tif
, TIFFTAG_RESOLUTIONUNIT
, tiffRes
);
649 TIFFSetField(tif
, TIFFTAG_XRESOLUTION
, (float)xres
);
650 TIFFSetField(tif
, TIFFTAG_YRESOLUTION
, (float)yres
);
654 int spp
= image
->GetOptionInt(wxIMAGE_OPTION_TIFF_SAMPLESPERPIXEL
);
658 int bps
= image
->GetOptionInt(wxIMAGE_OPTION_TIFF_BITSPERSAMPLE
);
665 // One bit per sample combined with 3 samples per pixel is
666 // not allowed and crashes libtiff.
670 int photometric
= PHOTOMETRIC_RGB
;
672 if ( image
->HasOption(wxIMAGE_OPTION_TIFF_PHOTOMETRIC
) )
674 photometric
= image
->GetOptionInt(wxIMAGE_OPTION_TIFF_PHOTOMETRIC
);
675 if (photometric
== PHOTOMETRIC_MINISWHITE
676 || photometric
== PHOTOMETRIC_MINISBLACK
)
678 // either b/w or greyscale
684 photometric
= PHOTOMETRIC_MINISWHITE
;
687 const bool hasAlpha
= image
->HasAlpha();
689 int compression
= image
->GetOptionInt(wxIMAGE_OPTION_TIFF_COMPRESSION
);
690 if ( !compression
|| (compression
== COMPRESSION_JPEG
&& hasAlpha
) )
692 // We can't use COMPRESSION_LZW because current version of libtiff
693 // doesn't implement it ("no longer implemented due to Unisys patent
694 // enforcement") and other compression methods are lossy so we
695 // shouldn't use them by default -- and the only remaining one is none.
696 // Also JPEG compression for alpha images is not a good idea (viewers
697 // not opening the image properly).
698 compression
= COMPRESSION_NONE
;
703 (photometric
== PHOTOMETRIC_RGB
&& spp
== 4)
704 || (photometric
<= PHOTOMETRIC_MINISBLACK
&& spp
== 2)
707 // Compensate for user passing a SamplesPerPixel that includes
708 // the alpha channel.
713 int extraSamples
= hasAlpha
? 1 : 0;
715 TIFFSetField(tif
, TIFFTAG_SAMPLESPERPIXEL
, spp
+ extraSamples
);
716 TIFFSetField(tif
, TIFFTAG_BITSPERSAMPLE
, bps
);
717 TIFFSetField(tif
, TIFFTAG_PHOTOMETRIC
, photometric
);
718 TIFFSetField(tif
, TIFFTAG_COMPRESSION
, compression
);
722 uint16 extra
[] = { EXTRASAMPLE_UNSPECIFIED
};
723 TIFFSetField(tif
, TIFFTAG_EXTRASAMPLES
, (long) 1, &extra
);
726 // scanlinesize is determined by spp+extraSamples and bps
727 const tsize_t linebytes
=
728 (tsize_t
)((imageWidth
* (spp
+ extraSamples
) * bps
+ 7) / 8);
732 const bool isColouredImage
= (spp
> 1)
733 && (photometric
!= PHOTOMETRIC_MINISWHITE
)
734 && (photometric
!= PHOTOMETRIC_MINISBLACK
);
737 if (TIFFScanlineSize(tif
) > linebytes
|| !isColouredImage
|| hasAlpha
)
739 buf
= (unsigned char *)_TIFFmalloc(TIFFScanlineSize(tif
));
744 wxLogError( _("TIFF: Couldn't allocate memory.") );
757 TIFFSetField(tif
, TIFFTAG_ROWSPERSTRIP
,TIFFDefaultStripSize(tif
, (uint32
) -1));
759 const int bitsPerPixel
= (spp
+ extraSamples
) * bps
;
760 const int bytesPerPixel
= (bitsPerPixel
+ 7) / 8;
761 const int pixelsPerByte
= 8 / bitsPerPixel
;
762 int remainingPixelCount
= 0;
766 // How many pixels to write in the last byte column?
767 remainingPixelCount
= imageWidth
% pixelsPerByte
;
768 if (!remainingPixelCount
) remainingPixelCount
= pixelsPerByte
;
771 const bool minIsWhite
= (photometric
== PHOTOMETRIC_MINISWHITE
);
772 unsigned char *ptr
= image
->GetData();
773 for ( int row
= 0; row
< image
->GetHeight(); row
++ )
782 for ( int column
= 0; column
< imageWidth
; column
++ )
784 buf
[column
*4 ] = ptr
[column
*3 ];
785 buf
[column
*4 + 1] = ptr
[column
*3 + 1];
786 buf
[column
*4 + 2] = ptr
[column
*3 + 2];
787 buf
[column
*4 + 3] = image
->GetAlpha(column
, row
);
792 memcpy(buf
, ptr
, imageWidth
* 3);
795 else if (spp
* bps
== 8) // greyscale image
797 for ( int column
= 0; column
< imageWidth
; column
++ )
799 uint8 value
= ptr
[column
*3 + 1];
805 buf
[column
* bytesPerPixel
] = value
;
809 value
= image
->GetAlpha(column
, row
);
810 buf
[column
*bytesPerPixel
+1]
811 = minIsWhite
? 255 - value
: value
;
815 else // black and white image
817 for ( int column
= 0; column
< linebytes
; column
++ )
820 int pixelsPerByteCount
= (column
+ 1 != linebytes
)
822 : remainingPixelCount
;
823 for ( int bp
= 0; bp
< pixelsPerByteCount
; bp
++ )
825 if ( (ptr
[column
* 3 * pixelsPerByte
+ bp
*3 + 1] <=127)
828 // check only green as this is sufficient
829 reverse
|= (uint8
) (128 >> (bp
* bitsPerPixel
));
833 && (image
->GetAlpha(column
* pixelsPerByte
+ bp
,
834 row
) <= 127) == minIsWhite
)
836 reverse
|= (uint8
) (64 >> (bp
* bitsPerPixel
));
840 buf
[column
] = reverse
;
845 if ( TIFFWriteScanline(tif
, buf
? buf
: ptr
, (uint32
)row
, 0) < 0 )
849 wxLogError( _("TIFF: Error writing image.") );
859 ptr
+= imageWidth
* 3;
862 (void) TIFFClose(tif
);
870 bool wxTIFFHandler::DoCanRead( wxInputStream
& stream
)
872 unsigned char hdr
[2];
874 if ( !stream
.Read(&hdr
[0], WXSIZEOF(hdr
)) ) // it's ok to modify the stream position here
877 return (hdr
[0] == 'I' && hdr
[1] == 'I') ||
878 (hdr
[0] == 'M' && hdr
[1] == 'M');
881 #endif // wxUSE_STREAMS
883 /*static*/ wxVersionInfo
wxTIFFHandler::GetLibraryVersionInfo()
889 const wxString
ver(::TIFFGetVersion());
890 if ( wxSscanf(ver
, "LIBTIFF, Version %d.%d.%d", &major
, &minor
, µ
) != 3 )
892 wxLogDebug("Unrecognized libtiff version string \"%s\"", ver
);
900 const wxString desc
= ver
.BeforeFirst('\n', ©right
);
901 copyright
.Replace("\n", "");
903 return wxVersionInfo("libtiff", major
, minor
, micro
, desc
, copyright
);
906 #endif // wxUSE_LIBTIFF