Added wxBITMAP_TYPE_TIFF and wxBITMAP_TYPE_TIFF_RESOURCE.
[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 if (!TIFFReadRGBAImage( tif, w, h, raster, 0 ))
398 {
399 if (verbose)
400 {
401 wxLogError( _("TIFF: Error reading image.") );
402 }
403
404 _TIFFfree( raster );
405 image->Destroy();
406 TIFFClose( tif );
407
408 return false;
409 }
410
411 unsigned char *ptr = image->GetData();
412 ptr += w*3*(h-1);
413
414 unsigned char *alpha = hasAlpha ? image->GetAlpha() : NULL;
415 if ( hasAlpha )
416 alpha += w*(h-1);
417
418 uint32 pos = 0;
419
420 for (uint32 i = 0; i < h; i++)
421 {
422 for (uint32 j = 0; j < w; j++)
423 {
424 *(ptr++) = (unsigned char)TIFFGetR(raster[pos]);
425 *(ptr++) = (unsigned char)TIFFGetG(raster[pos]);
426 *(ptr++) = (unsigned char)TIFFGetB(raster[pos]);
427 if ( hasAlpha )
428 *(alpha++) = (unsigned char)TIFFGetA(raster[pos]);
429
430 pos++;
431 }
432
433 // subtract line we just added plus one line:
434 ptr -= 2*w*3;
435 if ( hasAlpha )
436 alpha -= 2*w;
437 }
438
439
440 image->SetOption(wxIMAGE_OPTION_TIFF_PHOTOMETRIC, photometric);
441
442 uint16 spp, bps, compression;
443 /*
444 Read some baseline TIFF tags which helps when re-saving a TIFF
445 to be similar to the original image.
446 */
447 if ( TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &spp) )
448 {
449 image->SetOption(wxIMAGE_OPTION_TIFF_SAMPLESPERPIXEL, spp);
450 }
451
452 if ( TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bps) )
453 {
454 image->SetOption(wxIMAGE_OPTION_TIFF_BITSPERSAMPLE, bps);
455 }
456
457 if ( TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &compression) )
458 {
459 image->SetOption(wxIMAGE_OPTION_TIFF_COMPRESSION, compression);
460 }
461
462 // Set the resolution unit.
463 wxImageResolution resUnit = wxIMAGE_RESOLUTION_NONE;
464 uint16 tiffRes;
465 if ( TIFFGetFieldDefaulted(tif, TIFFTAG_RESOLUTIONUNIT, &tiffRes) )
466 {
467 switch (tiffRes)
468 {
469 default:
470 wxLogWarning(_("Unknown TIFF resolution unit %d ignored"),
471 tiffRes);
472 // fall through
473
474 case RESUNIT_NONE:
475 resUnit = wxIMAGE_RESOLUTION_NONE;
476 break;
477
478 case RESUNIT_INCH:
479 resUnit = wxIMAGE_RESOLUTION_INCHES;
480 break;
481
482 case RESUNIT_CENTIMETER:
483 resUnit = wxIMAGE_RESOLUTION_CM;
484 break;
485 }
486 }
487
488 image->SetOption(wxIMAGE_OPTION_RESOLUTIONUNIT, resUnit);
489
490 /*
491 Set the image resolution if it's available. Resolution tag is not
492 dependant on RESOLUTIONUNIT != RESUNIT_NONE (according to TIFF spec).
493 */
494 float resX, resY;
495
496 if ( TIFFGetField(tif, TIFFTAG_XRESOLUTION, &resX) )
497 {
498 /*
499 Use a string value to not lose precision.
500 rounding to int as cm and then converting to inch may
501 result in whole integer rounding error, eg. 201 instead of 200 dpi.
502 If an app wants an int, GetOptionInt will convert and round down.
503 */
504 image->SetOption(wxIMAGE_OPTION_RESOLUTIONX,
505 wxString::FromCDouble((double) resX));
506 }
507
508 if ( TIFFGetField(tif, TIFFTAG_YRESOLUTION, &resY) )
509 {
510 image->SetOption(wxIMAGE_OPTION_RESOLUTIONY,
511 wxString::FromCDouble((double) resY));
512 }
513
514 _TIFFfree( raster );
515
516 TIFFClose( tif );
517
518 return true;
519 }
520
521 int wxTIFFHandler::DoGetImageCount( wxInputStream& stream )
522 {
523 TIFF *tif = TIFFwxOpen( stream, "image", "r" );
524
525 if (!tif)
526 return 0;
527
528 int dircount = 0; // according to the libtiff docs, dircount should be set to 1 here???
529 do {
530 dircount++;
531 } while (TIFFReadDirectory(tif));
532
533 TIFFClose( tif );
534
535 // NOTE: this function modifies the current stream position but it's ok
536 // (see wxImageHandler::GetImageCount)
537
538 return dircount;
539 }
540
541 bool wxTIFFHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbose )
542 {
543 TIFF *tif = TIFFwxOpen( stream, "image", "w" );
544
545 if (!tif)
546 {
547 if (verbose)
548 {
549 wxLogError( _("TIFF: Error saving image.") );
550 }
551
552 return false;
553 }
554
555 TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
556 TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, (uint32)image->GetWidth());
557 TIFFSetField(tif, TIFFTAG_IMAGELENGTH, (uint32)image->GetHeight());
558 TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
559 TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
560
561 // save the image resolution if we have it
562 int xres, yres;
563 const wxImageResolution res = GetResolutionFromOptions(*image, &xres, &yres);
564 uint16 tiffRes;
565 switch ( res )
566 {
567 default:
568 wxFAIL_MSG( wxT("unknown image resolution units") );
569 // fall through
570
571 case wxIMAGE_RESOLUTION_NONE:
572 tiffRes = RESUNIT_NONE;
573 break;
574
575 case wxIMAGE_RESOLUTION_INCHES:
576 tiffRes = RESUNIT_INCH;
577 break;
578
579 case wxIMAGE_RESOLUTION_CM:
580 tiffRes = RESUNIT_CENTIMETER;
581 break;
582 }
583
584 if ( tiffRes != RESUNIT_NONE )
585 {
586 TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, tiffRes);
587 TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float)xres);
588 TIFFSetField(tif, TIFFTAG_YRESOLUTION, (float)yres);
589 }
590
591
592 int spp = image->GetOptionInt(wxIMAGE_OPTION_TIFF_SAMPLESPERPIXEL);
593 if ( !spp )
594 spp = 3;
595
596 int bps = image->GetOptionInt(wxIMAGE_OPTION_TIFF_BITSPERSAMPLE);
597 if ( !bps )
598 {
599 bps = 8;
600 }
601 else if (bps == 1)
602 {
603 // One bit per sample combined with 3 samples per pixel is
604 // not allowed and crashes libtiff.
605 spp = 1;
606 }
607
608 int photometric = PHOTOMETRIC_RGB;
609
610 if ( image->HasOption(wxIMAGE_OPTION_TIFF_PHOTOMETRIC) )
611 {
612 photometric = image->GetOptionInt(wxIMAGE_OPTION_TIFF_PHOTOMETRIC);
613 if (photometric == PHOTOMETRIC_MINISWHITE
614 || photometric == PHOTOMETRIC_MINISBLACK)
615 {
616 // either b/w or greyscale
617 spp = 1;
618 }
619 }
620 else if (spp == 1)
621 {
622 photometric = PHOTOMETRIC_MINISWHITE;
623 }
624
625 const bool isColouredImage = (spp > 1)
626 && (photometric != PHOTOMETRIC_MINISWHITE)
627 && (photometric != PHOTOMETRIC_MINISBLACK);
628
629 int compression = image->GetOptionInt(wxIMAGE_OPTION_TIFF_COMPRESSION);
630 if ( !compression )
631 {
632 // we can't use COMPRESSION_LZW because current version of libtiff
633 // doesn't implement it ("no longer implemented due to Unisys patent
634 // enforcement") and other compression methods are lossy so we
635 // shouldn't use them by default -- and the only remaining one is none
636 compression = COMPRESSION_NONE;
637 }
638
639 TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, spp);
640 TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bps);
641 TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, photometric);
642 TIFFSetField(tif, TIFFTAG_COMPRESSION, compression);
643
644 // scanlinesize if determined by spp and bps
645 tsize_t linebytes = (tsize_t)image->GetWidth() * spp * bps / 8;
646
647 if ( (image->GetWidth() % 8 > 0) && (spp * bps < 8) )
648 linebytes+=1;
649
650 unsigned char *buf;
651
652 if (TIFFScanlineSize(tif) > linebytes || !isColouredImage)
653 {
654 buf = (unsigned char *)_TIFFmalloc(TIFFScanlineSize(tif));
655 if (!buf)
656 {
657 if (verbose)
658 {
659 wxLogError( _("TIFF: Couldn't allocate memory.") );
660 }
661
662 TIFFClose( tif );
663
664 return false;
665 }
666 }
667 else
668 {
669 buf = NULL;
670 }
671
672 TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP,TIFFDefaultStripSize(tif, (uint32) -1));
673
674 const bool minIsWhite = (photometric == PHOTOMETRIC_MINISWHITE);
675 unsigned char *ptr = image->GetData();
676 for ( int row = 0; row < image->GetHeight(); row++ )
677 {
678 if ( buf )
679 {
680 if (isColouredImage)
681 {
682 // color image
683 memcpy(buf, ptr, image->GetWidth());
684 }
685 else if (spp * bps == 8) // greyscale image
686 {
687 for ( int column = 0; column < linebytes; column++ )
688 {
689 uint8 value = ptr[column*3 + 1];
690 if (minIsWhite)
691 {
692 value = 255 - value;
693 }
694
695 buf[column] = value;
696 }
697 }
698 else // black and white image
699 {
700 for ( int column = 0; column < linebytes; column++ )
701 {
702 uint8 reverse = 0;
703 for ( int bp = 0; bp < 8; bp++ )
704 {
705 if ( (ptr[column*24 + bp*3 + 1] <=127) == minIsWhite )
706 {
707 // check only green as this is sufficient
708 reverse = (uint8)(reverse | 128 >> bp);
709 }
710 }
711
712 buf[column] = reverse;
713 }
714 }
715 }
716
717 if ( TIFFWriteScanline(tif, buf ? buf : ptr, (uint32)row, 0) < 0 )
718 {
719 if (verbose)
720 {
721 wxLogError( _("TIFF: Error writing image.") );
722 }
723
724 TIFFClose( tif );
725 if (buf)
726 _TIFFfree(buf);
727
728 return false;
729 }
730
731 ptr += image->GetWidth()*3;
732 }
733
734 (void) TIFFClose(tif);
735
736 if (buf)
737 _TIFFfree(buf);
738
739 return true;
740 }
741
742 bool wxTIFFHandler::DoCanRead( wxInputStream& stream )
743 {
744 unsigned char hdr[2];
745
746 if ( !stream.Read(&hdr[0], WXSIZEOF(hdr)) ) // it's ok to modify the stream position here
747 return false;
748
749 return (hdr[0] == 'I' && hdr[1] == 'I') ||
750 (hdr[0] == 'M' && hdr[1] == 'M');
751 }
752
753 #endif // wxUSE_STREAMS
754
755 /*static*/ wxVersionInfo wxTIFFHandler::GetLibraryVersionInfo()
756 {
757 int major,
758 minor,
759 micro;
760
761 const wxString ver(::TIFFGetVersion());
762 if ( wxSscanf(ver, "LIBTIFF, Version %d.%d.%d", &major, &minor, &micro) != 3 )
763 {
764 wxLogDebug("Unrecognized libtiff version string \"%s\"", ver);
765
766 major =
767 minor =
768 micro = 0;
769 }
770
771 wxString copyright;
772 const wxString desc = ver.BeforeFirst('\n', &copyright);
773 copyright.Replace("\n", "");
774
775 return wxVersionInfo("libtiff", major, minor, micro, desc, copyright);
776 }
777
778 #endif // wxUSE_LIBTIFF