]> git.saurik.com Git - wxWidgets.git/blame - src/common/imagtiff.cpp
fixing overrelease and out-of-bounds write, fixes #13725
[wxWidgets.git] / src / common / imagtiff.cpp
CommitLineData
257bcf28 1/////////////////////////////////////////////////////////////////////////////
38d4b1e4 2// Name: src/common/imagtiff.cpp
8f493002
VS
3// Purpose: wxImage TIFF handler
4// Author: Robert Roebling
257bcf28 5// RCS-ID: $Id$
8f493002 6// Copyright: (c) Robert Roebling
65571936 7// Licence: wxWindows licence
257bcf28
RR
8/////////////////////////////////////////////////////////////////////////////
9
426272a3
VZ
10// ============================================================================
11// declarations
12// ============================================================================
13
14// ----------------------------------------------------------------------------
15// headers
16// ----------------------------------------------------------------------------
17
257bcf28
RR
18// For compilers that support precompilation, includes "wx.h".
19#include "wx/wxprec.h"
20
21#ifdef __BORLANDC__
8898456d 22 #pragma hdrstop
257bcf28
RR
23#endif
24
c96ea657 25#if wxUSE_IMAGE && wxUSE_LIBTIFF
257bcf28 26
88a7a4e1 27#include "wx/imagtiff.h"
ccec9093 28#include "wx/versioninfo.h"
88a7a4e1 29
8898456d
WS
30#ifndef WX_PRECOMP
31 #include "wx/log.h"
32 #include "wx/app.h"
88a7a4e1 33 #include "wx/intl.h"
0bca0373 34 #include "wx/bitmap.h"
02761f6c 35 #include "wx/module.h"
34deb5cd 36 #include "wx/wxcrtvararg.h"
8898456d
WS
37#endif
38
257bcf28
RR
39extern "C"
40{
8df068ca
CE
41#ifdef __DMC__
42 #include "tif_config.h"
426272a3 43#endif
257bcf28
RR
44 #include "tiff.h"
45 #include "tiffio.h"
257bcf28
RR
46}
47#include "wx/filefn.h"
48#include "wx/wfstream.h"
257bcf28 49
0729bd19 50#ifndef TIFFLINKAGEMODE
0e1f8ea4 51 #define TIFFLINKAGEMODE LINKAGEMODE
19193a2c 52#endif
0729bd19 53
426272a3
VZ
54// ============================================================================
55// implementation
56// ============================================================================
57
58// ----------------------------------------------------------------------------
59// TIFF library error/warning handlers
60// ----------------------------------------------------------------------------
61
34deb5cd
VS
62static wxString
63FormatTiffMessage(const char *module, const char *fmt, va_list ap)
64{
65 char buf[512];
66 if ( wxCRT_VsnprintfA(buf, WXSIZEOF(buf), fmt, ap) <= 0 )
67 {
68 // this isn't supposed to happen, but if it does, it's better
69 // than nothing
70 strcpy(buf, "Incorrectly formatted TIFF message");
71 }
72 buf[WXSIZEOF(buf)-1] = 0; // make sure it is always NULL-terminated
73
74 wxString msg(buf);
75 if ( module )
76 msg += wxString::Format(_(" (in module \"%s\")"), module);
77
78 return msg;
79}
80
426272a3
VZ
81extern "C"
82{
83
84static void
34deb5cd 85TIFFwxWarningHandler(const char* module, const char *fmt, va_list ap)
426272a3 86{
34deb5cd 87 wxLogWarning("%s", FormatTiffMessage(module, fmt, ap));
426272a3
VZ
88}
89
90static void
34deb5cd 91TIFFwxErrorHandler(const char* module, const char *fmt, va_list ap)
426272a3 92{
34deb5cd 93 wxLogError("%s", FormatTiffMessage(module, fmt, ap));
426272a3
VZ
94}
95
96} // extern "C"
97
257bcf28
RR
98//-----------------------------------------------------------------------------
99// wxTIFFHandler
100//-----------------------------------------------------------------------------
101
257bcf28 102IMPLEMENT_DYNAMIC_CLASS(wxTIFFHandler,wxImageHandler)
257bcf28 103
426272a3
VZ
104wxTIFFHandler::wxTIFFHandler()
105{
106 m_name = wxT("TIFF file");
107 m_extension = wxT("tif");
ba4800d3 108 m_altExtensions.Add(wxT("tiff"));
4ca8531f 109 m_type = wxBITMAP_TYPE_TIFF;
426272a3
VZ
110 m_mime = wxT("image/tiff");
111 TIFFSetWarningHandler((TIFFErrorHandler) TIFFwxWarningHandler);
112 TIFFSetErrorHandler((TIFFErrorHandler) TIFFwxErrorHandler);
113}
114
e30285ab
VZ
115#if wxUSE_STREAMS
116
ec524671
VZ
117// helper to translate our, possibly 64 bit, wxFileOffset to TIFF, always 32
118// bit, toff_t
119static toff_t wxFileOffsetToTIFF(wxFileOffset ofs)
120{
121 if ( ofs == wxInvalidOffset )
122 return (toff_t)-1;
123
124 toff_t tofs = wx_truncate_cast(toff_t, ofs);
38d4b1e4 125 wxCHECK_MSG( (wxFileOffset)tofs == ofs, (toff_t)-1,
9a83f860 126 wxT("TIFF library doesn't support large files") );
ec524671
VZ
127
128 return tofs;
129}
130
131// another helper to convert standard seek mode to our
132static wxSeekMode wxSeekModeFromTIFF(int whence)
133{
134 switch ( whence )
135 {
136 case SEEK_SET:
137 return wxFromStart;
138
139 case SEEK_CUR:
140 return wxFromCurrent;
141
142 case SEEK_END:
143 return wxFromEnd;
144
145 default:
146 return wxFromCurrent;
147 }
148}
149
90350682
VZ
150extern "C"
151{
152
153tsize_t TIFFLINKAGEMODE
46f36538 154wxTIFFNullProc(thandle_t WXUNUSED(handle),
19193a2c
KB
155 tdata_t WXUNUSED(buf),
156 tsize_t WXUNUSED(size))
f6bcfd97
BP
157{
158 return (tsize_t) -1;
159}
160
90350682 161tsize_t TIFFLINKAGEMODE
46f36538 162wxTIFFReadProc(thandle_t handle, tdata_t buf, tsize_t size)
257bcf28
RR
163{
164 wxInputStream *stream = (wxInputStream*) handle;
165 stream->Read( (void*) buf, (size_t) size );
4a10ea8b 166 return wx_truncate_cast(tsize_t, stream->LastRead());
257bcf28
RR
167}
168
90350682 169tsize_t TIFFLINKAGEMODE
46f36538 170wxTIFFWriteProc(thandle_t handle, tdata_t buf, tsize_t size)
257bcf28
RR
171{
172 wxOutputStream *stream = (wxOutputStream*) handle;
173 stream->Write( (void*) buf, (size_t) size );
4a10ea8b 174 return wx_truncate_cast(tsize_t, stream->LastWrite());
257bcf28
RR
175}
176
90350682 177toff_t TIFFLINKAGEMODE
46f36538 178wxTIFFSeekIProc(thandle_t handle, toff_t off, int whence)
257bcf28
RR
179{
180 wxInputStream *stream = (wxInputStream*) handle;
13111b2a 181
ec524671
VZ
182 return wxFileOffsetToTIFF(stream->SeekI((wxFileOffset)off,
183 wxSeekModeFromTIFF(whence)));
257bcf28
RR
184}
185
90350682 186toff_t TIFFLINKAGEMODE
46f36538 187wxTIFFSeekOProc(thandle_t handle, toff_t off, int whence)
f6bcfd97
BP
188{
189 wxOutputStream *stream = (wxOutputStream*) handle;
f6bcfd97 190
7ffc9aff
DS
191 toff_t offset = wxFileOffsetToTIFF(
192 stream->SeekO((wxFileOffset)off, wxSeekModeFromTIFF(whence)) );
193
194 if (offset != (toff_t) -1 || whence != SEEK_SET)
195 {
196 return offset;
197 }
198
199
200 /*
201 Try to workaround problems with libtiff seeking past the end of streams.
202
203 This occurs when libtiff is writing tag entries past the end of a
204 stream but hasn't written the directory yet (which will be placed
205 before the tags and contain offsets to the just written tags).
206 The behaviour for seeking past the end of a stream is not consistent
207 and doesn't work with for example wxMemoryOutputStream. When this type
208 of seeking fails (with SEEK_SET), fill in the gap with zeroes and try
209 again.
210 */
211
212 wxFileOffset streamLength = stream->GetLength();
213 if (streamLength != wxInvalidOffset && (wxFileOffset) off > streamLength)
214 {
215 if (stream->SeekO(streamLength, wxFromStart) == wxInvalidOffset)
216 {
217 return (toff_t) -1;
218 }
219
220 for (wxFileOffset i = 0; i < (wxFileOffset) off - streamLength; ++i)
221 {
222 stream->PutC(0);
223 }
224 }
225
226 return wxFileOffsetToTIFF( stream->TellO() );
f6bcfd97
BP
227}
228
90350682 229int TIFFLINKAGEMODE
46f36538 230wxTIFFCloseIProc(thandle_t WXUNUSED(handle))
257bcf28 231{
be0d315e
VZ
232 // there is no need to close the input stream
233 return 0;
234}
235
236int TIFFLINKAGEMODE
46f36538 237wxTIFFCloseOProc(thandle_t handle)
be0d315e
VZ
238{
239 wxOutputStream *stream = (wxOutputStream*) handle;
240
241 return stream->Close() ? 0 : -1;
257bcf28
RR
242}
243
90350682 244toff_t TIFFLINKAGEMODE
46f36538 245wxTIFFSizeProc(thandle_t handle)
257bcf28 246{
f6bcfd97 247 wxStreamBase *stream = (wxStreamBase*) handle;
0b72db08 248 return (toff_t) stream->GetSize();
257bcf28
RR
249}
250
90350682 251int TIFFLINKAGEMODE
46f36538 252wxTIFFMapProc(thandle_t WXUNUSED(handle),
13111b2a
VZ
253 tdata_t* WXUNUSED(pbase),
254 toff_t* WXUNUSED(psize))
257bcf28
RR
255{
256 return 0;
257}
258
90350682 259void TIFFLINKAGEMODE
46f36538 260wxTIFFUnmapProc(thandle_t WXUNUSED(handle),
13111b2a
VZ
261 tdata_t WXUNUSED(base),
262 toff_t WXUNUSED(size))
257bcf28
RR
263{
264}
265
90350682
VZ
266} // extern "C"
267
257bcf28
RR
268TIFF*
269TIFFwxOpen(wxInputStream &stream, const char* name, const char* mode)
270{
271 TIFF* tif = TIFFClientOpen(name, mode,
272 (thandle_t) &stream,
46f36538
VZ
273 wxTIFFReadProc, wxTIFFNullProc,
274 wxTIFFSeekIProc, wxTIFFCloseIProc, wxTIFFSizeProc,
275 wxTIFFMapProc, wxTIFFUnmapProc);
257bcf28 276
257bcf28
RR
277 return tif;
278}
279
f6bcfd97
BP
280TIFF*
281TIFFwxOpen(wxOutputStream &stream, const char* name, const char* mode)
282{
283 TIFF* tif = TIFFClientOpen(name, mode,
284 (thandle_t) &stream,
46f36538
VZ
285 wxTIFFNullProc, wxTIFFWriteProc,
286 wxTIFFSeekOProc, wxTIFFCloseOProc, wxTIFFSizeProc,
287 wxTIFFMapProc, wxTIFFUnmapProc);
f6bcfd97
BP
288
289 return tif;
290}
257bcf28 291
700ec454 292bool wxTIFFHandler::LoadFile( wxImage *image, wxInputStream& stream, bool verbose, int index )
257bcf28 293{
60d43ad8
VS
294 if (index == -1)
295 index = 0;
296
257bcf28 297 image->Destroy();
13111b2a 298
0b72db08 299 TIFF *tif = TIFFwxOpen( stream, "image", "r" );
13111b2a 300
257bcf28
RR
301 if (!tif)
302 {
303 if (verbose)
af588446 304 {
58c837a4 305 wxLogError( _("TIFF: Error loading image.") );
af588446 306 }
13111b2a 307
7beb59f3 308 return false;
257bcf28 309 }
13111b2a 310
700ec454
RR
311 if (!TIFFSetDirectory( tif, (tdir_t)index ))
312 {
313 if (verbose)
af588446 314 {
700ec454 315 wxLogError( _("Invalid TIFF image index.") );
af588446 316 }
13111b2a 317
700ec454 318 TIFFClose( tif );
13111b2a 319
7beb59f3 320 return false;
700ec454 321 }
257bcf28
RR
322
323 uint32 w, h;
257bcf28 324 uint32 *raster;
13111b2a 325
257bcf28
RR
326 TIFFGetField( tif, TIFFTAG_IMAGEWIDTH, &w );
327 TIFFGetField( tif, TIFFTAG_IMAGELENGTH, &h );
13111b2a 328
ebbaec82
DS
329 uint16 samplesPerPixel = 0;
330 (void) TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &samplesPerPixel);
331
332 uint16 bitsPerSample = 0;
333 (void) TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bitsPerSample);
334
b6ac40dc
VS
335 uint16 extraSamples;
336 uint16* samplesInfo;
337 TIFFGetFieldDefaulted(tif, TIFFTAG_EXTRASAMPLES,
338 &extraSamples, &samplesInfo);
ebbaec82
DS
339
340 uint16 photometric;
3d59540f
DS
341 if (!TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric))
342 {
343 photometric = PHOTOMETRIC_MINISWHITE;
344 }
345 const bool hasAlpha = (extraSamples >= 1
ebbaec82 346 && ((samplesInfo[0] == EXTRASAMPLE_UNSPECIFIED)
3d59540f
DS
347 || samplesInfo[0] == EXTRASAMPLE_ASSOCALPHA
348 || samplesInfo[0] == EXTRASAMPLE_UNASSALPHA))
349 || (extraSamples == 0 && samplesPerPixel == 4
350 && photometric == PHOTOMETRIC_RGB);
b6ac40dc 351
5beedebb
VZ
352 // guard against integer overflow during multiplication which could result
353 // in allocating a too small buffer and then overflowing it
ca588225 354 const double bytesNeeded = (double)w * (double)h * sizeof(uint32);
5beedebb
VZ
355 if ( bytesNeeded >= wxUINT32_MAX )
356 {
357 if ( verbose )
af588446 358 {
5beedebb 359 wxLogError( _("TIFF: Image size is abnormally big.") );
af588446 360 }
5beedebb
VZ
361
362 TIFFClose(tif);
363
364 return false;
365 }
13111b2a 366
8f2a8de6 367 raster = (uint32*) _TIFFmalloc( (uint32)bytesNeeded );
13111b2a 368
257bcf28
RR
369 if (!raster)
370 {
371 if (verbose)
af588446 372 {
58c837a4 373 wxLogError( _("TIFF: Couldn't allocate memory.") );
af588446 374 }
38755449 375
f6bcfd97 376 TIFFClose( tif );
13111b2a 377
7beb59f3 378 return false;
257bcf28
RR
379 }
380
479cd5de 381 image->Create( (int)w, (int)h );
a1b806b9 382 if (!image->IsOk())
257bcf28
RR
383 {
384 if (verbose)
af588446 385 {
58c837a4 386 wxLogError( _("TIFF: Couldn't allocate memory.") );
af588446 387 }
13111b2a
VZ
388
389 _TIFFfree( raster );
f6bcfd97 390 TIFFClose( tif );
13111b2a 391
7beb59f3 392 return false;
257bcf28 393 }
13111b2a 394
b6ac40dc
VS
395 if ( hasAlpha )
396 image->SetAlpha();
397
f60e3134
DS
398 uint16 planarConfig = PLANARCONFIG_CONTIG;
399 (void) TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &planarConfig);
400
401 bool ok = true;
402 char msg[1024] = "";
ebbaec82
DS
403 if
404 (
405 (planarConfig == PLANARCONFIG_CONTIG && samplesPerPixel == 2
406 && extraSamples == 1)
407 &&
408 (
409 ( !TIFFRGBAImageOK(tif, msg) )
410 || (bitsPerSample == 8)
411 )
412 )
f60e3134 413 {
ebbaec82 414 const bool isGreyScale = (bitsPerSample == 8);
f60e3134
DS
415 unsigned char *buf = (unsigned char *)_TIFFmalloc(TIFFScanlineSize(tif));
416 uint32 pos = 0;
ebbaec82
DS
417 const bool minIsWhite = (photometric == PHOTOMETRIC_MINISWHITE);
418 const int minValue = minIsWhite ? 255 : 0;
f60e3134
DS
419 const int maxValue = 255 - minValue;
420
421 /*
422 Decode to ABGR format as that is what the code, that converts to
423 wxImage, later on expects (normally TIFFReadRGBAImageOriented is
424 used to decode which uses an ABGR layout).
425 */
426 for (uint32 y = 0; y < h; ++y)
427 {
428 if (TIFFReadScanline(tif, buf, y, 0) != 1)
429 {
430 ok = false;
431 break;
432 }
433
ebbaec82 434 if (isGreyScale)
f60e3134 435 {
ebbaec82
DS
436 for (uint32 x = 0; x < w; ++x)
437 {
438 uint8 val = minIsWhite ? 255 - buf[x*2] : buf[x*2];
439 uint8 alpha = minIsWhite ? 255 - buf[x*2+1] : buf[x*2+1];
440 raster[pos] = val + (val << 8) + (val << 16)
441 + (alpha << 24);
442 pos++;
443 }
444 }
445 else
446 {
447 for (uint32 x = 0; x < w; ++x)
448 {
449 int mask = buf[x*2/8] << ((x*2)%8);
450
451 uint8 val = mask & 128 ? maxValue : minValue;
452 raster[pos] = val + (val << 8) + (val << 16)
453 + ((mask & 64 ? maxValue : minValue) << 24);
454 pos++;
455 }
f60e3134
DS
456 }
457 }
458
459 _TIFFfree(buf);
460 }
461 else
462 {
463 ok = TIFFReadRGBAImageOriented( tif, w, h, raster,
464 ORIENTATION_TOPLEFT, 0 ) != 0;
465 }
466
467
468 if (!ok)
257bcf28
RR
469 {
470 if (verbose)
af588446 471 {
58c837a4 472 wxLogError( _("TIFF: Error reading image.") );
af588446 473 }
13111b2a
VZ
474
475 _TIFFfree( raster );
476 image->Destroy();
f6bcfd97 477 TIFFClose( tif );
13111b2a 478
7beb59f3 479 return false;
257bcf28 480 }
13111b2a 481
257bcf28 482 unsigned char *ptr = image->GetData();
b6ac40dc 483
c6973bab 484 unsigned char *alpha = image->GetAlpha();
b6ac40dc 485
257bcf28 486 uint32 pos = 0;
13111b2a 487
257bcf28
RR
488 for (uint32 i = 0; i < h; i++)
489 {
ff7c6c9c 490 for (uint32 j = 0; j < w; j++)
13111b2a 491 {
b6ac40dc
VS
492 *(ptr++) = (unsigned char)TIFFGetR(raster[pos]);
493 *(ptr++) = (unsigned char)TIFFGetG(raster[pos]);
494 *(ptr++) = (unsigned char)TIFFGetB(raster[pos]);
495 if ( hasAlpha )
496 *(alpha++) = (unsigned char)TIFFGetA(raster[pos]);
497
13111b2a
VZ
498 pos++;
499 }
257bcf28 500 }
13111b2a 501
4d37f425 502
fc695138
DS
503 image->SetOption(wxIMAGE_OPTION_TIFF_PHOTOMETRIC, photometric);
504
ebbaec82 505 uint16 compression;
4d37f425 506 /*
ebbaec82 507 Copy some baseline TIFF tags which helps when re-saving a TIFF
4d37f425
DS
508 to be similar to the original image.
509 */
ebbaec82 510 if (samplesPerPixel)
4d37f425 511 {
ebbaec82 512 image->SetOption(wxIMAGE_OPTION_TIFF_SAMPLESPERPIXEL, samplesPerPixel);
4d37f425
DS
513 }
514
ebbaec82 515 if (bitsPerSample)
4d37f425 516 {
ebbaec82 517 image->SetOption(wxIMAGE_OPTION_TIFF_BITSPERSAMPLE, bitsPerSample);
4d37f425
DS
518 }
519
520 if ( TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &compression) )
521 {
d65172db 522 image->SetOption(wxIMAGE_OPTION_TIFF_COMPRESSION, compression);
4d37f425
DS
523 }
524
525 // Set the resolution unit.
526 wxImageResolution resUnit = wxIMAGE_RESOLUTION_NONE;
37ba70a5 527 uint16 tiffRes;
4d37f425 528 if ( TIFFGetFieldDefaulted(tif, TIFFTAG_RESOLUTIONUNIT, &tiffRes) )
37ba70a5 529 {
4d37f425 530 switch (tiffRes)
37ba70a5
VZ
531 {
532 default:
533 wxLogWarning(_("Unknown TIFF resolution unit %d ignored"),
4d37f425 534 tiffRes);
37ba70a5
VZ
535 // fall through
536
537 case RESUNIT_NONE:
4d37f425 538 resUnit = wxIMAGE_RESOLUTION_NONE;
37ba70a5
VZ
539 break;
540
541 case RESUNIT_INCH:
4d37f425 542 resUnit = wxIMAGE_RESOLUTION_INCHES;
37ba70a5
VZ
543 break;
544
545 case RESUNIT_CENTIMETER:
4d37f425 546 resUnit = wxIMAGE_RESOLUTION_CM;
37ba70a5
VZ
547 break;
548 }
4d37f425 549 }
37ba70a5 550
4d37f425 551 image->SetOption(wxIMAGE_OPTION_RESOLUTIONUNIT, resUnit);
37ba70a5 552
4d37f425
DS
553 /*
554 Set the image resolution if it's available. Resolution tag is not
37424888 555 dependent on RESOLUTIONUNIT != RESUNIT_NONE (according to TIFF spec).
4d37f425
DS
556 */
557 float resX, resY;
558
559 if ( TIFFGetField(tif, TIFFTAG_XRESOLUTION, &resX) )
560 {
561 /*
562 Use a string value to not lose precision.
563 rounding to int as cm and then converting to inch may
564 result in whole integer rounding error, eg. 201 instead of 200 dpi.
565 If an app wants an int, GetOptionInt will convert and round down.
566 */
567 image->SetOption(wxIMAGE_OPTION_RESOLUTIONX,
568 wxString::FromCDouble((double) resX));
37ba70a5
VZ
569 }
570
4d37f425
DS
571 if ( TIFFGetField(tif, TIFFTAG_YRESOLUTION, &resY) )
572 {
573 image->SetOption(wxIMAGE_OPTION_RESOLUTIONY,
574 wxString::FromCDouble((double) resY));
575 }
37ba70a5 576
257bcf28 577 _TIFFfree( raster );
13111b2a 578
257bcf28 579 TIFFClose( tif );
13111b2a 580
7beb59f3 581 return true;
257bcf28
RR
582}
583
8faef7cc 584int wxTIFFHandler::DoGetImageCount( wxInputStream& stream )
700ec454
RR
585{
586 TIFF *tif = TIFFwxOpen( stream, "image", "r" );
13111b2a 587
700ec454 588 if (!tif)
13111b2a 589 return 0;
257bcf28 590
700ec454
RR
591 int dircount = 0; // according to the libtiff docs, dircount should be set to 1 here???
592 do {
593 dircount++;
594 } while (TIFFReadDirectory(tif));
13111b2a 595
700ec454 596 TIFFClose( tif );
03647350 597
8faef7cc
FM
598 // NOTE: this function modifies the current stream position but it's ok
599 // (see wxImageHandler::GetImageCount)
13111b2a 600
700ec454
RR
601 return dircount;
602}
257bcf28 603
f6bcfd97 604bool wxTIFFHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbose )
257bcf28 605{
f6bcfd97
BP
606 TIFF *tif = TIFFwxOpen( stream, "image", "w" );
607
608 if (!tif)
609 {
610 if (verbose)
af588446 611 {
f6bcfd97 612 wxLogError( _("TIFF: Error saving image.") );
af588446 613 }
f6bcfd97 614
7beb59f3 615 return false;
f6bcfd97
BP
616 }
617
ab176b4b
DS
618 const int imageWidth = image->GetWidth();
619 TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, (uint32) imageWidth);
f6bcfd97
BP
620 TIFFSetField(tif, TIFFTAG_IMAGELENGTH, (uint32)image->GetHeight());
621 TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
f6bcfd97 622 TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
38755449 623
37ba70a5
VZ
624 // save the image resolution if we have it
625 int xres, yres;
626 const wxImageResolution res = GetResolutionFromOptions(*image, &xres, &yres);
627 uint16 tiffRes;
628 switch ( res )
fe9308c6 629 {
37ba70a5 630 default:
9a83f860 631 wxFAIL_MSG( wxT("unknown image resolution units") );
37ba70a5
VZ
632 // fall through
633
634 case wxIMAGE_RESOLUTION_NONE:
635 tiffRes = RESUNIT_NONE;
636 break;
637
638 case wxIMAGE_RESOLUTION_INCHES:
639 tiffRes = RESUNIT_INCH;
640 break;
641
642 case wxIMAGE_RESOLUTION_CM:
643 tiffRes = RESUNIT_CENTIMETER;
644 break;
fe9308c6
VZ
645 }
646
37ba70a5
VZ
647 if ( tiffRes != RESUNIT_NONE )
648 {
649 TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, tiffRes);
650 TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float)xres);
651 TIFFSetField(tif, TIFFTAG_YRESOLUTION, (float)yres);
652 }
653
654
d65172db 655 int spp = image->GetOptionInt(wxIMAGE_OPTION_TIFF_SAMPLESPERPIXEL);
fe9308c6
VZ
656 if ( !spp )
657 spp = 3;
658
096c930c
DS
659 int bps = image->GetOptionInt(wxIMAGE_OPTION_TIFF_BITSPERSAMPLE);
660 if ( !bps )
0e11090e 661 {
096c930c 662 bps = 8;
0e11090e
DS
663 }
664 else if (bps == 1)
665 {
666 // One bit per sample combined with 3 samples per pixel is
667 // not allowed and crashes libtiff.
668 spp = 1;
669 }
fe9308c6 670
fc695138
DS
671 int photometric = PHOTOMETRIC_RGB;
672
673 if ( image->HasOption(wxIMAGE_OPTION_TIFF_PHOTOMETRIC) )
674 {
675 photometric = image->GetOptionInt(wxIMAGE_OPTION_TIFF_PHOTOMETRIC);
676 if (photometric == PHOTOMETRIC_MINISWHITE
677 || photometric == PHOTOMETRIC_MINISBLACK)
678 {
679 // either b/w or greyscale
680 spp = 1;
681 }
682 }
ab176b4b 683 else if (spp <= 2)
fc695138 684 {
2453ceee 685 photometric = PHOTOMETRIC_MINISWHITE;
fc695138
DS
686 }
687
ab176b4b 688 const bool hasAlpha = image->HasAlpha();
fc695138 689
d65172db 690 int compression = image->GetOptionInt(wxIMAGE_OPTION_TIFF_COMPRESSION);
ab176b4b 691 if ( !compression || (compression == COMPRESSION_JPEG && hasAlpha) )
f5e20985 692 {
ab176b4b 693 // We can't use COMPRESSION_LZW because current version of libtiff
f5e20985
VZ
694 // doesn't implement it ("no longer implemented due to Unisys patent
695 // enforcement") and other compression methods are lossy so we
ab176b4b
DS
696 // shouldn't use them by default -- and the only remaining one is none.
697 // Also JPEG compression for alpha images is not a good idea (viewers
698 // not opening the image properly).
f5e20985
VZ
699 compression = COMPRESSION_NONE;
700 }
fe9308c6 701
ab176b4b
DS
702 if
703 (
704 (photometric == PHOTOMETRIC_RGB && spp == 4)
705 || (photometric <= PHOTOMETRIC_MINISBLACK && spp == 2)
706 )
707 {
708 // Compensate for user passing a SamplesPerPixel that includes
709 // the alpha channel.
710 spp--;
711 }
712
713
714 int extraSamples = hasAlpha ? 1 : 0;
715
716 TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, spp + extraSamples);
096c930c 717 TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bps);
fc695138 718 TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, photometric);
fe9308c6
VZ
719 TIFFSetField(tif, TIFFTAG_COMPRESSION, compression);
720
ab176b4b
DS
721 if (extraSamples)
722 {
723 uint16 extra[] = { EXTRASAMPLE_UNSPECIFIED };
724 TIFFSetField(tif, TIFFTAG_EXTRASAMPLES, (long) 1, &extra);
725 }
726
727 // scanlinesize is determined by spp+extraSamples and bps
649df4bf 728 const tsize_t linebytes =
ab176b4b 729 (tsize_t)((imageWidth * (spp + extraSamples) * bps + 7) / 8);
fe9308c6 730
f6bcfd97 731 unsigned char *buf;
38755449 732
ab176b4b
DS
733 const bool isColouredImage = (spp > 1)
734 && (photometric != PHOTOMETRIC_MINISWHITE)
735 && (photometric != PHOTOMETRIC_MINISBLACK);
736
737
738 if (TIFFScanlineSize(tif) > linebytes || !isColouredImage || hasAlpha)
f6bcfd97
BP
739 {
740 buf = (unsigned char *)_TIFFmalloc(TIFFScanlineSize(tif));
741 if (!buf)
742 {
743 if (verbose)
af588446 744 {
f6bcfd97 745 wxLogError( _("TIFF: Couldn't allocate memory.") );
af588446 746 }
f6bcfd97
BP
747
748 TIFFClose( tif );
749
7beb59f3 750 return false;
f6bcfd97 751 }
38755449
DW
752 }
753 else
f6bcfd97
BP
754 {
755 buf = NULL;
756 }
757
fe9308c6
VZ
758 TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP,TIFFDefaultStripSize(tif, (uint32) -1));
759
ab176b4b
DS
760 const int bitsPerPixel = (spp + extraSamples) * bps;
761 const int bytesPerPixel = (bitsPerPixel + 7) / 8;
f2de33ed
DS
762 const int pixelsPerByte = 8 / bitsPerPixel;
763 int remainingPixelCount = 0;
764
765 if (pixelsPerByte)
766 {
767 // How many pixels to write in the last byte column?
ab176b4b
DS
768 remainingPixelCount = imageWidth % pixelsPerByte;
769 if (!remainingPixelCount) remainingPixelCount = pixelsPerByte;
f2de33ed
DS
770 }
771
fc695138 772 const bool minIsWhite = (photometric == PHOTOMETRIC_MINISWHITE);
f6bcfd97 773 unsigned char *ptr = image->GetData();
fe9308c6 774 for ( int row = 0; row < image->GetHeight(); row++ )
f6bcfd97 775 {
fe9308c6
VZ
776 if ( buf )
777 {
fc695138 778 if (isColouredImage)
fe9308c6 779 {
ab176b4b
DS
780 // colour image
781 if (hasAlpha)
782 {
783 for ( int column = 0; column < imageWidth; column++ )
784 {
785 buf[column*4 ] = ptr[column*3 ];
786 buf[column*4 + 1] = ptr[column*3 + 1];
787 buf[column*4 + 2] = ptr[column*3 + 2];
788 buf[column*4 + 3] = image->GetAlpha(column, row);
789 }
790 }
791 else
792 {
793 memcpy(buf, ptr, imageWidth * 3);
794 }
fe9308c6 795 }
8e28cc47
DS
796 else if (spp * bps == 8) // greyscale image
797 {
ab176b4b 798 for ( int column = 0; column < imageWidth; column++ )
8e28cc47 799 {
fc695138
DS
800 uint8 value = ptr[column*3 + 1];
801 if (minIsWhite)
802 {
803 value = 255 - value;
804 }
805
ab176b4b
DS
806 buf[column * bytesPerPixel] = value;
807
808 if (hasAlpha)
809 {
810 value = image->GetAlpha(column, row);
811 buf[column*bytesPerPixel+1]
812 = minIsWhite ? 255 - value : value;
813 }
8e28cc47
DS
814 }
815 }
fe9308c6
VZ
816 else // black and white image
817 {
818 for ( int column = 0; column < linebytes; column++ )
819 {
820 uint8 reverse = 0;
f2de33ed
DS
821 int pixelsPerByteCount = (column + 1 != linebytes)
822 ? pixelsPerByte
823 : remainingPixelCount;
824 for ( int bp = 0; bp < pixelsPerByteCount; bp++ )
fe9308c6 825 {
ab176b4b
DS
826 if ( (ptr[column * 3 * pixelsPerByte + bp*3 + 1] <=127)
827 == minIsWhite )
fe9308c6 828 {
7f77b25e 829 // check only green as this is sufficient
ab176b4b
DS
830 reverse |= (uint8) (128 >> (bp * bitsPerPixel));
831 }
832
833 if (hasAlpha
834 && (image->GetAlpha(column * pixelsPerByte + bp,
835 row) <= 127) == minIsWhite)
836 {
837 reverse |= (uint8) (64 >> (bp * bitsPerPixel));
fe9308c6 838 }
fe9308c6
VZ
839 }
840
3ba9ea72 841 buf[column] = reverse;
fe9308c6
VZ
842 }
843 }
844 }
38755449 845
fe9308c6 846 if ( TIFFWriteScanline(tif, buf ? buf : ptr, (uint32)row, 0) < 0 )
f6bcfd97 847 {
19193a2c 848 if (verbose)
af588446 849 {
19193a2c 850 wxLogError( _("TIFF: Error writing image.") );
af588446 851 }
38755449 852
f6bcfd97
BP
853 TIFFClose( tif );
854 if (buf)
855 _TIFFfree(buf);
38755449 856
7beb59f3 857 return false;
f6bcfd97 858 }
fe9308c6 859
ab176b4b 860 ptr += imageWidth * 3;
f6bcfd97
BP
861 }
862
863 (void) TIFFClose(tif);
864
865 if (buf)
fe9308c6 866 _TIFFfree(buf);
f6bcfd97 867
7beb59f3 868 return true;
257bcf28
RR
869}
870
871bool wxTIFFHandler::DoCanRead( wxInputStream& stream )
872{
0b72db08 873 unsigned char hdr[2];
257bcf28 874
8faef7cc 875 if ( !stream.Read(&hdr[0], WXSIZEOF(hdr)) ) // it's ok to modify the stream position here
7beb59f3 876 return false;
79fa2374 877
79fa2374
VZ
878 return (hdr[0] == 'I' && hdr[1] == 'I') ||
879 (hdr[0] == 'M' && hdr[1] == 'M');
257bcf28
RR
880}
881
e30285ab 882#endif // wxUSE_STREAMS
257bcf28 883
ccec9093
VZ
884/*static*/ wxVersionInfo wxTIFFHandler::GetLibraryVersionInfo()
885{
886 int major,
887 minor,
888 micro;
889
890 const wxString ver(::TIFFGetVersion());
9c1f960f 891 if ( wxSscanf(ver, "LIBTIFF, Version %d.%d.%d", &major, &minor, &micro) != 3 )
ccec9093
VZ
892 {
893 wxLogDebug("Unrecognized libtiff version string \"%s\"", ver);
894
895 major =
896 minor =
897 micro = 0;
898 }
899
900 wxString copyright;
901 const wxString desc = ver.BeforeFirst('\n', &copyright);
902 copyright.Replace("\n", "");
903
904 return wxVersionInfo("libtiff", major, minor, micro, desc, copyright);
905}
906
e30285ab 907#endif // wxUSE_LIBTIFF