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:
405 // set the image resolution if it's available
407 if ( TIFFGetField(tif
, TIFFTAG_RESOLUTIONUNIT
, &tiffRes
) )
409 wxImageResolution res
;
413 wxLogWarning(_("Unknown TIFF resolution unit %d ignored"),
418 res
= wxIMAGE_RESOLUTION_NONE
;
422 res
= wxIMAGE_RESOLUTION_INCHES
;
425 case RESUNIT_CENTIMETER
:
426 res
= wxIMAGE_RESOLUTION_CM
;
430 if ( res
!= wxIMAGE_RESOLUTION_NONE
)
433 if ( TIFFGetField(tif
, TIFFTAG_XRESOLUTION
, &xres
) )
434 image
->SetOption(wxIMAGE_OPTION_RESOLUTIONX
, wxRound(xres
));
436 if ( TIFFGetField(tif
, TIFFTAG_YRESOLUTION
, &yres
) )
437 image
->SetOption(wxIMAGE_OPTION_RESOLUTIONY
, wxRound(yres
));
449 int wxTIFFHandler::DoGetImageCount( wxInputStream
& stream
)
451 TIFF
*tif
= TIFFwxOpen( stream
, "image", "r" );
456 int dircount
= 0; // according to the libtiff docs, dircount should be set to 1 here???
459 } while (TIFFReadDirectory(tif
));
463 // NOTE: this function modifies the current stream position but it's ok
464 // (see wxImageHandler::GetImageCount)
469 bool wxTIFFHandler::SaveFile( wxImage
*image
, wxOutputStream
& stream
, bool verbose
)
471 TIFF
*tif
= TIFFwxOpen( stream
, "image", "w" );
477 wxLogError( _("TIFF: Error saving image.") );
483 TIFFSetField(tif
, TIFFTAG_ORIENTATION
, ORIENTATION_TOPLEFT
);
484 TIFFSetField(tif
, TIFFTAG_IMAGEWIDTH
, (uint32
)image
->GetWidth());
485 TIFFSetField(tif
, TIFFTAG_IMAGELENGTH
, (uint32
)image
->GetHeight());
486 TIFFSetField(tif
, TIFFTAG_ORIENTATION
, ORIENTATION_TOPLEFT
);
487 TIFFSetField(tif
, TIFFTAG_PLANARCONFIG
, PLANARCONFIG_CONTIG
);
489 // save the image resolution if we have it
491 const wxImageResolution res
= GetResolutionFromOptions(*image
, &xres
, &yres
);
496 wxFAIL_MSG( wxT("unknown image resolution units") );
499 case wxIMAGE_RESOLUTION_NONE
:
500 tiffRes
= RESUNIT_NONE
;
503 case wxIMAGE_RESOLUTION_INCHES
:
504 tiffRes
= RESUNIT_INCH
;
507 case wxIMAGE_RESOLUTION_CM
:
508 tiffRes
= RESUNIT_CENTIMETER
;
512 if ( tiffRes
!= RESUNIT_NONE
)
514 TIFFSetField(tif
, TIFFTAG_RESOLUTIONUNIT
, tiffRes
);
515 TIFFSetField(tif
, TIFFTAG_XRESOLUTION
, (float)xres
);
516 TIFFSetField(tif
, TIFFTAG_YRESOLUTION
, (float)yres
);
520 int spp
= image
->GetOptionInt(wxIMAGE_OPTION_SAMPLESPERPIXEL
);
524 int bpp
= image
->GetOptionInt(wxIMAGE_OPTION_BITSPERSAMPLE
);
528 int compression
= image
->GetOptionInt(wxIMAGE_OPTION_COMPRESSION
);
531 // we can't use COMPRESSION_LZW because current version of libtiff
532 // doesn't implement it ("no longer implemented due to Unisys patent
533 // enforcement") and other compression methods are lossy so we
534 // shouldn't use them by default -- and the only remaining one is none
535 compression
= COMPRESSION_NONE
;
538 TIFFSetField(tif
, TIFFTAG_SAMPLESPERPIXEL
, spp
);
539 TIFFSetField(tif
, TIFFTAG_BITSPERSAMPLE
, bpp
);
540 TIFFSetField(tif
, TIFFTAG_PHOTOMETRIC
, spp
*bpp
== 1 ? PHOTOMETRIC_MINISBLACK
542 TIFFSetField(tif
, TIFFTAG_COMPRESSION
, compression
);
544 // scanlinesize if determined by spp and bpp
545 tsize_t linebytes
= (tsize_t
)image
->GetWidth() * spp
* bpp
/ 8;
547 if ( (image
->GetWidth() % 8 > 0) && (spp
* bpp
< 8) )
552 if (TIFFScanlineSize(tif
) > linebytes
|| (spp
* bpp
< 24))
554 buf
= (unsigned char *)_TIFFmalloc(TIFFScanlineSize(tif
));
559 wxLogError( _("TIFF: Couldn't allocate memory.") );
572 TIFFSetField(tif
, TIFFTAG_ROWSPERSTRIP
,TIFFDefaultStripSize(tif
, (uint32
) -1));
574 unsigned char *ptr
= image
->GetData();
575 for ( int row
= 0; row
< image
->GetHeight(); row
++ )
582 memcpy(buf
, ptr
, image
->GetWidth());
584 else // black and white image
586 for ( int column
= 0; column
< linebytes
; column
++ )
589 for ( int bp
= 0; bp
< 8; bp
++ )
591 if ( ptr
[column
*24 + bp
*3] > 0 )
593 // check only red as this is sufficient
594 reverse
= (uint8
)(reverse
| 128 >> bp
);
598 buf
[column
] = reverse
;
603 if ( TIFFWriteScanline(tif
, buf
? buf
: ptr
, (uint32
)row
, 0) < 0 )
607 wxLogError( _("TIFF: Error writing image.") );
617 ptr
+= image
->GetWidth()*3;
620 (void) TIFFClose(tif
);
628 bool wxTIFFHandler::DoCanRead( wxInputStream
& stream
)
630 unsigned char hdr
[2];
632 if ( !stream
.Read(&hdr
[0], WXSIZEOF(hdr
)) ) // it's ok to modify the stream position here
635 return (hdr
[0] == 'I' && hdr
[1] == 'I') ||
636 (hdr
[0] == 'M' && hdr
[1] == 'M');
639 #endif // wxUSE_STREAMS
641 /*static*/ wxVersionInfo
wxTIFFHandler::GetLibraryVersionInfo()
647 const wxString
ver(::TIFFGetVersion());
648 if ( wxSscanf(ver
, "LIBTIFF, Version %d.%d.%d", &major
, &minor
, µ
) != 3 )
650 wxLogDebug("Unrecognized libtiff version string \"%s\"", ver
);
658 const wxString desc
= ver
.BeforeFirst('\n', ©right
);
659 copyright
.Replace("\n", "");
661 return wxVersionInfo("libtiff", major
, minor
, micro
, desc
, copyright
);
664 #endif // wxUSE_LIBTIFF