Allow handling EVT_UPDATE_UI for wxID_UNDO/REDO at wxDocument level.
[wxWidgets.git] / src / common / imagtiff.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/imagtiff.cpp
3 // Purpose: wxImage TIFF handler
4 // Author: Robert Roebling
5 // RCS-ID: $Id$
6 // Copyright: (c) Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 // ============================================================================
11 // declarations
12 // ============================================================================
13
14 // ----------------------------------------------------------------------------
15 // headers
16 // ----------------------------------------------------------------------------
17
18 // For compilers that support precompilation, includes "wx.h".
19 #include "wx/wxprec.h"
20
21 #ifdef __BORLANDC__
22 #pragma hdrstop
23 #endif
24
25 #if wxUSE_IMAGE && wxUSE_LIBTIFF
26
27 #include "wx/imagtiff.h"
28 #include "wx/versioninfo.h"
29
30 #ifndef WX_PRECOMP
31 #include "wx/log.h"
32 #include "wx/app.h"
33 #include "wx/intl.h"
34 #include "wx/bitmap.h"
35 #include "wx/module.h"
36 #include "wx/wxcrtvararg.h"
37 #endif
38
39 extern "C"
40 {
41 #ifdef __DMC__
42 #include "tif_config.h"
43 #endif
44 #include "tiff.h"
45 #include "tiffio.h"
46 }
47 #include "wx/filefn.h"
48 #include "wx/wfstream.h"
49
50 #ifndef TIFFLINKAGEMODE
51 #define TIFFLINKAGEMODE LINKAGEMODE
52 #endif
53
54 // ============================================================================
55 // implementation
56 // ============================================================================
57
58 // ----------------------------------------------------------------------------
59 // TIFF library error/warning handlers
60 // ----------------------------------------------------------------------------
61
62 static wxString
63 FormatTiffMessage(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
81 extern "C"
82 {
83
84 static void
85 TIFFwxWarningHandler(const char* module, const char *fmt, va_list ap)
86 {
87 wxLogWarning("%s", FormatTiffMessage(module, fmt, ap));
88 }
89
90 static void
91 TIFFwxErrorHandler(const char* module, const char *fmt, va_list ap)
92 {
93 wxLogError("%s", FormatTiffMessage(module, fmt, ap));
94 }
95
96 } // extern "C"
97
98 //-----------------------------------------------------------------------------
99 // wxTIFFHandler
100 //-----------------------------------------------------------------------------
101
102 IMPLEMENT_DYNAMIC_CLASS(wxTIFFHandler,wxImageHandler)
103
104 wxTIFFHandler::wxTIFFHandler()
105 {
106 m_name = wxT("TIFF file");
107 m_extension = wxT("tif");
108 m_altExtensions.Add(wxT("tiff"));
109 m_type = wxBITMAP_TYPE_TIFF;
110 m_mime = wxT("image/tiff");
111 TIFFSetWarningHandler((TIFFErrorHandler) TIFFwxWarningHandler);
112 TIFFSetErrorHandler((TIFFErrorHandler) TIFFwxErrorHandler);
113 }
114
115 #if wxUSE_STREAMS
116
117 // helper to translate our, possibly 64 bit, wxFileOffset to TIFF, always 32
118 // bit, toff_t
119 static 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);
125 wxCHECK_MSG( (wxFileOffset)tofs == ofs, (toff_t)-1,
126 wxT("TIFF library doesn't support large files") );
127
128 return tofs;
129 }
130
131 // another helper to convert standard seek mode to our
132 static 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
150 extern "C"
151 {
152
153 tsize_t TIFFLINKAGEMODE
154 wxTIFFNullProc(thandle_t WXUNUSED(handle),
155 tdata_t WXUNUSED(buf),
156 tsize_t WXUNUSED(size))
157 {
158 return (tsize_t) -1;
159 }
160
161 tsize_t TIFFLINKAGEMODE
162 wxTIFFReadProc(thandle_t handle, tdata_t buf, tsize_t size)
163 {
164 wxInputStream *stream = (wxInputStream*) handle;
165 stream->Read( (void*) buf, (size_t) size );
166 return wx_truncate_cast(tsize_t, stream->LastRead());
167 }
168
169 tsize_t TIFFLINKAGEMODE
170 wxTIFFWriteProc(thandle_t handle, tdata_t buf, tsize_t size)
171 {
172 wxOutputStream *stream = (wxOutputStream*) handle;
173 stream->Write( (void*) buf, (size_t) size );
174 return wx_truncate_cast(tsize_t, stream->LastWrite());
175 }
176
177 toff_t TIFFLINKAGEMODE
178 wxTIFFSeekIProc(thandle_t handle, toff_t off, int whence)
179 {
180 wxInputStream *stream = (wxInputStream*) handle;
181
182 return wxFileOffsetToTIFF(stream->SeekI((wxFileOffset)off,
183 wxSeekModeFromTIFF(whence)));
184 }
185
186 toff_t TIFFLINKAGEMODE
187 wxTIFFSeekOProc(thandle_t handle, toff_t off, int whence)
188 {
189 wxOutputStream *stream = (wxOutputStream*) handle;
190
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() );
227 }
228
229 int TIFFLINKAGEMODE
230 wxTIFFCloseIProc(thandle_t WXUNUSED(handle))
231 {
232 // there is no need to close the input stream
233 return 0;
234 }
235
236 int TIFFLINKAGEMODE
237 wxTIFFCloseOProc(thandle_t handle)
238 {
239 wxOutputStream *stream = (wxOutputStream*) handle;
240
241 return stream->Close() ? 0 : -1;
242 }
243
244 toff_t TIFFLINKAGEMODE
245 wxTIFFSizeProc(thandle_t handle)
246 {
247 wxStreamBase *stream = (wxStreamBase*) handle;
248 return (toff_t) stream->GetSize();
249 }
250
251 int TIFFLINKAGEMODE
252 wxTIFFMapProc(thandle_t WXUNUSED(handle),
253 tdata_t* WXUNUSED(pbase),
254 toff_t* WXUNUSED(psize))
255 {
256 return 0;
257 }
258
259 void TIFFLINKAGEMODE
260 wxTIFFUnmapProc(thandle_t WXUNUSED(handle),
261 tdata_t WXUNUSED(base),
262 toff_t WXUNUSED(size))
263 {
264 }
265
266 } // extern "C"
267
268 TIFF*
269 TIFFwxOpen(wxInputStream &stream, const char* name, const char* mode)
270 {
271 TIFF* tif = TIFFClientOpen(name, mode,
272 (thandle_t) &stream,
273 wxTIFFReadProc, wxTIFFNullProc,
274 wxTIFFSeekIProc, wxTIFFCloseIProc, wxTIFFSizeProc,
275 wxTIFFMapProc, wxTIFFUnmapProc);
276
277 return tif;
278 }
279
280 TIFF*
281 TIFFwxOpen(wxOutputStream &stream, const char* name, const char* mode)
282 {
283 TIFF* tif = TIFFClientOpen(name, mode,
284 (thandle_t) &stream,
285 wxTIFFNullProc, wxTIFFWriteProc,
286 wxTIFFSeekOProc, wxTIFFCloseOProc, wxTIFFSizeProc,
287 wxTIFFMapProc, wxTIFFUnmapProc);
288
289 return tif;
290 }
291
292 bool wxTIFFHandler::LoadFile( wxImage *image, wxInputStream& stream, bool verbose, int index )
293 {
294 if (index == -1)
295 index = 0;
296
297 image->Destroy();
298
299 TIFF *tif = TIFFwxOpen( stream, "image", "r" );
300
301 if (!tif)
302 {
303 if (verbose)
304 {
305 wxLogError( _("TIFF: Error loading image.") );
306 }
307
308 return false;
309 }
310
311 if (!TIFFSetDirectory( tif, (tdir_t)index ))
312 {
313 if (verbose)
314 {
315 wxLogError( _("Invalid TIFF image index.") );
316 }
317
318 TIFFClose( tif );
319
320 return false;
321 }
322
323 uint32 w, h;
324 uint32 *raster;
325
326 TIFFGetField( tif, TIFFTAG_IMAGEWIDTH, &w );
327 TIFFGetField( tif, TIFFTAG_IMAGELENGTH, &h );
328
329 uint16 samplesPerPixel = 0;
330 (void) TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &samplesPerPixel);
331
332 uint16 bitsPerSample = 0;
333 (void) TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bitsPerSample);
334
335 uint16 extraSamples;
336 uint16* samplesInfo;
337 TIFFGetFieldDefaulted(tif, TIFFTAG_EXTRASAMPLES,
338 &extraSamples, &samplesInfo);
339
340 uint16 photometric;
341 if (!TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric))
342 {
343 photometric = PHOTOMETRIC_MINISWHITE;
344 }
345 const bool hasAlpha = (extraSamples >= 1
346 && ((samplesInfo[0] == EXTRASAMPLE_UNSPECIFIED)
347 || samplesInfo[0] == EXTRASAMPLE_ASSOCALPHA
348 || samplesInfo[0] == EXTRASAMPLE_UNASSALPHA))
349 || (extraSamples == 0 && samplesPerPixel == 4
350 && photometric == PHOTOMETRIC_RGB);
351
352 // guard against integer overflow during multiplication which could result
353 // in allocating a too small buffer and then overflowing it
354 const double bytesNeeded = (double)w * (double)h * sizeof(uint32);
355 if ( bytesNeeded >= wxUINT32_MAX )
356 {
357 if ( verbose )
358 {
359 wxLogError( _("TIFF: Image size is abnormally big.") );
360 }
361
362 TIFFClose(tif);
363
364 return false;
365 }
366
367 raster = (uint32*) _TIFFmalloc( (uint32)bytesNeeded );
368
369 if (!raster)
370 {
371 if (verbose)
372 {
373 wxLogError( _("TIFF: Couldn't allocate memory.") );
374 }
375
376 TIFFClose( tif );
377
378 return false;
379 }
380
381 image->Create( (int)w, (int)h );
382 if (!image->IsOk())
383 {
384 if (verbose)
385 {
386 wxLogError( _("TIFF: Couldn't allocate memory.") );
387 }
388
389 _TIFFfree( raster );
390 TIFFClose( tif );
391
392 return false;
393 }
394
395 if ( hasAlpha )
396 image->SetAlpha();
397
398 uint16 planarConfig = PLANARCONFIG_CONTIG;
399 (void) TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &planarConfig);
400
401 bool ok = true;
402 char msg[1024] = "";
403 if
404 (
405 (planarConfig == PLANARCONFIG_CONTIG && samplesPerPixel == 2
406 && extraSamples == 1)
407 &&
408 (
409 ( !TIFFRGBAImageOK(tif, msg) )
410 || (bitsPerSample == 8)
411 )
412 )
413 {
414 const bool isGreyScale = (bitsPerSample == 8);
415 unsigned char *buf = (unsigned char *)_TIFFmalloc(TIFFScanlineSize(tif));
416 uint32 pos = 0;
417 const bool minIsWhite = (photometric == PHOTOMETRIC_MINISWHITE);
418 const int minValue = minIsWhite ? 255 : 0;
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
434 if (isGreyScale)
435 {
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 }
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)
469 {
470 if (verbose)
471 {
472 wxLogError( _("TIFF: Error reading image.") );
473 }
474
475 _TIFFfree( raster );
476 image->Destroy();
477 TIFFClose( tif );
478
479 return false;
480 }
481
482 unsigned char *ptr = image->GetData();
483
484 unsigned char *alpha = image->GetAlpha();
485
486 uint32 pos = 0;
487
488 for (uint32 i = 0; i < h; i++)
489 {
490 for (uint32 j = 0; j < w; j++)
491 {
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
498 pos++;
499 }
500 }
501
502
503 image->SetOption(wxIMAGE_OPTION_TIFF_PHOTOMETRIC, photometric);
504
505 uint16 compression;
506 /*
507 Copy some baseline TIFF tags which helps when re-saving a TIFF
508 to be similar to the original image.
509 */
510 if (samplesPerPixel)
511 {
512 image->SetOption(wxIMAGE_OPTION_TIFF_SAMPLESPERPIXEL, samplesPerPixel);
513 }
514
515 if (bitsPerSample)
516 {
517 image->SetOption(wxIMAGE_OPTION_TIFF_BITSPERSAMPLE, bitsPerSample);
518 }
519
520 if ( TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &compression) )
521 {
522 image->SetOption(wxIMAGE_OPTION_TIFF_COMPRESSION, compression);
523 }
524
525 // Set the resolution unit.
526 wxImageResolution resUnit = wxIMAGE_RESOLUTION_NONE;
527 uint16 tiffRes;
528 if ( TIFFGetFieldDefaulted(tif, TIFFTAG_RESOLUTIONUNIT, &tiffRes) )
529 {
530 switch (tiffRes)
531 {
532 default:
533 wxLogWarning(_("Unknown TIFF resolution unit %d ignored"),
534 tiffRes);
535 // fall through
536
537 case RESUNIT_NONE:
538 resUnit = wxIMAGE_RESOLUTION_NONE;
539 break;
540
541 case RESUNIT_INCH:
542 resUnit = wxIMAGE_RESOLUTION_INCHES;
543 break;
544
545 case RESUNIT_CENTIMETER:
546 resUnit = wxIMAGE_RESOLUTION_CM;
547 break;
548 }
549 }
550
551 image->SetOption(wxIMAGE_OPTION_RESOLUTIONUNIT, resUnit);
552
553 /*
554 Set the image resolution if it's available. Resolution tag is not
555 dependant on RESOLUTIONUNIT != RESUNIT_NONE (according to TIFF spec).
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));
569 }
570
571 if ( TIFFGetField(tif, TIFFTAG_YRESOLUTION, &resY) )
572 {
573 image->SetOption(wxIMAGE_OPTION_RESOLUTIONY,
574 wxString::FromCDouble((double) resY));
575 }
576
577 _TIFFfree( raster );
578
579 TIFFClose( tif );
580
581 return true;
582 }
583
584 int wxTIFFHandler::DoGetImageCount( wxInputStream& stream )
585 {
586 TIFF *tif = TIFFwxOpen( stream, "image", "r" );
587
588 if (!tif)
589 return 0;
590
591 int dircount = 0; // according to the libtiff docs, dircount should be set to 1 here???
592 do {
593 dircount++;
594 } while (TIFFReadDirectory(tif));
595
596 TIFFClose( tif );
597
598 // NOTE: this function modifies the current stream position but it's ok
599 // (see wxImageHandler::GetImageCount)
600
601 return dircount;
602 }
603
604 bool wxTIFFHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbose )
605 {
606 TIFF *tif = TIFFwxOpen( stream, "image", "w" );
607
608 if (!tif)
609 {
610 if (verbose)
611 {
612 wxLogError( _("TIFF: Error saving image.") );
613 }
614
615 return false;
616 }
617
618 const int imageWidth = image->GetWidth();
619 TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, (uint32) imageWidth);
620 TIFFSetField(tif, TIFFTAG_IMAGELENGTH, (uint32)image->GetHeight());
621 TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
622 TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
623
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 )
629 {
630 default:
631 wxFAIL_MSG( wxT("unknown image resolution units") );
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;
645 }
646
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
655 int spp = image->GetOptionInt(wxIMAGE_OPTION_TIFF_SAMPLESPERPIXEL);
656 if ( !spp )
657 spp = 3;
658
659 int bps = image->GetOptionInt(wxIMAGE_OPTION_TIFF_BITSPERSAMPLE);
660 if ( !bps )
661 {
662 bps = 8;
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 }
670
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 }
683 else if (spp <= 2)
684 {
685 photometric = PHOTOMETRIC_MINISWHITE;
686 }
687
688 const bool hasAlpha = image->HasAlpha();
689
690 int compression = image->GetOptionInt(wxIMAGE_OPTION_TIFF_COMPRESSION);
691 if ( !compression || (compression == COMPRESSION_JPEG && hasAlpha) )
692 {
693 // We can't use COMPRESSION_LZW because current version of libtiff
694 // doesn't implement it ("no longer implemented due to Unisys patent
695 // enforcement") and other compression methods are lossy so we
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).
699 compression = COMPRESSION_NONE;
700 }
701
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);
717 TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bps);
718 TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, photometric);
719 TIFFSetField(tif, TIFFTAG_COMPRESSION, compression);
720
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
728 const tsize_t linebytes =
729 (tsize_t)((imageWidth * (spp + extraSamples) * bps + 7) / 8);
730
731 unsigned char *buf;
732
733 const bool isColouredImage = (spp > 1)
734 && (photometric != PHOTOMETRIC_MINISWHITE)
735 && (photometric != PHOTOMETRIC_MINISBLACK);
736
737
738 if (TIFFScanlineSize(tif) > linebytes || !isColouredImage || hasAlpha)
739 {
740 buf = (unsigned char *)_TIFFmalloc(TIFFScanlineSize(tif));
741 if (!buf)
742 {
743 if (verbose)
744 {
745 wxLogError( _("TIFF: Couldn't allocate memory.") );
746 }
747
748 TIFFClose( tif );
749
750 return false;
751 }
752 }
753 else
754 {
755 buf = NULL;
756 }
757
758 TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP,TIFFDefaultStripSize(tif, (uint32) -1));
759
760 const int bitsPerPixel = (spp + extraSamples) * bps;
761 const int bytesPerPixel = (bitsPerPixel + 7) / 8;
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?
768 remainingPixelCount = imageWidth % pixelsPerByte;
769 if (!remainingPixelCount) remainingPixelCount = pixelsPerByte;
770 }
771
772 const bool minIsWhite = (photometric == PHOTOMETRIC_MINISWHITE);
773 unsigned char *ptr = image->GetData();
774 for ( int row = 0; row < image->GetHeight(); row++ )
775 {
776 if ( buf )
777 {
778 if (isColouredImage)
779 {
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 }
795 }
796 else if (spp * bps == 8) // greyscale image
797 {
798 for ( int column = 0; column < imageWidth; column++ )
799 {
800 uint8 value = ptr[column*3 + 1];
801 if (minIsWhite)
802 {
803 value = 255 - value;
804 }
805
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 }
814 }
815 }
816 else // black and white image
817 {
818 for ( int column = 0; column < linebytes; column++ )
819 {
820 uint8 reverse = 0;
821 int pixelsPerByteCount = (column + 1 != linebytes)
822 ? pixelsPerByte
823 : remainingPixelCount;
824 for ( int bp = 0; bp < pixelsPerByteCount; bp++ )
825 {
826 if ( (ptr[column * 3 * pixelsPerByte + bp*3 + 1] <=127)
827 == minIsWhite )
828 {
829 // check only green as this is sufficient
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));
838 }
839 }
840
841 buf[column] = reverse;
842 }
843 }
844 }
845
846 if ( TIFFWriteScanline(tif, buf ? buf : ptr, (uint32)row, 0) < 0 )
847 {
848 if (verbose)
849 {
850 wxLogError( _("TIFF: Error writing image.") );
851 }
852
853 TIFFClose( tif );
854 if (buf)
855 _TIFFfree(buf);
856
857 return false;
858 }
859
860 ptr += imageWidth * 3;
861 }
862
863 (void) TIFFClose(tif);
864
865 if (buf)
866 _TIFFfree(buf);
867
868 return true;
869 }
870
871 bool wxTIFFHandler::DoCanRead( wxInputStream& stream )
872 {
873 unsigned char hdr[2];
874
875 if ( !stream.Read(&hdr[0], WXSIZEOF(hdr)) ) // it's ok to modify the stream position here
876 return false;
877
878 return (hdr[0] == 'I' && hdr[1] == 'I') ||
879 (hdr[0] == 'M' && hdr[1] == 'M');
880 }
881
882 #endif // wxUSE_STREAMS
883
884 /*static*/ wxVersionInfo wxTIFFHandler::GetLibraryVersionInfo()
885 {
886 int major,
887 minor,
888 micro;
889
890 const wxString ver(::TIFFGetVersion());
891 if ( wxSscanf(ver, "LIBTIFF, Version %d.%d.%d", &major, &minor, &micro) != 3 )
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
907 #endif // wxUSE_LIBTIFF