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