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