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 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
17 #if wxUSE_IMAGE && wxUSE_LIBTIFF
19 #include "wx/imagtiff.h"
25 #include "wx/bitmap.h"
26 #include "wx/module.h"
32 #include "tif_config.h"
37 #include "wx/filefn.h"
38 #include "wx/wfstream.h"
40 #ifndef TIFFLINKAGEMODE
41 #if defined(__WATCOMC__) && defined(__WXMGL__)
42 #define TIFFLINKAGEMODE cdecl
44 #define TIFFLINKAGEMODE LINKAGEMODE
48 //-----------------------------------------------------------------------------
50 //-----------------------------------------------------------------------------
52 IMPLEMENT_DYNAMIC_CLASS(wxTIFFHandler
,wxImageHandler
)
56 // helper to translate our, possibly 64 bit, wxFileOffset to TIFF, always 32
58 static toff_t
wxFileOffsetToTIFF(wxFileOffset ofs
)
60 if ( ofs
== wxInvalidOffset
)
63 toff_t tofs
= wx_truncate_cast(toff_t
, ofs
);
64 wxCHECK_MSG( (wxFileOffset
)tofs
== ofs
, (toff_t
)-1,
65 _T("TIFF library doesn't support large files") );
70 // another helper to convert standard seek mode to our
71 static wxSeekMode
wxSeekModeFromTIFF(int whence
)
92 tsize_t TIFFLINKAGEMODE
93 wxTIFFNullProc(thandle_t
WXUNUSED(handle
),
94 tdata_t
WXUNUSED(buf
),
95 tsize_t
WXUNUSED(size
))
100 tsize_t TIFFLINKAGEMODE
101 wxTIFFReadProc(thandle_t handle
, tdata_t buf
, tsize_t size
)
103 wxInputStream
*stream
= (wxInputStream
*) handle
;
104 stream
->Read( (void*) buf
, (size_t) size
);
105 return wx_truncate_cast(tsize_t
, stream
->LastRead());
108 tsize_t TIFFLINKAGEMODE
109 wxTIFFWriteProc(thandle_t handle
, tdata_t buf
, tsize_t size
)
111 wxOutputStream
*stream
= (wxOutputStream
*) handle
;
112 stream
->Write( (void*) buf
, (size_t) size
);
113 return wx_truncate_cast(tsize_t
, stream
->LastWrite());
116 toff_t TIFFLINKAGEMODE
117 wxTIFFSeekIProc(thandle_t handle
, toff_t off
, int whence
)
119 wxInputStream
*stream
= (wxInputStream
*) handle
;
121 return wxFileOffsetToTIFF(stream
->SeekI((wxFileOffset
)off
,
122 wxSeekModeFromTIFF(whence
)));
125 toff_t TIFFLINKAGEMODE
126 wxTIFFSeekOProc(thandle_t handle
, toff_t off
, int whence
)
128 wxOutputStream
*stream
= (wxOutputStream
*) handle
;
130 return wxFileOffsetToTIFF(stream
->SeekO((wxFileOffset
)off
,
131 wxSeekModeFromTIFF(whence
)));
135 wxTIFFCloseIProc(thandle_t
WXUNUSED(handle
))
137 // there is no need to close the input stream
142 wxTIFFCloseOProc(thandle_t handle
)
144 wxOutputStream
*stream
= (wxOutputStream
*) handle
;
146 return stream
->Close() ? 0 : -1;
149 toff_t TIFFLINKAGEMODE
150 wxTIFFSizeProc(thandle_t handle
)
152 wxStreamBase
*stream
= (wxStreamBase
*) handle
;
153 return (toff_t
) stream
->GetSize();
157 wxTIFFMapProc(thandle_t
WXUNUSED(handle
),
158 tdata_t
* WXUNUSED(pbase
),
159 toff_t
* WXUNUSED(psize
))
165 wxTIFFUnmapProc(thandle_t
WXUNUSED(handle
),
166 tdata_t
WXUNUSED(base
),
167 toff_t
WXUNUSED(size
))
172 TIFFwxWarningHandler(const char* module,
173 const char* WXUNUSED_IN_UNICODE(fmt
),
174 va_list WXUNUSED_IN_UNICODE(ap
))
177 wxLogWarning(_("tiff module: %s"), wxString::FromAscii(module).c_str());
179 // FIXME: this is not terrible informative but better than crashing!
181 wxLogWarning(_("TIFF library warning."));
183 wxVLogWarning(fmt
, ap
);
188 TIFFwxErrorHandler(const char* module,
189 const char* WXUNUSED_IN_UNICODE(fmt
),
190 va_list WXUNUSED_IN_UNICODE(ap
))
193 wxLogError(_("tiff module: %s"), wxString::FromAscii(module).c_str());
197 wxLogError(_("TIFF library error."));
199 wxVLogError(fmt
, ap
);
206 TIFFwxOpen(wxInputStream
&stream
, const char* name
, const char* mode
)
208 TIFF
* tif
= TIFFClientOpen(name
, mode
,
210 wxTIFFReadProc
, wxTIFFNullProc
,
211 wxTIFFSeekIProc
, wxTIFFCloseIProc
, wxTIFFSizeProc
,
212 wxTIFFMapProc
, wxTIFFUnmapProc
);
218 TIFFwxOpen(wxOutputStream
&stream
, const char* name
, const char* mode
)
220 TIFF
* tif
= TIFFClientOpen(name
, mode
,
222 wxTIFFNullProc
, wxTIFFWriteProc
,
223 wxTIFFSeekOProc
, wxTIFFCloseOProc
, wxTIFFSizeProc
,
224 wxTIFFMapProc
, wxTIFFUnmapProc
);
229 wxTIFFHandler::wxTIFFHandler()
231 m_name
= wxT("TIFF file");
232 m_extension
= wxT("tif");
233 m_type
= wxBITMAP_TYPE_TIF
;
234 m_mime
= wxT("image/tiff");
235 TIFFSetWarningHandler((TIFFErrorHandler
) TIFFwxWarningHandler
);
236 TIFFSetErrorHandler((TIFFErrorHandler
) TIFFwxErrorHandler
);
239 bool wxTIFFHandler::LoadFile( wxImage
*image
, wxInputStream
& stream
, bool verbose
, int index
)
246 TIFF
*tif
= TIFFwxOpen( stream
, "image", "r" );
251 wxLogError( _("TIFF: Error loading image.") );
256 if (!TIFFSetDirectory( tif
, (tdir_t
)index
))
259 wxLogError( _("Invalid TIFF image index.") );
270 TIFFGetField( tif
, TIFFTAG_IMAGEWIDTH
, &w
);
271 TIFFGetField( tif
, TIFFTAG_IMAGELENGTH
, &h
);
275 TIFFGetFieldDefaulted(tif
, TIFFTAG_EXTRASAMPLES
,
276 &extraSamples
, &samplesInfo
);
277 const bool hasAlpha
= (extraSamples
== 1 &&
278 (samplesInfo
[0] == EXTRASAMPLE_ASSOCALPHA
||
279 samplesInfo
[0] == EXTRASAMPLE_UNASSALPHA
));
283 raster
= (uint32
*) _TIFFmalloc( npixels
* sizeof(uint32
) );
288 wxLogError( _("TIFF: Couldn't allocate memory.") );
295 image
->Create( (int)w
, (int)h
);
299 wxLogError( _("TIFF: Couldn't allocate memory.") );
310 if (!TIFFReadRGBAImage( tif
, w
, h
, raster
, 0 ))
313 wxLogError( _("TIFF: Error reading image.") );
322 unsigned char *ptr
= image
->GetData();
325 unsigned char *alpha
= hasAlpha
? image
->GetAlpha() : NULL
;
331 for (uint32 i
= 0; i
< h
; i
++)
333 for (uint32 j
= 0; j
< w
; j
++)
335 *(ptr
++) = (unsigned char)TIFFGetR(raster
[pos
]);
336 *(ptr
++) = (unsigned char)TIFFGetG(raster
[pos
]);
337 *(ptr
++) = (unsigned char)TIFFGetB(raster
[pos
]);
339 *(alpha
++) = (unsigned char)TIFFGetA(raster
[pos
]);
344 // subtract line we just added plus one line:
350 // set the image resolution if it's available
352 if ( TIFFGetField(tif
, TIFFTAG_RESOLUTIONUNIT
, &tiffRes
) )
354 wxImageResolution res
;
358 wxLogWarning(_("Unknown TIFF resolution unit %d ignored"),
363 res
= wxIMAGE_RESOLUTION_NONE
;
367 res
= wxIMAGE_RESOLUTION_INCHES
;
370 case RESUNIT_CENTIMETER
:
371 res
= wxIMAGE_RESOLUTION_CM
;
375 if ( res
!= wxIMAGE_RESOLUTION_NONE
)
378 if ( TIFFGetField(tif
, TIFFTAG_XRESOLUTION
, &xres
) )
379 image
->SetOption(wxIMAGE_OPTION_RESOLUTIONX
, wxRound(xres
));
381 if ( TIFFGetField(tif
, TIFFTAG_YRESOLUTION
, &yres
) )
382 image
->SetOption(wxIMAGE_OPTION_RESOLUTIONY
, wxRound(yres
));
394 int wxTIFFHandler::GetImageCount( wxInputStream
& stream
)
396 TIFF
*tif
= TIFFwxOpen( stream
, "image", "r" );
401 int dircount
= 0; // according to the libtiff docs, dircount should be set to 1 here???
404 } while (TIFFReadDirectory(tif
));
411 bool wxTIFFHandler::SaveFile( wxImage
*image
, wxOutputStream
& stream
, bool verbose
)
413 TIFF
*tif
= TIFFwxOpen( stream
, "image", "w" );
418 wxLogError( _("TIFF: Error saving image.") );
423 TIFFSetField(tif
, TIFFTAG_ORIENTATION
, ORIENTATION_TOPLEFT
);
424 TIFFSetField(tif
, TIFFTAG_IMAGEWIDTH
, (uint32
)image
->GetWidth());
425 TIFFSetField(tif
, TIFFTAG_IMAGELENGTH
, (uint32
)image
->GetHeight());
426 TIFFSetField(tif
, TIFFTAG_ORIENTATION
, ORIENTATION_TOPLEFT
);
427 TIFFSetField(tif
, TIFFTAG_PLANARCONFIG
, PLANARCONFIG_CONTIG
);
429 // save the image resolution if we have it
431 const wxImageResolution res
= GetResolutionFromOptions(*image
, &xres
, &yres
);
436 wxFAIL_MSG( _T("unknown image resolution units") );
439 case wxIMAGE_RESOLUTION_NONE
:
440 tiffRes
= RESUNIT_NONE
;
443 case wxIMAGE_RESOLUTION_INCHES
:
444 tiffRes
= RESUNIT_INCH
;
447 case wxIMAGE_RESOLUTION_CM
:
448 tiffRes
= RESUNIT_CENTIMETER
;
452 if ( tiffRes
!= RESUNIT_NONE
)
454 TIFFSetField(tif
, TIFFTAG_RESOLUTIONUNIT
, tiffRes
);
455 TIFFSetField(tif
, TIFFTAG_XRESOLUTION
, (float)xres
);
456 TIFFSetField(tif
, TIFFTAG_YRESOLUTION
, (float)yres
);
460 int spp
= image
->GetOptionInt(wxIMAGE_OPTION_SAMPLESPERPIXEL
);
464 int bpp
= image
->GetOptionInt(wxIMAGE_OPTION_BITSPERSAMPLE
);
468 int compression
= image
->GetOptionInt(wxIMAGE_OPTION_COMPRESSION
);
471 // we can't use COMPRESSION_LZW because current version of libtiff
472 // doesn't implement it ("no longer implemented due to Unisys patent
473 // enforcement") and other compression methods are lossy so we
474 // shouldn't use them by default -- and the only remaining one is none
475 compression
= COMPRESSION_NONE
;
478 TIFFSetField(tif
, TIFFTAG_SAMPLESPERPIXEL
, spp
);
479 TIFFSetField(tif
, TIFFTAG_BITSPERSAMPLE
, bpp
);
480 TIFFSetField(tif
, TIFFTAG_PHOTOMETRIC
, spp
*bpp
== 1 ? PHOTOMETRIC_MINISBLACK
482 TIFFSetField(tif
, TIFFTAG_COMPRESSION
, compression
);
484 // scanlinesize if determined by spp and bpp
485 tsize_t linebytes
= (tsize_t
)image
->GetWidth() * spp
* bpp
/ 8;
487 if ( (image
->GetWidth() % 8 > 0) && (spp
* bpp
< 8) )
492 if (TIFFScanlineSize(tif
) > linebytes
|| (spp
* bpp
< 24))
494 buf
= (unsigned char *)_TIFFmalloc(TIFFScanlineSize(tif
));
498 wxLogError( _("TIFF: Couldn't allocate memory.") );
510 TIFFSetField(tif
, TIFFTAG_ROWSPERSTRIP
,TIFFDefaultStripSize(tif
, (uint32
) -1));
512 unsigned char *ptr
= image
->GetData();
513 for ( int row
= 0; row
< image
->GetHeight(); row
++ )
520 memcpy(buf
, ptr
, image
->GetWidth());
522 else // black and white image
524 for ( int column
= 0; column
< linebytes
; column
++ )
527 for ( int bp
= 0; bp
< 8; bp
++ )
529 if ( ptr
[column
*24 + bp
*3] > 0 )
531 // check only red as this is sufficient
532 reverse
= (uint8
)(reverse
| 128 >> bp
);
536 buf
[column
] = reverse
;
541 if ( TIFFWriteScanline(tif
, buf
? buf
: ptr
, (uint32
)row
, 0) < 0 )
544 wxLogError( _("TIFF: Error writing image.") );
553 ptr
+= image
->GetWidth()*3;
556 (void) TIFFClose(tif
);
564 bool wxTIFFHandler::DoCanRead( wxInputStream
& stream
)
566 unsigned char hdr
[2];
568 if ( !stream
.Read(&hdr
[0], WXSIZEOF(hdr
)) )
571 return (hdr
[0] == 'I' && hdr
[1] == 'I') ||
572 (hdr
[0] == 'M' && hdr
[1] == 'M');
575 #endif // wxUSE_STREAMS
577 #endif // wxUSE_LIBTIFF