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