]> git.saurik.com Git - wxWidgets.git/blame - src/common/imagtiff.cpp
Dramatically optimise inserting many items in wxGenericListCtrl.
[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
ebbaec82
DS
333 uint16 samplesPerPixel = 0;
334 (void) TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &samplesPerPixel);
335
336 uint16 bitsPerSample = 0;
337 (void) TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bitsPerSample);
338
b6ac40dc
VS
339 uint16 extraSamples;
340 uint16* samplesInfo;
341 TIFFGetFieldDefaulted(tif, TIFFTAG_EXTRASAMPLES,
342 &extraSamples, &samplesInfo);
ebbaec82
DS
343
344 uint16 photometric;
3d59540f
DS
345 if (!TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric))
346 {
347 photometric = PHOTOMETRIC_MINISWHITE;
348 }
349 const bool hasAlpha = (extraSamples >= 1
ebbaec82 350 && ((samplesInfo[0] == EXTRASAMPLE_UNSPECIFIED)
3d59540f
DS
351 || samplesInfo[0] == EXTRASAMPLE_ASSOCALPHA
352 || samplesInfo[0] == EXTRASAMPLE_UNASSALPHA))
353 || (extraSamples == 0 && samplesPerPixel == 4
354 && photometric == PHOTOMETRIC_RGB);
b6ac40dc 355
5beedebb
VZ
356 // guard against integer overflow during multiplication which could result
357 // in allocating a too small buffer and then overflowing it
ca588225 358 const double bytesNeeded = (double)w * (double)h * sizeof(uint32);
5beedebb
VZ
359 if ( bytesNeeded >= wxUINT32_MAX )
360 {
361 if ( verbose )
af588446 362 {
5beedebb 363 wxLogError( _("TIFF: Image size is abnormally big.") );
af588446 364 }
5beedebb
VZ
365
366 TIFFClose(tif);
367
368 return false;
369 }
13111b2a 370
8f2a8de6 371 raster = (uint32*) _TIFFmalloc( (uint32)bytesNeeded );
13111b2a 372
257bcf28
RR
373 if (!raster)
374 {
375 if (verbose)
af588446 376 {
58c837a4 377 wxLogError( _("TIFF: Couldn't allocate memory.") );
af588446 378 }
38755449 379
f6bcfd97 380 TIFFClose( tif );
13111b2a 381
7beb59f3 382 return false;
257bcf28
RR
383 }
384
479cd5de 385 image->Create( (int)w, (int)h );
a1b806b9 386 if (!image->IsOk())
257bcf28
RR
387 {
388 if (verbose)
af588446 389 {
58c837a4 390 wxLogError( _("TIFF: Couldn't allocate memory.") );
af588446 391 }
13111b2a
VZ
392
393 _TIFFfree( raster );
f6bcfd97 394 TIFFClose( tif );
13111b2a 395
7beb59f3 396 return false;
257bcf28 397 }
13111b2a 398
b6ac40dc
VS
399 if ( hasAlpha )
400 image->SetAlpha();
401
f60e3134
DS
402 uint16 planarConfig = PLANARCONFIG_CONTIG;
403 (void) TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &planarConfig);
404
405 bool ok = true;
406 char msg[1024] = "";
ebbaec82
DS
407 if
408 (
409 (planarConfig == PLANARCONFIG_CONTIG && samplesPerPixel == 2
410 && extraSamples == 1)
411 &&
412 (
413 ( !TIFFRGBAImageOK(tif, msg) )
414 || (bitsPerSample == 8)
415 )
416 )
f60e3134 417 {
ebbaec82 418 const bool isGreyScale = (bitsPerSample == 8);
f60e3134
DS
419 unsigned char *buf = (unsigned char *)_TIFFmalloc(TIFFScanlineSize(tif));
420 uint32 pos = 0;
ebbaec82
DS
421 const bool minIsWhite = (photometric == PHOTOMETRIC_MINISWHITE);
422 const int minValue = minIsWhite ? 255 : 0;
f60e3134
DS
423 const int maxValue = 255 - minValue;
424
425 /*
426 Decode to ABGR format as that is what the code, that converts to
427 wxImage, later on expects (normally TIFFReadRGBAImageOriented is
428 used to decode which uses an ABGR layout).
429 */
430 for (uint32 y = 0; y < h; ++y)
431 {
432 if (TIFFReadScanline(tif, buf, y, 0) != 1)
433 {
434 ok = false;
435 break;
436 }
437
ebbaec82 438 if (isGreyScale)
f60e3134 439 {
ebbaec82
DS
440 for (uint32 x = 0; x < w; ++x)
441 {
442 uint8 val = minIsWhite ? 255 - buf[x*2] : buf[x*2];
443 uint8 alpha = minIsWhite ? 255 - buf[x*2+1] : buf[x*2+1];
444 raster[pos] = val + (val << 8) + (val << 16)
445 + (alpha << 24);
446 pos++;
447 }
448 }
449 else
450 {
451 for (uint32 x = 0; x < w; ++x)
452 {
453 int mask = buf[x*2/8] << ((x*2)%8);
454
455 uint8 val = mask & 128 ? maxValue : minValue;
456 raster[pos] = val + (val << 8) + (val << 16)
457 + ((mask & 64 ? maxValue : minValue) << 24);
458 pos++;
459 }
f60e3134
DS
460 }
461 }
462
463 _TIFFfree(buf);
464 }
465 else
466 {
467 ok = TIFFReadRGBAImageOriented( tif, w, h, raster,
468 ORIENTATION_TOPLEFT, 0 ) != 0;
469 }
470
471
472 if (!ok)
257bcf28
RR
473 {
474 if (verbose)
af588446 475 {
58c837a4 476 wxLogError( _("TIFF: Error reading image.") );
af588446 477 }
13111b2a
VZ
478
479 _TIFFfree( raster );
480 image->Destroy();
f6bcfd97 481 TIFFClose( tif );
13111b2a 482
7beb59f3 483 return false;
257bcf28 484 }
13111b2a 485
257bcf28 486 unsigned char *ptr = image->GetData();
b6ac40dc 487
c6973bab 488 unsigned char *alpha = image->GetAlpha();
b6ac40dc 489
257bcf28 490 uint32 pos = 0;
13111b2a 491
257bcf28
RR
492 for (uint32 i = 0; i < h; i++)
493 {
ff7c6c9c 494 for (uint32 j = 0; j < w; j++)
13111b2a 495 {
b6ac40dc
VS
496 *(ptr++) = (unsigned char)TIFFGetR(raster[pos]);
497 *(ptr++) = (unsigned char)TIFFGetG(raster[pos]);
498 *(ptr++) = (unsigned char)TIFFGetB(raster[pos]);
499 if ( hasAlpha )
500 *(alpha++) = (unsigned char)TIFFGetA(raster[pos]);
501
13111b2a
VZ
502 pos++;
503 }
257bcf28 504 }
13111b2a 505
4d37f425 506
fc695138
DS
507 image->SetOption(wxIMAGE_OPTION_TIFF_PHOTOMETRIC, photometric);
508
ebbaec82 509 uint16 compression;
4d37f425 510 /*
ebbaec82 511 Copy some baseline TIFF tags which helps when re-saving a TIFF
4d37f425
DS
512 to be similar to the original image.
513 */
ebbaec82 514 if (samplesPerPixel)
4d37f425 515 {
ebbaec82 516 image->SetOption(wxIMAGE_OPTION_TIFF_SAMPLESPERPIXEL, samplesPerPixel);
4d37f425
DS
517 }
518
ebbaec82 519 if (bitsPerSample)
4d37f425 520 {
ebbaec82 521 image->SetOption(wxIMAGE_OPTION_TIFF_BITSPERSAMPLE, bitsPerSample);
4d37f425
DS
522 }
523
524 if ( TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &compression) )
525 {
d65172db 526 image->SetOption(wxIMAGE_OPTION_TIFF_COMPRESSION, compression);
4d37f425
DS
527 }
528
529 // Set the resolution unit.
530 wxImageResolution resUnit = wxIMAGE_RESOLUTION_NONE;
37ba70a5 531 uint16 tiffRes;
4d37f425 532 if ( TIFFGetFieldDefaulted(tif, TIFFTAG_RESOLUTIONUNIT, &tiffRes) )
37ba70a5 533 {
4d37f425 534 switch (tiffRes)
37ba70a5
VZ
535 {
536 default:
537 wxLogWarning(_("Unknown TIFF resolution unit %d ignored"),
4d37f425 538 tiffRes);
37ba70a5
VZ
539 // fall through
540
541 case RESUNIT_NONE:
4d37f425 542 resUnit = wxIMAGE_RESOLUTION_NONE;
37ba70a5
VZ
543 break;
544
545 case RESUNIT_INCH:
4d37f425 546 resUnit = wxIMAGE_RESOLUTION_INCHES;
37ba70a5
VZ
547 break;
548
549 case RESUNIT_CENTIMETER:
4d37f425 550 resUnit = wxIMAGE_RESOLUTION_CM;
37ba70a5
VZ
551 break;
552 }
4d37f425 553 }
37ba70a5 554
4d37f425 555 image->SetOption(wxIMAGE_OPTION_RESOLUTIONUNIT, resUnit);
37ba70a5 556
4d37f425
DS
557 /*
558 Set the image resolution if it's available. Resolution tag is not
559 dependant on RESOLUTIONUNIT != RESUNIT_NONE (according to TIFF spec).
560 */
561 float resX, resY;
562
563 if ( TIFFGetField(tif, TIFFTAG_XRESOLUTION, &resX) )
564 {
565 /*
566 Use a string value to not lose precision.
567 rounding to int as cm and then converting to inch may
568 result in whole integer rounding error, eg. 201 instead of 200 dpi.
569 If an app wants an int, GetOptionInt will convert and round down.
570 */
571 image->SetOption(wxIMAGE_OPTION_RESOLUTIONX,
572 wxString::FromCDouble((double) resX));
37ba70a5
VZ
573 }
574
4d37f425
DS
575 if ( TIFFGetField(tif, TIFFTAG_YRESOLUTION, &resY) )
576 {
577 image->SetOption(wxIMAGE_OPTION_RESOLUTIONY,
578 wxString::FromCDouble((double) resY));
579 }
37ba70a5 580
257bcf28 581 _TIFFfree( raster );
13111b2a 582
257bcf28 583 TIFFClose( tif );
13111b2a 584
7beb59f3 585 return true;
257bcf28
RR
586}
587
8faef7cc 588int wxTIFFHandler::DoGetImageCount( wxInputStream& stream )
700ec454
RR
589{
590 TIFF *tif = TIFFwxOpen( stream, "image", "r" );
13111b2a 591
700ec454 592 if (!tif)
13111b2a 593 return 0;
257bcf28 594
700ec454
RR
595 int dircount = 0; // according to the libtiff docs, dircount should be set to 1 here???
596 do {
597 dircount++;
598 } while (TIFFReadDirectory(tif));
13111b2a 599
700ec454 600 TIFFClose( tif );
03647350 601
8faef7cc
FM
602 // NOTE: this function modifies the current stream position but it's ok
603 // (see wxImageHandler::GetImageCount)
13111b2a 604
700ec454
RR
605 return dircount;
606}
257bcf28 607
f6bcfd97 608bool wxTIFFHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbose )
257bcf28 609{
f6bcfd97
BP
610 TIFF *tif = TIFFwxOpen( stream, "image", "w" );
611
612 if (!tif)
613 {
614 if (verbose)
af588446 615 {
f6bcfd97 616 wxLogError( _("TIFF: Error saving image.") );
af588446 617 }
f6bcfd97 618
7beb59f3 619 return false;
f6bcfd97
BP
620 }
621
ab176b4b
DS
622 const int imageWidth = image->GetWidth();
623 TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, (uint32) imageWidth);
f6bcfd97
BP
624 TIFFSetField(tif, TIFFTAG_IMAGELENGTH, (uint32)image->GetHeight());
625 TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
f6bcfd97 626 TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
38755449 627
37ba70a5
VZ
628 // save the image resolution if we have it
629 int xres, yres;
630 const wxImageResolution res = GetResolutionFromOptions(*image, &xres, &yres);
631 uint16 tiffRes;
632 switch ( res )
fe9308c6 633 {
37ba70a5 634 default:
9a83f860 635 wxFAIL_MSG( wxT("unknown image resolution units") );
37ba70a5
VZ
636 // fall through
637
638 case wxIMAGE_RESOLUTION_NONE:
639 tiffRes = RESUNIT_NONE;
640 break;
641
642 case wxIMAGE_RESOLUTION_INCHES:
643 tiffRes = RESUNIT_INCH;
644 break;
645
646 case wxIMAGE_RESOLUTION_CM:
647 tiffRes = RESUNIT_CENTIMETER;
648 break;
fe9308c6
VZ
649 }
650
37ba70a5
VZ
651 if ( tiffRes != RESUNIT_NONE )
652 {
653 TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, tiffRes);
654 TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float)xres);
655 TIFFSetField(tif, TIFFTAG_YRESOLUTION, (float)yres);
656 }
657
658
d65172db 659 int spp = image->GetOptionInt(wxIMAGE_OPTION_TIFF_SAMPLESPERPIXEL);
fe9308c6
VZ
660 if ( !spp )
661 spp = 3;
662
096c930c
DS
663 int bps = image->GetOptionInt(wxIMAGE_OPTION_TIFF_BITSPERSAMPLE);
664 if ( !bps )
0e11090e 665 {
096c930c 666 bps = 8;
0e11090e
DS
667 }
668 else if (bps == 1)
669 {
670 // One bit per sample combined with 3 samples per pixel is
671 // not allowed and crashes libtiff.
672 spp = 1;
673 }
fe9308c6 674
fc695138
DS
675 int photometric = PHOTOMETRIC_RGB;
676
677 if ( image->HasOption(wxIMAGE_OPTION_TIFF_PHOTOMETRIC) )
678 {
679 photometric = image->GetOptionInt(wxIMAGE_OPTION_TIFF_PHOTOMETRIC);
680 if (photometric == PHOTOMETRIC_MINISWHITE
681 || photometric == PHOTOMETRIC_MINISBLACK)
682 {
683 // either b/w or greyscale
684 spp = 1;
685 }
686 }
ab176b4b 687 else if (spp <= 2)
fc695138 688 {
2453ceee 689 photometric = PHOTOMETRIC_MINISWHITE;
fc695138
DS
690 }
691
ab176b4b 692 const bool hasAlpha = image->HasAlpha();
fc695138 693
d65172db 694 int compression = image->GetOptionInt(wxIMAGE_OPTION_TIFF_COMPRESSION);
ab176b4b 695 if ( !compression || (compression == COMPRESSION_JPEG && hasAlpha) )
f5e20985 696 {
ab176b4b 697 // We can't use COMPRESSION_LZW because current version of libtiff
f5e20985
VZ
698 // doesn't implement it ("no longer implemented due to Unisys patent
699 // enforcement") and other compression methods are lossy so we
ab176b4b
DS
700 // shouldn't use them by default -- and the only remaining one is none.
701 // Also JPEG compression for alpha images is not a good idea (viewers
702 // not opening the image properly).
f5e20985
VZ
703 compression = COMPRESSION_NONE;
704 }
fe9308c6 705
ab176b4b
DS
706 if
707 (
708 (photometric == PHOTOMETRIC_RGB && spp == 4)
709 || (photometric <= PHOTOMETRIC_MINISBLACK && spp == 2)
710 )
711 {
712 // Compensate for user passing a SamplesPerPixel that includes
713 // the alpha channel.
714 spp--;
715 }
716
717
718 int extraSamples = hasAlpha ? 1 : 0;
719
720 TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, spp + extraSamples);
096c930c 721 TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bps);
fc695138 722 TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, photometric);
fe9308c6
VZ
723 TIFFSetField(tif, TIFFTAG_COMPRESSION, compression);
724
ab176b4b
DS
725 if (extraSamples)
726 {
727 uint16 extra[] = { EXTRASAMPLE_UNSPECIFIED };
728 TIFFSetField(tif, TIFFTAG_EXTRASAMPLES, (long) 1, &extra);
729 }
730
731 // scanlinesize is determined by spp+extraSamples and bps
649df4bf 732 const tsize_t linebytes =
ab176b4b 733 (tsize_t)((imageWidth * (spp + extraSamples) * bps + 7) / 8);
fe9308c6 734
f6bcfd97 735 unsigned char *buf;
38755449 736
ab176b4b
DS
737 const bool isColouredImage = (spp > 1)
738 && (photometric != PHOTOMETRIC_MINISWHITE)
739 && (photometric != PHOTOMETRIC_MINISBLACK);
740
741
742 if (TIFFScanlineSize(tif) > linebytes || !isColouredImage || hasAlpha)
f6bcfd97
BP
743 {
744 buf = (unsigned char *)_TIFFmalloc(TIFFScanlineSize(tif));
745 if (!buf)
746 {
747 if (verbose)
af588446 748 {
f6bcfd97 749 wxLogError( _("TIFF: Couldn't allocate memory.") );
af588446 750 }
f6bcfd97
BP
751
752 TIFFClose( tif );
753
7beb59f3 754 return false;
f6bcfd97 755 }
38755449
DW
756 }
757 else
f6bcfd97
BP
758 {
759 buf = NULL;
760 }
761
fe9308c6
VZ
762 TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP,TIFFDefaultStripSize(tif, (uint32) -1));
763
ab176b4b
DS
764 const int bitsPerPixel = (spp + extraSamples) * bps;
765 const int bytesPerPixel = (bitsPerPixel + 7) / 8;
f2de33ed
DS
766 const int pixelsPerByte = 8 / bitsPerPixel;
767 int remainingPixelCount = 0;
768
769 if (pixelsPerByte)
770 {
771 // How many pixels to write in the last byte column?
ab176b4b
DS
772 remainingPixelCount = imageWidth % pixelsPerByte;
773 if (!remainingPixelCount) remainingPixelCount = pixelsPerByte;
f2de33ed
DS
774 }
775
fc695138 776 const bool minIsWhite = (photometric == PHOTOMETRIC_MINISWHITE);
f6bcfd97 777 unsigned char *ptr = image->GetData();
fe9308c6 778 for ( int row = 0; row < image->GetHeight(); row++ )
f6bcfd97 779 {
fe9308c6
VZ
780 if ( buf )
781 {
fc695138 782 if (isColouredImage)
fe9308c6 783 {
ab176b4b
DS
784 // colour image
785 if (hasAlpha)
786 {
787 for ( int column = 0; column < imageWidth; column++ )
788 {
789 buf[column*4 ] = ptr[column*3 ];
790 buf[column*4 + 1] = ptr[column*3 + 1];
791 buf[column*4 + 2] = ptr[column*3 + 2];
792 buf[column*4 + 3] = image->GetAlpha(column, row);
793 }
794 }
795 else
796 {
797 memcpy(buf, ptr, imageWidth * 3);
798 }
fe9308c6 799 }
8e28cc47
DS
800 else if (spp * bps == 8) // greyscale image
801 {
ab176b4b 802 for ( int column = 0; column < imageWidth; column++ )
8e28cc47 803 {
fc695138
DS
804 uint8 value = ptr[column*3 + 1];
805 if (minIsWhite)
806 {
807 value = 255 - value;
808 }
809
ab176b4b
DS
810 buf[column * bytesPerPixel] = value;
811
812 if (hasAlpha)
813 {
814 value = image->GetAlpha(column, row);
815 buf[column*bytesPerPixel+1]
816 = minIsWhite ? 255 - value : value;
817 }
8e28cc47
DS
818 }
819 }
fe9308c6
VZ
820 else // black and white image
821 {
822 for ( int column = 0; column < linebytes; column++ )
823 {
824 uint8 reverse = 0;
f2de33ed
DS
825 int pixelsPerByteCount = (column + 1 != linebytes)
826 ? pixelsPerByte
827 : remainingPixelCount;
828 for ( int bp = 0; bp < pixelsPerByteCount; bp++ )
fe9308c6 829 {
ab176b4b
DS
830 if ( (ptr[column * 3 * pixelsPerByte + bp*3 + 1] <=127)
831 == minIsWhite )
fe9308c6 832 {
7f77b25e 833 // check only green as this is sufficient
ab176b4b
DS
834 reverse |= (uint8) (128 >> (bp * bitsPerPixel));
835 }
836
837 if (hasAlpha
838 && (image->GetAlpha(column * pixelsPerByte + bp,
839 row) <= 127) == minIsWhite)
840 {
841 reverse |= (uint8) (64 >> (bp * bitsPerPixel));
fe9308c6 842 }
fe9308c6
VZ
843 }
844
3ba9ea72 845 buf[column] = reverse;
fe9308c6
VZ
846 }
847 }
848 }
38755449 849
fe9308c6 850 if ( TIFFWriteScanline(tif, buf ? buf : ptr, (uint32)row, 0) < 0 )
f6bcfd97 851 {
19193a2c 852 if (verbose)
af588446 853 {
19193a2c 854 wxLogError( _("TIFF: Error writing image.") );
af588446 855 }
38755449 856
f6bcfd97
BP
857 TIFFClose( tif );
858 if (buf)
859 _TIFFfree(buf);
38755449 860
7beb59f3 861 return false;
f6bcfd97 862 }
fe9308c6 863
ab176b4b 864 ptr += imageWidth * 3;
f6bcfd97
BP
865 }
866
867 (void) TIFFClose(tif);
868
869 if (buf)
fe9308c6 870 _TIFFfree(buf);
f6bcfd97 871
7beb59f3 872 return true;
257bcf28
RR
873}
874
875bool wxTIFFHandler::DoCanRead( wxInputStream& stream )
876{
0b72db08 877 unsigned char hdr[2];
257bcf28 878
8faef7cc 879 if ( !stream.Read(&hdr[0], WXSIZEOF(hdr)) ) // it's ok to modify the stream position here
7beb59f3 880 return false;
79fa2374 881
79fa2374
VZ
882 return (hdr[0] == 'I' && hdr[1] == 'I') ||
883 (hdr[0] == 'M' && hdr[1] == 'M');
257bcf28
RR
884}
885
e30285ab 886#endif // wxUSE_STREAMS
257bcf28 887
ccec9093
VZ
888/*static*/ wxVersionInfo wxTIFFHandler::GetLibraryVersionInfo()
889{
890 int major,
891 minor,
892 micro;
893
894 const wxString ver(::TIFFGetVersion());
9c1f960f 895 if ( wxSscanf(ver, "LIBTIFF, Version %d.%d.%d", &major, &minor, &micro) != 3 )
ccec9093
VZ
896 {
897 wxLogDebug("Unrecognized libtiff version string \"%s\"", ver);
898
899 major =
900 minor =
901 micro = 0;
902 }
903
904 wxString copyright;
905 const wxString desc = ver.BeforeFirst('\n', &copyright);
906 copyright.Replace("\n", "");
907
908 return wxVersionInfo("libtiff", major, minor, micro, desc, copyright);
909}
910
e30285ab 911#endif // wxUSE_LIBTIFF