]> git.saurik.com Git - wxWidgets.git/blob - src/common/imagtiff.cpp
Update version to 2.9.4 in version.bkl too and rebake everything.
[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 samplesPerPixel = 0;
334 (void) TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &samplesPerPixel);
335
336 uint16 bitsPerSample = 0;
337 (void) TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bitsPerSample);
338
339 uint16 extraSamples;
340 uint16* samplesInfo;
341 TIFFGetFieldDefaulted(tif, TIFFTAG_EXTRASAMPLES,
342 &extraSamples, &samplesInfo);
343
344 uint16 photometric;
345 if (!TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric))
346 {
347 photometric = PHOTOMETRIC_MINISWHITE;
348 }
349 const bool hasAlpha = (extraSamples >= 1
350 && ((samplesInfo[0] == EXTRASAMPLE_UNSPECIFIED)
351 || samplesInfo[0] == EXTRASAMPLE_ASSOCALPHA
352 || samplesInfo[0] == EXTRASAMPLE_UNASSALPHA))
353 || (extraSamples == 0 && samplesPerPixel == 4
354 && photometric == PHOTOMETRIC_RGB);
355
356 // guard against integer overflow during multiplication which could result
357 // in allocating a too small buffer and then overflowing it
358 const double bytesNeeded = (double)w * (double)h * sizeof(uint32);
359 if ( bytesNeeded >= wxUINT32_MAX )
360 {
361 if ( verbose )
362 {
363 wxLogError( _("TIFF: Image size is abnormally big.") );
364 }
365
366 TIFFClose(tif);
367
368 return false;
369 }
370
371 raster = (uint32*) _TIFFmalloc( (uint32)bytesNeeded );
372
373 if (!raster)
374 {
375 if (verbose)
376 {
377 wxLogError( _("TIFF: Couldn't allocate memory.") );
378 }
379
380 TIFFClose( tif );
381
382 return false;
383 }
384
385 image->Create( (int)w, (int)h );
386 if (!image->IsOk())
387 {
388 if (verbose)
389 {
390 wxLogError( _("TIFF: Couldn't allocate memory.") );
391 }
392
393 _TIFFfree( raster );
394 TIFFClose( tif );
395
396 return false;
397 }
398
399 if ( hasAlpha )
400 image->SetAlpha();
401
402 uint16 planarConfig = PLANARCONFIG_CONTIG;
403 (void) TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &planarConfig);
404
405 bool ok = true;
406 char msg[1024] = "";
407 if
408 (
409 (planarConfig == PLANARCONFIG_CONTIG && samplesPerPixel == 2
410 && extraSamples == 1)
411 &&
412 (
413 ( !TIFFRGBAImageOK(tif, msg) )
414 || (bitsPerSample == 8)
415 )
416 )
417 {
418 const bool isGreyScale = (bitsPerSample == 8);
419 unsigned char *buf = (unsigned char *)_TIFFmalloc(TIFFScanlineSize(tif));
420 uint32 pos = 0;
421 const bool minIsWhite = (photometric == PHOTOMETRIC_MINISWHITE);
422 const int minValue = minIsWhite ? 255 : 0;
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
438 if (isGreyScale)
439 {
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 }
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)
473 {
474 if (verbose)
475 {
476 wxLogError( _("TIFF: Error reading image.") );
477 }
478
479 _TIFFfree( raster );
480 image->Destroy();
481 TIFFClose( tif );
482
483 return false;
484 }
485
486 unsigned char *ptr = image->GetData();
487
488 unsigned char *alpha = image->GetAlpha();
489
490 uint32 pos = 0;
491
492 for (uint32 i = 0; i < h; i++)
493 {
494 for (uint32 j = 0; j < w; j++)
495 {
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
502 pos++;
503 }
504 }
505
506
507 image->SetOption(wxIMAGE_OPTION_TIFF_PHOTOMETRIC, photometric);
508
509 uint16 compression;
510 /*
511 Copy some baseline TIFF tags which helps when re-saving a TIFF
512 to be similar to the original image.
513 */
514 if (samplesPerPixel)
515 {
516 image->SetOption(wxIMAGE_OPTION_TIFF_SAMPLESPERPIXEL, samplesPerPixel);
517 }
518
519 if (bitsPerSample)
520 {
521 image->SetOption(wxIMAGE_OPTION_TIFF_BITSPERSAMPLE, bitsPerSample);
522 }
523
524 if ( TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &compression) )
525 {
526 image->SetOption(wxIMAGE_OPTION_TIFF_COMPRESSION, compression);
527 }
528
529 // Set the resolution unit.
530 wxImageResolution resUnit = wxIMAGE_RESOLUTION_NONE;
531 uint16 tiffRes;
532 if ( TIFFGetFieldDefaulted(tif, TIFFTAG_RESOLUTIONUNIT, &tiffRes) )
533 {
534 switch (tiffRes)
535 {
536 default:
537 wxLogWarning(_("Unknown TIFF resolution unit %d ignored"),
538 tiffRes);
539 // fall through
540
541 case RESUNIT_NONE:
542 resUnit = wxIMAGE_RESOLUTION_NONE;
543 break;
544
545 case RESUNIT_INCH:
546 resUnit = wxIMAGE_RESOLUTION_INCHES;
547 break;
548
549 case RESUNIT_CENTIMETER:
550 resUnit = wxIMAGE_RESOLUTION_CM;
551 break;
552 }
553 }
554
555 image->SetOption(wxIMAGE_OPTION_RESOLUTIONUNIT, resUnit);
556
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));
573 }
574
575 if ( TIFFGetField(tif, TIFFTAG_YRESOLUTION, &resY) )
576 {
577 image->SetOption(wxIMAGE_OPTION_RESOLUTIONY,
578 wxString::FromCDouble((double) resY));
579 }
580
581 _TIFFfree( raster );
582
583 TIFFClose( tif );
584
585 return true;
586 }
587
588 int wxTIFFHandler::DoGetImageCount( wxInputStream& stream )
589 {
590 TIFF *tif = TIFFwxOpen( stream, "image", "r" );
591
592 if (!tif)
593 return 0;
594
595 int dircount = 0; // according to the libtiff docs, dircount should be set to 1 here???
596 do {
597 dircount++;
598 } while (TIFFReadDirectory(tif));
599
600 TIFFClose( tif );
601
602 // NOTE: this function modifies the current stream position but it's ok
603 // (see wxImageHandler::GetImageCount)
604
605 return dircount;
606 }
607
608 bool wxTIFFHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbose )
609 {
610 TIFF *tif = TIFFwxOpen( stream, "image", "w" );
611
612 if (!tif)
613 {
614 if (verbose)
615 {
616 wxLogError( _("TIFF: Error saving image.") );
617 }
618
619 return false;
620 }
621
622 const int imageWidth = image->GetWidth();
623 TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, (uint32) imageWidth);
624 TIFFSetField(tif, TIFFTAG_IMAGELENGTH, (uint32)image->GetHeight());
625 TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
626 TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
627
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 )
633 {
634 default:
635 wxFAIL_MSG( wxT("unknown image resolution units") );
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;
649 }
650
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
659 int spp = image->GetOptionInt(wxIMAGE_OPTION_TIFF_SAMPLESPERPIXEL);
660 if ( !spp )
661 spp = 3;
662
663 int bps = image->GetOptionInt(wxIMAGE_OPTION_TIFF_BITSPERSAMPLE);
664 if ( !bps )
665 {
666 bps = 8;
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 }
674
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 }
687 else if (spp <= 2)
688 {
689 photometric = PHOTOMETRIC_MINISWHITE;
690 }
691
692 const bool hasAlpha = image->HasAlpha();
693
694 int compression = image->GetOptionInt(wxIMAGE_OPTION_TIFF_COMPRESSION);
695 if ( !compression || (compression == COMPRESSION_JPEG && hasAlpha) )
696 {
697 // We can't use COMPRESSION_LZW because current version of libtiff
698 // doesn't implement it ("no longer implemented due to Unisys patent
699 // enforcement") and other compression methods are lossy so we
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).
703 compression = COMPRESSION_NONE;
704 }
705
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);
721 TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bps);
722 TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, photometric);
723 TIFFSetField(tif, TIFFTAG_COMPRESSION, compression);
724
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
732 const tsize_t linebytes =
733 (tsize_t)((imageWidth * (spp + extraSamples) * bps + 7) / 8);
734
735 unsigned char *buf;
736
737 const bool isColouredImage = (spp > 1)
738 && (photometric != PHOTOMETRIC_MINISWHITE)
739 && (photometric != PHOTOMETRIC_MINISBLACK);
740
741
742 if (TIFFScanlineSize(tif) > linebytes || !isColouredImage || hasAlpha)
743 {
744 buf = (unsigned char *)_TIFFmalloc(TIFFScanlineSize(tif));
745 if (!buf)
746 {
747 if (verbose)
748 {
749 wxLogError( _("TIFF: Couldn't allocate memory.") );
750 }
751
752 TIFFClose( tif );
753
754 return false;
755 }
756 }
757 else
758 {
759 buf = NULL;
760 }
761
762 TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP,TIFFDefaultStripSize(tif, (uint32) -1));
763
764 const int bitsPerPixel = (spp + extraSamples) * bps;
765 const int bytesPerPixel = (bitsPerPixel + 7) / 8;
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?
772 remainingPixelCount = imageWidth % pixelsPerByte;
773 if (!remainingPixelCount) remainingPixelCount = pixelsPerByte;
774 }
775
776 const bool minIsWhite = (photometric == PHOTOMETRIC_MINISWHITE);
777 unsigned char *ptr = image->GetData();
778 for ( int row = 0; row < image->GetHeight(); row++ )
779 {
780 if ( buf )
781 {
782 if (isColouredImage)
783 {
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 }
799 }
800 else if (spp * bps == 8) // greyscale image
801 {
802 for ( int column = 0; column < imageWidth; column++ )
803 {
804 uint8 value = ptr[column*3 + 1];
805 if (minIsWhite)
806 {
807 value = 255 - value;
808 }
809
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 }
818 }
819 }
820 else // black and white image
821 {
822 for ( int column = 0; column < linebytes; column++ )
823 {
824 uint8 reverse = 0;
825 int pixelsPerByteCount = (column + 1 != linebytes)
826 ? pixelsPerByte
827 : remainingPixelCount;
828 for ( int bp = 0; bp < pixelsPerByteCount; bp++ )
829 {
830 if ( (ptr[column * 3 * pixelsPerByte + bp*3 + 1] <=127)
831 == minIsWhite )
832 {
833 // check only green as this is sufficient
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));
842 }
843 }
844
845 buf[column] = reverse;
846 }
847 }
848 }
849
850 if ( TIFFWriteScanline(tif, buf ? buf : ptr, (uint32)row, 0) < 0 )
851 {
852 if (verbose)
853 {
854 wxLogError( _("TIFF: Error writing image.") );
855 }
856
857 TIFFClose( tif );
858 if (buf)
859 _TIFFfree(buf);
860
861 return false;
862 }
863
864 ptr += imageWidth * 3;
865 }
866
867 (void) TIFFClose(tif);
868
869 if (buf)
870 _TIFFfree(buf);
871
872 return true;
873 }
874
875 bool wxTIFFHandler::DoCanRead( wxInputStream& stream )
876 {
877 unsigned char hdr[2];
878
879 if ( !stream.Read(&hdr[0], WXSIZEOF(hdr)) ) // it's ok to modify the stream position here
880 return false;
881
882 return (hdr[0] == 'I' && hdr[1] == 'I') ||
883 (hdr[0] == 'M' && hdr[1] == 'M');
884 }
885
886 #endif // wxUSE_STREAMS
887
888 /*static*/ wxVersionInfo wxTIFFHandler::GetLibraryVersionInfo()
889 {
890 int major,
891 minor,
892 micro;
893
894 const wxString ver(::TIFFGetVersion());
895 if ( wxSscanf(ver, "LIBTIFF, Version %d.%d.%d", &major, &minor, &micro) != 3 )
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
911 #endif // wxUSE_LIBTIFF