]> git.saurik.com Git - wxWidgets.git/blame - src/common/imagtiff.cpp
Updated manual regarding image alpha support for TGA handler.
[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
0bca0373
WS
51 #if defined(__WATCOMC__) && defined(__WXMGL__)
52 #define TIFFLINKAGEMODE cdecl
53 #else
54 #define TIFFLINKAGEMODE LINKAGEMODE
55 #endif
19193a2c 56#endif
0729bd19 57
426272a3
VZ
58// ============================================================================
59// implementation
60// ============================================================================
61
62// ----------------------------------------------------------------------------
63// TIFF library error/warning handlers
64// ----------------------------------------------------------------------------
65
34deb5cd
VS
66static wxString
67FormatTiffMessage(const char *module, const char *fmt, va_list ap)
68{
69 char buf[512];
70 if ( wxCRT_VsnprintfA(buf, WXSIZEOF(buf), fmt, ap) <= 0 )
71 {
72 // this isn't supposed to happen, but if it does, it's better
73 // than nothing
74 strcpy(buf, "Incorrectly formatted TIFF message");
75 }
76 buf[WXSIZEOF(buf)-1] = 0; // make sure it is always NULL-terminated
77
78 wxString msg(buf);
79 if ( module )
80 msg += wxString::Format(_(" (in module \"%s\")"), module);
81
82 return msg;
83}
84
426272a3
VZ
85extern "C"
86{
87
88static void
34deb5cd 89TIFFwxWarningHandler(const char* module, const char *fmt, va_list ap)
426272a3 90{
34deb5cd 91 wxLogWarning("%s", FormatTiffMessage(module, fmt, ap));
426272a3
VZ
92}
93
94static void
34deb5cd 95TIFFwxErrorHandler(const char* module, const char *fmt, va_list ap)
426272a3 96{
34deb5cd 97 wxLogError("%s", FormatTiffMessage(module, fmt, ap));
426272a3
VZ
98}
99
100} // extern "C"
101
257bcf28
RR
102//-----------------------------------------------------------------------------
103// wxTIFFHandler
104//-----------------------------------------------------------------------------
105
257bcf28 106IMPLEMENT_DYNAMIC_CLASS(wxTIFFHandler,wxImageHandler)
257bcf28 107
426272a3
VZ
108wxTIFFHandler::wxTIFFHandler()
109{
110 m_name = wxT("TIFF file");
111 m_extension = wxT("tif");
ba4800d3 112 m_altExtensions.Add(wxT("tiff"));
4ca8531f 113 m_type = wxBITMAP_TYPE_TIFF;
426272a3
VZ
114 m_mime = wxT("image/tiff");
115 TIFFSetWarningHandler((TIFFErrorHandler) TIFFwxWarningHandler);
116 TIFFSetErrorHandler((TIFFErrorHandler) TIFFwxErrorHandler);
117}
118
e30285ab
VZ
119#if wxUSE_STREAMS
120
ec524671
VZ
121// helper to translate our, possibly 64 bit, wxFileOffset to TIFF, always 32
122// bit, toff_t
123static toff_t wxFileOffsetToTIFF(wxFileOffset ofs)
124{
125 if ( ofs == wxInvalidOffset )
126 return (toff_t)-1;
127
128 toff_t tofs = wx_truncate_cast(toff_t, ofs);
38d4b1e4 129 wxCHECK_MSG( (wxFileOffset)tofs == ofs, (toff_t)-1,
9a83f860 130 wxT("TIFF library doesn't support large files") );
ec524671
VZ
131
132 return tofs;
133}
134
135// another helper to convert standard seek mode to our
136static wxSeekMode wxSeekModeFromTIFF(int whence)
137{
138 switch ( whence )
139 {
140 case SEEK_SET:
141 return wxFromStart;
142
143 case SEEK_CUR:
144 return wxFromCurrent;
145
146 case SEEK_END:
147 return wxFromEnd;
148
149 default:
150 return wxFromCurrent;
151 }
152}
153
90350682
VZ
154extern "C"
155{
156
157tsize_t TIFFLINKAGEMODE
46f36538 158wxTIFFNullProc(thandle_t WXUNUSED(handle),
19193a2c
KB
159 tdata_t WXUNUSED(buf),
160 tsize_t WXUNUSED(size))
f6bcfd97
BP
161{
162 return (tsize_t) -1;
163}
164
90350682 165tsize_t TIFFLINKAGEMODE
46f36538 166wxTIFFReadProc(thandle_t handle, tdata_t buf, tsize_t size)
257bcf28
RR
167{
168 wxInputStream *stream = (wxInputStream*) handle;
169 stream->Read( (void*) buf, (size_t) size );
4a10ea8b 170 return wx_truncate_cast(tsize_t, stream->LastRead());
257bcf28
RR
171}
172
90350682 173tsize_t TIFFLINKAGEMODE
46f36538 174wxTIFFWriteProc(thandle_t handle, tdata_t buf, tsize_t size)
257bcf28
RR
175{
176 wxOutputStream *stream = (wxOutputStream*) handle;
177 stream->Write( (void*) buf, (size_t) size );
4a10ea8b 178 return wx_truncate_cast(tsize_t, stream->LastWrite());
257bcf28
RR
179}
180
90350682 181toff_t TIFFLINKAGEMODE
46f36538 182wxTIFFSeekIProc(thandle_t handle, toff_t off, int whence)
257bcf28
RR
183{
184 wxInputStream *stream = (wxInputStream*) handle;
13111b2a 185
ec524671
VZ
186 return wxFileOffsetToTIFF(stream->SeekI((wxFileOffset)off,
187 wxSeekModeFromTIFF(whence)));
257bcf28
RR
188}
189
90350682 190toff_t TIFFLINKAGEMODE
46f36538 191wxTIFFSeekOProc(thandle_t handle, toff_t off, int whence)
f6bcfd97
BP
192{
193 wxOutputStream *stream = (wxOutputStream*) handle;
f6bcfd97 194
7ffc9aff
DS
195 toff_t offset = wxFileOffsetToTIFF(
196 stream->SeekO((wxFileOffset)off, wxSeekModeFromTIFF(whence)) );
197
198 if (offset != (toff_t) -1 || whence != SEEK_SET)
199 {
200 return offset;
201 }
202
203
204 /*
205 Try to workaround problems with libtiff seeking past the end of streams.
206
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
213 again.
214 */
215
216 wxFileOffset streamLength = stream->GetLength();
217 if (streamLength != wxInvalidOffset && (wxFileOffset) off > streamLength)
218 {
219 if (stream->SeekO(streamLength, wxFromStart) == wxInvalidOffset)
220 {
221 return (toff_t) -1;
222 }
223
224 for (wxFileOffset i = 0; i < (wxFileOffset) off - streamLength; ++i)
225 {
226 stream->PutC(0);
227 }
228 }
229
230 return wxFileOffsetToTIFF( stream->TellO() );
f6bcfd97
BP
231}
232
90350682 233int TIFFLINKAGEMODE
46f36538 234wxTIFFCloseIProc(thandle_t WXUNUSED(handle))
257bcf28 235{
be0d315e
VZ
236 // there is no need to close the input stream
237 return 0;
238}
239
240int TIFFLINKAGEMODE
46f36538 241wxTIFFCloseOProc(thandle_t handle)
be0d315e
VZ
242{
243 wxOutputStream *stream = (wxOutputStream*) handle;
244
245 return stream->Close() ? 0 : -1;
257bcf28
RR
246}
247
90350682 248toff_t TIFFLINKAGEMODE
46f36538 249wxTIFFSizeProc(thandle_t handle)
257bcf28 250{
f6bcfd97 251 wxStreamBase *stream = (wxStreamBase*) handle;
0b72db08 252 return (toff_t) stream->GetSize();
257bcf28
RR
253}
254
90350682 255int TIFFLINKAGEMODE
46f36538 256wxTIFFMapProc(thandle_t WXUNUSED(handle),
13111b2a
VZ
257 tdata_t* WXUNUSED(pbase),
258 toff_t* WXUNUSED(psize))
257bcf28
RR
259{
260 return 0;
261}
262
90350682 263void TIFFLINKAGEMODE
46f36538 264wxTIFFUnmapProc(thandle_t WXUNUSED(handle),
13111b2a
VZ
265 tdata_t WXUNUSED(base),
266 toff_t WXUNUSED(size))
257bcf28
RR
267{
268}
269
90350682
VZ
270} // extern "C"
271
257bcf28
RR
272TIFF*
273TIFFwxOpen(wxInputStream &stream, const char* name, const char* mode)
274{
275 TIFF* tif = TIFFClientOpen(name, mode,
276 (thandle_t) &stream,
46f36538
VZ
277 wxTIFFReadProc, wxTIFFNullProc,
278 wxTIFFSeekIProc, wxTIFFCloseIProc, wxTIFFSizeProc,
279 wxTIFFMapProc, wxTIFFUnmapProc);
257bcf28 280
257bcf28
RR
281 return tif;
282}
283
f6bcfd97
BP
284TIFF*
285TIFFwxOpen(wxOutputStream &stream, const char* name, const char* mode)
286{
287 TIFF* tif = TIFFClientOpen(name, mode,
288 (thandle_t) &stream,
46f36538
VZ
289 wxTIFFNullProc, wxTIFFWriteProc,
290 wxTIFFSeekOProc, wxTIFFCloseOProc, wxTIFFSizeProc,
291 wxTIFFMapProc, wxTIFFUnmapProc);
f6bcfd97
BP
292
293 return tif;
294}
257bcf28 295
700ec454 296bool wxTIFFHandler::LoadFile( wxImage *image, wxInputStream& stream, bool verbose, int index )
257bcf28 297{
60d43ad8
VS
298 if (index == -1)
299 index = 0;
300
257bcf28 301 image->Destroy();
13111b2a 302
0b72db08 303 TIFF *tif = TIFFwxOpen( stream, "image", "r" );
13111b2a 304
257bcf28
RR
305 if (!tif)
306 {
307 if (verbose)
af588446 308 {
58c837a4 309 wxLogError( _("TIFF: Error loading image.") );
af588446 310 }
13111b2a 311
7beb59f3 312 return false;
257bcf28 313 }
13111b2a 314
700ec454
RR
315 if (!TIFFSetDirectory( tif, (tdir_t)index ))
316 {
317 if (verbose)
af588446 318 {
700ec454 319 wxLogError( _("Invalid TIFF image index.") );
af588446 320 }
13111b2a 321
700ec454 322 TIFFClose( tif );
13111b2a 323
7beb59f3 324 return false;
700ec454 325 }
257bcf28
RR
326
327 uint32 w, h;
257bcf28 328 uint32 *raster;
13111b2a 329
257bcf28
RR
330 TIFFGetField( tif, TIFFTAG_IMAGEWIDTH, &w );
331 TIFFGetField( tif, TIFFTAG_IMAGELENGTH, &h );
13111b2a 332
3d59540f
DS
333 uint16 photometric;
334 uint16 samplesPerPixel;
b6ac40dc
VS
335 uint16 extraSamples;
336 uint16* samplesInfo;
3d59540f 337 TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &samplesPerPixel);
b6ac40dc
VS
338 TIFFGetFieldDefaulted(tif, TIFFTAG_EXTRASAMPLES,
339 &extraSamples, &samplesInfo);
3d59540f
DS
340 if (!TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric))
341 {
342 photometric = PHOTOMETRIC_MINISWHITE;
343 }
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);
b6ac40dc 350
5beedebb
VZ
351 // guard against integer overflow during multiplication which could result
352 // in allocating a too small buffer and then overflowing it
ca588225 353 const double bytesNeeded = (double)w * (double)h * sizeof(uint32);
5beedebb
VZ
354 if ( bytesNeeded >= wxUINT32_MAX )
355 {
356 if ( verbose )
af588446 357 {
5beedebb 358 wxLogError( _("TIFF: Image size is abnormally big.") );
af588446 359 }
5beedebb
VZ
360
361 TIFFClose(tif);
362
363 return false;
364 }
13111b2a 365
8f2a8de6 366 raster = (uint32*) _TIFFmalloc( (uint32)bytesNeeded );
13111b2a 367
257bcf28
RR
368 if (!raster)
369 {
370 if (verbose)
af588446 371 {
58c837a4 372 wxLogError( _("TIFF: Couldn't allocate memory.") );
af588446 373 }
38755449 374
f6bcfd97 375 TIFFClose( tif );
13111b2a 376
7beb59f3 377 return false;
257bcf28
RR
378 }
379
479cd5de 380 image->Create( (int)w, (int)h );
a1b806b9 381 if (!image->IsOk())
257bcf28
RR
382 {
383 if (verbose)
af588446 384 {
58c837a4 385 wxLogError( _("TIFF: Couldn't allocate memory.") );
af588446 386 }
13111b2a
VZ
387
388 _TIFFfree( raster );
f6bcfd97 389 TIFFClose( tif );
13111b2a 390
7beb59f3 391 return false;
257bcf28 392 }
13111b2a 393
b6ac40dc
VS
394 if ( hasAlpha )
395 image->SetAlpha();
396
5d2be7cc 397 if (!TIFFReadRGBAImageOriented( tif, w, h, raster, ORIENTATION_TOPLEFT, 0 ))
257bcf28
RR
398 {
399 if (verbose)
af588446 400 {
58c837a4 401 wxLogError( _("TIFF: Error reading image.") );
af588446 402 }
13111b2a
VZ
403
404 _TIFFfree( raster );
405 image->Destroy();
f6bcfd97 406 TIFFClose( tif );
13111b2a 407
7beb59f3 408 return false;
257bcf28 409 }
13111b2a 410
257bcf28 411 unsigned char *ptr = image->GetData();
b6ac40dc 412
c6973bab 413 unsigned char *alpha = image->GetAlpha();
b6ac40dc 414
257bcf28 415 uint32 pos = 0;
13111b2a 416
257bcf28
RR
417 for (uint32 i = 0; i < h; i++)
418 {
ff7c6c9c 419 for (uint32 j = 0; j < w; j++)
13111b2a 420 {
b6ac40dc
VS
421 *(ptr++) = (unsigned char)TIFFGetR(raster[pos]);
422 *(ptr++) = (unsigned char)TIFFGetG(raster[pos]);
423 *(ptr++) = (unsigned char)TIFFGetB(raster[pos]);
424 if ( hasAlpha )
425 *(alpha++) = (unsigned char)TIFFGetA(raster[pos]);
426
13111b2a
VZ
427 pos++;
428 }
257bcf28 429 }
13111b2a 430
4d37f425 431
fc695138
DS
432 image->SetOption(wxIMAGE_OPTION_TIFF_PHOTOMETRIC, photometric);
433
096c930c 434 uint16 spp, bps, compression;
4d37f425
DS
435 /*
436 Read some baseline TIFF tags which helps when re-saving a TIFF
437 to be similar to the original image.
438 */
439 if ( TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &spp) )
440 {
d65172db 441 image->SetOption(wxIMAGE_OPTION_TIFF_SAMPLESPERPIXEL, spp);
4d37f425
DS
442 }
443
096c930c 444 if ( TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bps) )
4d37f425 445 {
096c930c 446 image->SetOption(wxIMAGE_OPTION_TIFF_BITSPERSAMPLE, bps);
4d37f425
DS
447 }
448
449 if ( TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &compression) )
450 {
d65172db 451 image->SetOption(wxIMAGE_OPTION_TIFF_COMPRESSION, compression);
4d37f425
DS
452 }
453
454 // Set the resolution unit.
455 wxImageResolution resUnit = wxIMAGE_RESOLUTION_NONE;
37ba70a5 456 uint16 tiffRes;
4d37f425 457 if ( TIFFGetFieldDefaulted(tif, TIFFTAG_RESOLUTIONUNIT, &tiffRes) )
37ba70a5 458 {
4d37f425 459 switch (tiffRes)
37ba70a5
VZ
460 {
461 default:
462 wxLogWarning(_("Unknown TIFF resolution unit %d ignored"),
4d37f425 463 tiffRes);
37ba70a5
VZ
464 // fall through
465
466 case RESUNIT_NONE:
4d37f425 467 resUnit = wxIMAGE_RESOLUTION_NONE;
37ba70a5
VZ
468 break;
469
470 case RESUNIT_INCH:
4d37f425 471 resUnit = wxIMAGE_RESOLUTION_INCHES;
37ba70a5
VZ
472 break;
473
474 case RESUNIT_CENTIMETER:
4d37f425 475 resUnit = wxIMAGE_RESOLUTION_CM;
37ba70a5
VZ
476 break;
477 }
4d37f425 478 }
37ba70a5 479
4d37f425 480 image->SetOption(wxIMAGE_OPTION_RESOLUTIONUNIT, resUnit);
37ba70a5 481
4d37f425
DS
482 /*
483 Set the image resolution if it's available. Resolution tag is not
484 dependant on RESOLUTIONUNIT != RESUNIT_NONE (according to TIFF spec).
485 */
486 float resX, resY;
487
488 if ( TIFFGetField(tif, TIFFTAG_XRESOLUTION, &resX) )
489 {
490 /*
491 Use a string value to not lose precision.
492 rounding to int as cm and then converting to inch may
493 result in whole integer rounding error, eg. 201 instead of 200 dpi.
494 If an app wants an int, GetOptionInt will convert and round down.
495 */
496 image->SetOption(wxIMAGE_OPTION_RESOLUTIONX,
497 wxString::FromCDouble((double) resX));
37ba70a5
VZ
498 }
499
4d37f425
DS
500 if ( TIFFGetField(tif, TIFFTAG_YRESOLUTION, &resY) )
501 {
502 image->SetOption(wxIMAGE_OPTION_RESOLUTIONY,
503 wxString::FromCDouble((double) resY));
504 }
37ba70a5 505
257bcf28 506 _TIFFfree( raster );
13111b2a 507
257bcf28 508 TIFFClose( tif );
13111b2a 509
7beb59f3 510 return true;
257bcf28
RR
511}
512
8faef7cc 513int wxTIFFHandler::DoGetImageCount( wxInputStream& stream )
700ec454
RR
514{
515 TIFF *tif = TIFFwxOpen( stream, "image", "r" );
13111b2a 516
700ec454 517 if (!tif)
13111b2a 518 return 0;
257bcf28 519
700ec454
RR
520 int dircount = 0; // according to the libtiff docs, dircount should be set to 1 here???
521 do {
522 dircount++;
523 } while (TIFFReadDirectory(tif));
13111b2a 524
700ec454 525 TIFFClose( tif );
03647350 526
8faef7cc
FM
527 // NOTE: this function modifies the current stream position but it's ok
528 // (see wxImageHandler::GetImageCount)
13111b2a 529
700ec454
RR
530 return dircount;
531}
257bcf28 532
f6bcfd97 533bool wxTIFFHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbose )
257bcf28 534{
f6bcfd97
BP
535 TIFF *tif = TIFFwxOpen( stream, "image", "w" );
536
537 if (!tif)
538 {
539 if (verbose)
af588446 540 {
f6bcfd97 541 wxLogError( _("TIFF: Error saving image.") );
af588446 542 }
f6bcfd97 543
7beb59f3 544 return false;
f6bcfd97
BP
545 }
546
547 TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, (uint32)image->GetWidth());
548 TIFFSetField(tif, TIFFTAG_IMAGELENGTH, (uint32)image->GetHeight());
549 TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
f6bcfd97 550 TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
38755449 551
37ba70a5
VZ
552 // save the image resolution if we have it
553 int xres, yres;
554 const wxImageResolution res = GetResolutionFromOptions(*image, &xres, &yres);
555 uint16 tiffRes;
556 switch ( res )
fe9308c6 557 {
37ba70a5 558 default:
9a83f860 559 wxFAIL_MSG( wxT("unknown image resolution units") );
37ba70a5
VZ
560 // fall through
561
562 case wxIMAGE_RESOLUTION_NONE:
563 tiffRes = RESUNIT_NONE;
564 break;
565
566 case wxIMAGE_RESOLUTION_INCHES:
567 tiffRes = RESUNIT_INCH;
568 break;
569
570 case wxIMAGE_RESOLUTION_CM:
571 tiffRes = RESUNIT_CENTIMETER;
572 break;
fe9308c6
VZ
573 }
574
37ba70a5
VZ
575 if ( tiffRes != RESUNIT_NONE )
576 {
577 TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, tiffRes);
578 TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float)xres);
579 TIFFSetField(tif, TIFFTAG_YRESOLUTION, (float)yres);
580 }
581
582
d65172db 583 int spp = image->GetOptionInt(wxIMAGE_OPTION_TIFF_SAMPLESPERPIXEL);
fe9308c6
VZ
584 if ( !spp )
585 spp = 3;
586
096c930c
DS
587 int bps = image->GetOptionInt(wxIMAGE_OPTION_TIFF_BITSPERSAMPLE);
588 if ( !bps )
0e11090e 589 {
096c930c 590 bps = 8;
0e11090e
DS
591 }
592 else if (bps == 1)
593 {
594 // One bit per sample combined with 3 samples per pixel is
595 // not allowed and crashes libtiff.
596 spp = 1;
597 }
fe9308c6 598
fc695138
DS
599 int photometric = PHOTOMETRIC_RGB;
600
601 if ( image->HasOption(wxIMAGE_OPTION_TIFF_PHOTOMETRIC) )
602 {
603 photometric = image->GetOptionInt(wxIMAGE_OPTION_TIFF_PHOTOMETRIC);
604 if (photometric == PHOTOMETRIC_MINISWHITE
605 || photometric == PHOTOMETRIC_MINISBLACK)
606 {
607 // either b/w or greyscale
608 spp = 1;
609 }
610 }
611 else if (spp == 1)
612 {
2453ceee 613 photometric = PHOTOMETRIC_MINISWHITE;
fc695138
DS
614 }
615
616 const bool isColouredImage = (spp > 1)
617 && (photometric != PHOTOMETRIC_MINISWHITE)
618 && (photometric != PHOTOMETRIC_MINISBLACK);
619
d65172db 620 int compression = image->GetOptionInt(wxIMAGE_OPTION_TIFF_COMPRESSION);
fe9308c6 621 if ( !compression )
f5e20985
VZ
622 {
623 // we can't use COMPRESSION_LZW because current version of libtiff
624 // doesn't implement it ("no longer implemented due to Unisys patent
625 // enforcement") and other compression methods are lossy so we
626 // shouldn't use them by default -- and the only remaining one is none
627 compression = COMPRESSION_NONE;
628 }
fe9308c6
VZ
629
630 TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, spp);
096c930c 631 TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bps);
fc695138 632 TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, photometric);
fe9308c6
VZ
633 TIFFSetField(tif, TIFFTAG_COMPRESSION, compression);
634
649df4bf
DS
635 // scanlinesize is determined by spp and bps
636 const tsize_t linebytes =
637 (tsize_t)((image->GetWidth() * spp * bps + 7) / 8);
fe9308c6 638
f6bcfd97 639 unsigned char *buf;
38755449 640
fc695138 641 if (TIFFScanlineSize(tif) > linebytes || !isColouredImage)
f6bcfd97
BP
642 {
643 buf = (unsigned char *)_TIFFmalloc(TIFFScanlineSize(tif));
644 if (!buf)
645 {
646 if (verbose)
af588446 647 {
f6bcfd97 648 wxLogError( _("TIFF: Couldn't allocate memory.") );
af588446 649 }
f6bcfd97
BP
650
651 TIFFClose( tif );
652
7beb59f3 653 return false;
f6bcfd97 654 }
38755449
DW
655 }
656 else
f6bcfd97
BP
657 {
658 buf = NULL;
659 }
660
fe9308c6
VZ
661 TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP,TIFFDefaultStripSize(tif, (uint32) -1));
662
f2de33ed
DS
663 const int bitsPerPixel = spp * bps;
664 const int pixelsPerByte = 8 / bitsPerPixel;
665 int remainingPixelCount = 0;
666
667 if (pixelsPerByte)
668 {
669 // How many pixels to write in the last byte column?
670 remainingPixelCount = image->GetWidth() % pixelsPerByte;
671 if (!remainingPixelCount) remainingPixelCount = 8;
672 }
673
fc695138 674 const bool minIsWhite = (photometric == PHOTOMETRIC_MINISWHITE);
f6bcfd97 675 unsigned char *ptr = image->GetData();
fe9308c6 676 for ( int row = 0; row < image->GetHeight(); row++ )
f6bcfd97 677 {
fe9308c6
VZ
678 if ( buf )
679 {
fc695138 680 if (isColouredImage)
fe9308c6
VZ
681 {
682 // color image
683 memcpy(buf, ptr, image->GetWidth());
684 }
8e28cc47
DS
685 else if (spp * bps == 8) // greyscale image
686 {
687 for ( int column = 0; column < linebytes; column++ )
688 {
fc695138
DS
689 uint8 value = ptr[column*3 + 1];
690 if (minIsWhite)
691 {
692 value = 255 - value;
693 }
694
695 buf[column] = value;
8e28cc47
DS
696 }
697 }
fe9308c6
VZ
698 else // black and white image
699 {
700 for ( int column = 0; column < linebytes; column++ )
701 {
702 uint8 reverse = 0;
f2de33ed
DS
703 int pixelsPerByteCount = (column + 1 != linebytes)
704 ? pixelsPerByte
705 : remainingPixelCount;
706 for ( int bp = 0; bp < pixelsPerByteCount; bp++ )
fe9308c6 707 {
fc695138 708 if ( (ptr[column*24 + bp*3 + 1] <=127) == minIsWhite )
fe9308c6 709 {
7f77b25e 710 // check only green as this is sufficient
38d4b1e4 711 reverse = (uint8)(reverse | 128 >> bp);
fe9308c6 712 }
fe9308c6
VZ
713 }
714
3ba9ea72 715 buf[column] = reverse;
fe9308c6
VZ
716 }
717 }
718 }
38755449 719
fe9308c6 720 if ( TIFFWriteScanline(tif, buf ? buf : ptr, (uint32)row, 0) < 0 )
f6bcfd97 721 {
19193a2c 722 if (verbose)
af588446 723 {
19193a2c 724 wxLogError( _("TIFF: Error writing image.") );
af588446 725 }
38755449 726
f6bcfd97
BP
727 TIFFClose( tif );
728 if (buf)
729 _TIFFfree(buf);
38755449 730
7beb59f3 731 return false;
f6bcfd97 732 }
fe9308c6 733
f6bcfd97
BP
734 ptr += image->GetWidth()*3;
735 }
736
737 (void) TIFFClose(tif);
738
739 if (buf)
fe9308c6 740 _TIFFfree(buf);
f6bcfd97 741
7beb59f3 742 return true;
257bcf28
RR
743}
744
745bool wxTIFFHandler::DoCanRead( wxInputStream& stream )
746{
0b72db08 747 unsigned char hdr[2];
257bcf28 748
8faef7cc 749 if ( !stream.Read(&hdr[0], WXSIZEOF(hdr)) ) // it's ok to modify the stream position here
7beb59f3 750 return false;
79fa2374 751
79fa2374
VZ
752 return (hdr[0] == 'I' && hdr[1] == 'I') ||
753 (hdr[0] == 'M' && hdr[1] == 'M');
257bcf28
RR
754}
755
e30285ab 756#endif // wxUSE_STREAMS
257bcf28 757
ccec9093
VZ
758/*static*/ wxVersionInfo wxTIFFHandler::GetLibraryVersionInfo()
759{
760 int major,
761 minor,
762 micro;
763
764 const wxString ver(::TIFFGetVersion());
9c1f960f 765 if ( wxSscanf(ver, "LIBTIFF, Version %d.%d.%d", &major, &minor, &micro) != 3 )
ccec9093
VZ
766 {
767 wxLogDebug("Unrecognized libtiff version string \"%s\"", ver);
768
769 major =
770 minor =
771 micro = 0;
772 }
773
774 wxString copyright;
775 const wxString desc = ver.BeforeFirst('\n', &copyright);
776 copyright.Replace("\n", "");
777
778 return wxVersionInfo("libtiff", major, minor, micro, desc, copyright);
779}
780
e30285ab 781#endif // wxUSE_LIBTIFF