]> git.saurik.com Git - wxWidgets.git/blob - src/common/imagtiff.cpp
Initial work on virtual file system support for the WebKitGTK+ backend. It now suppor...
[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_TIF;
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 return wxFileOffsetToTIFF(stream->SeekO((wxFileOffset)off,
196 wxSeekModeFromTIFF(whence)));
197 }
198
199 int TIFFLINKAGEMODE
200 wxTIFFCloseIProc(thandle_t WXUNUSED(handle))
201 {
202 // there is no need to close the input stream
203 return 0;
204 }
205
206 int TIFFLINKAGEMODE
207 wxTIFFCloseOProc(thandle_t handle)
208 {
209 wxOutputStream *stream = (wxOutputStream*) handle;
210
211 return stream->Close() ? 0 : -1;
212 }
213
214 toff_t TIFFLINKAGEMODE
215 wxTIFFSizeProc(thandle_t handle)
216 {
217 wxStreamBase *stream = (wxStreamBase*) handle;
218 return (toff_t) stream->GetSize();
219 }
220
221 int TIFFLINKAGEMODE
222 wxTIFFMapProc(thandle_t WXUNUSED(handle),
223 tdata_t* WXUNUSED(pbase),
224 toff_t* WXUNUSED(psize))
225 {
226 return 0;
227 }
228
229 void TIFFLINKAGEMODE
230 wxTIFFUnmapProc(thandle_t WXUNUSED(handle),
231 tdata_t WXUNUSED(base),
232 toff_t WXUNUSED(size))
233 {
234 }
235
236 } // extern "C"
237
238 TIFF*
239 TIFFwxOpen(wxInputStream &stream, const char* name, const char* mode)
240 {
241 TIFF* tif = TIFFClientOpen(name, mode,
242 (thandle_t) &stream,
243 wxTIFFReadProc, wxTIFFNullProc,
244 wxTIFFSeekIProc, wxTIFFCloseIProc, wxTIFFSizeProc,
245 wxTIFFMapProc, wxTIFFUnmapProc);
246
247 return tif;
248 }
249
250 TIFF*
251 TIFFwxOpen(wxOutputStream &stream, const char* name, const char* mode)
252 {
253 TIFF* tif = TIFFClientOpen(name, mode,
254 (thandle_t) &stream,
255 wxTIFFNullProc, wxTIFFWriteProc,
256 wxTIFFSeekOProc, wxTIFFCloseOProc, wxTIFFSizeProc,
257 wxTIFFMapProc, wxTIFFUnmapProc);
258
259 return tif;
260 }
261
262 bool wxTIFFHandler::LoadFile( wxImage *image, wxInputStream& stream, bool verbose, int index )
263 {
264 if (index == -1)
265 index = 0;
266
267 image->Destroy();
268
269 TIFF *tif = TIFFwxOpen( stream, "image", "r" );
270
271 if (!tif)
272 {
273 if (verbose)
274 {
275 wxLogError( _("TIFF: Error loading image.") );
276 }
277
278 return false;
279 }
280
281 if (!TIFFSetDirectory( tif, (tdir_t)index ))
282 {
283 if (verbose)
284 {
285 wxLogError( _("Invalid TIFF image index.") );
286 }
287
288 TIFFClose( tif );
289
290 return false;
291 }
292
293 uint32 w, h;
294 uint32 *raster;
295
296 TIFFGetField( tif, TIFFTAG_IMAGEWIDTH, &w );
297 TIFFGetField( tif, TIFFTAG_IMAGELENGTH, &h );
298
299 uint16 photometric;
300 uint16 samplesPerPixel;
301 uint16 extraSamples;
302 uint16* samplesInfo;
303 TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &samplesPerPixel);
304 TIFFGetFieldDefaulted(tif, TIFFTAG_EXTRASAMPLES,
305 &extraSamples, &samplesInfo);
306 if (!TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric))
307 {
308 photometric = PHOTOMETRIC_MINISWHITE;
309 }
310 const bool hasAlpha = (extraSamples >= 1
311 && ((samplesInfo[0] == EXTRASAMPLE_UNSPECIFIED && samplesPerPixel > 3)
312 || samplesInfo[0] == EXTRASAMPLE_ASSOCALPHA
313 || samplesInfo[0] == EXTRASAMPLE_UNASSALPHA))
314 || (extraSamples == 0 && samplesPerPixel == 4
315 && photometric == PHOTOMETRIC_RGB);
316
317 // guard against integer overflow during multiplication which could result
318 // in allocating a too small buffer and then overflowing it
319 const double bytesNeeded = (double)w * (double)h * sizeof(uint32);
320 if ( bytesNeeded >= wxUINT32_MAX )
321 {
322 if ( verbose )
323 {
324 wxLogError( _("TIFF: Image size is abnormally big.") );
325 }
326
327 TIFFClose(tif);
328
329 return false;
330 }
331
332 raster = (uint32*) _TIFFmalloc( (uint32)bytesNeeded );
333
334 if (!raster)
335 {
336 if (verbose)
337 {
338 wxLogError( _("TIFF: Couldn't allocate memory.") );
339 }
340
341 TIFFClose( tif );
342
343 return false;
344 }
345
346 image->Create( (int)w, (int)h );
347 if (!image->IsOk())
348 {
349 if (verbose)
350 {
351 wxLogError( _("TIFF: Couldn't allocate memory.") );
352 }
353
354 _TIFFfree( raster );
355 TIFFClose( tif );
356
357 return false;
358 }
359
360 if ( hasAlpha )
361 image->SetAlpha();
362
363 if (!TIFFReadRGBAImage( tif, w, h, raster, 0 ))
364 {
365 if (verbose)
366 {
367 wxLogError( _("TIFF: Error reading image.") );
368 }
369
370 _TIFFfree( raster );
371 image->Destroy();
372 TIFFClose( tif );
373
374 return false;
375 }
376
377 unsigned char *ptr = image->GetData();
378 ptr += w*3*(h-1);
379
380 unsigned char *alpha = hasAlpha ? image->GetAlpha() : NULL;
381 if ( hasAlpha )
382 alpha += w*(h-1);
383
384 uint32 pos = 0;
385
386 for (uint32 i = 0; i < h; i++)
387 {
388 for (uint32 j = 0; j < w; j++)
389 {
390 *(ptr++) = (unsigned char)TIFFGetR(raster[pos]);
391 *(ptr++) = (unsigned char)TIFFGetG(raster[pos]);
392 *(ptr++) = (unsigned char)TIFFGetB(raster[pos]);
393 if ( hasAlpha )
394 *(alpha++) = (unsigned char)TIFFGetA(raster[pos]);
395
396 pos++;
397 }
398
399 // subtract line we just added plus one line:
400 ptr -= 2*w*3;
401 if ( hasAlpha )
402 alpha -= 2*w;
403 }
404
405
406 uint16 spp, bpp, compression;
407 /*
408 Read some baseline TIFF tags which helps when re-saving a TIFF
409 to be similar to the original image.
410 */
411 if ( TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &spp) )
412 {
413 image->SetOption(wxIMAGE_OPTION_SAMPLESPERPIXEL, spp);
414 }
415
416 if ( TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bpp) )
417 {
418 image->SetOption(wxIMAGE_OPTION_BITSPERSAMPLE, bpp);
419 }
420
421 if ( TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &compression) )
422 {
423 image->SetOption(wxIMAGE_OPTION_COMPRESSION, compression);
424 }
425
426 // Set the resolution unit.
427 wxImageResolution resUnit = wxIMAGE_RESOLUTION_NONE;
428 uint16 tiffRes;
429 if ( TIFFGetFieldDefaulted(tif, TIFFTAG_RESOLUTIONUNIT, &tiffRes) )
430 {
431 switch (tiffRes)
432 {
433 default:
434 wxLogWarning(_("Unknown TIFF resolution unit %d ignored"),
435 tiffRes);
436 // fall through
437
438 case RESUNIT_NONE:
439 resUnit = wxIMAGE_RESOLUTION_NONE;
440 break;
441
442 case RESUNIT_INCH:
443 resUnit = wxIMAGE_RESOLUTION_INCHES;
444 break;
445
446 case RESUNIT_CENTIMETER:
447 resUnit = wxIMAGE_RESOLUTION_CM;
448 break;
449 }
450 }
451
452 image->SetOption(wxIMAGE_OPTION_RESOLUTIONUNIT, resUnit);
453
454 /*
455 Set the image resolution if it's available. Resolution tag is not
456 dependant on RESOLUTIONUNIT != RESUNIT_NONE (according to TIFF spec).
457 */
458 float resX, resY;
459
460 if ( TIFFGetField(tif, TIFFTAG_XRESOLUTION, &resX) )
461 {
462 /*
463 Use a string value to not lose precision.
464 rounding to int as cm and then converting to inch may
465 result in whole integer rounding error, eg. 201 instead of 200 dpi.
466 If an app wants an int, GetOptionInt will convert and round down.
467 */
468 image->SetOption(wxIMAGE_OPTION_RESOLUTIONX,
469 wxString::FromCDouble((double) resX));
470 }
471
472 if ( TIFFGetField(tif, TIFFTAG_YRESOLUTION, &resY) )
473 {
474 image->SetOption(wxIMAGE_OPTION_RESOLUTIONY,
475 wxString::FromCDouble((double) resY));
476 }
477
478 _TIFFfree( raster );
479
480 TIFFClose( tif );
481
482 return true;
483 }
484
485 int wxTIFFHandler::DoGetImageCount( wxInputStream& stream )
486 {
487 TIFF *tif = TIFFwxOpen( stream, "image", "r" );
488
489 if (!tif)
490 return 0;
491
492 int dircount = 0; // according to the libtiff docs, dircount should be set to 1 here???
493 do {
494 dircount++;
495 } while (TIFFReadDirectory(tif));
496
497 TIFFClose( tif );
498
499 // NOTE: this function modifies the current stream position but it's ok
500 // (see wxImageHandler::GetImageCount)
501
502 return dircount;
503 }
504
505 bool wxTIFFHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbose )
506 {
507 TIFF *tif = TIFFwxOpen( stream, "image", "w" );
508
509 if (!tif)
510 {
511 if (verbose)
512 {
513 wxLogError( _("TIFF: Error saving image.") );
514 }
515
516 return false;
517 }
518
519 TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
520 TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, (uint32)image->GetWidth());
521 TIFFSetField(tif, TIFFTAG_IMAGELENGTH, (uint32)image->GetHeight());
522 TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
523 TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
524
525 // save the image resolution if we have it
526 int xres, yres;
527 const wxImageResolution res = GetResolutionFromOptions(*image, &xres, &yres);
528 uint16 tiffRes;
529 switch ( res )
530 {
531 default:
532 wxFAIL_MSG( wxT("unknown image resolution units") );
533 // fall through
534
535 case wxIMAGE_RESOLUTION_NONE:
536 tiffRes = RESUNIT_NONE;
537 break;
538
539 case wxIMAGE_RESOLUTION_INCHES:
540 tiffRes = RESUNIT_INCH;
541 break;
542
543 case wxIMAGE_RESOLUTION_CM:
544 tiffRes = RESUNIT_CENTIMETER;
545 break;
546 }
547
548 if ( tiffRes != RESUNIT_NONE )
549 {
550 TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, tiffRes);
551 TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float)xres);
552 TIFFSetField(tif, TIFFTAG_YRESOLUTION, (float)yres);
553 }
554
555
556 int spp = image->GetOptionInt(wxIMAGE_OPTION_SAMPLESPERPIXEL);
557 if ( !spp )
558 spp = 3;
559
560 int bpp = image->GetOptionInt(wxIMAGE_OPTION_BITSPERSAMPLE);
561 if ( !bpp )
562 bpp = 8;
563
564 int compression = image->GetOptionInt(wxIMAGE_OPTION_COMPRESSION);
565 if ( !compression )
566 {
567 // we can't use COMPRESSION_LZW because current version of libtiff
568 // doesn't implement it ("no longer implemented due to Unisys patent
569 // enforcement") and other compression methods are lossy so we
570 // shouldn't use them by default -- and the only remaining one is none
571 compression = COMPRESSION_NONE;
572 }
573
574 TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, spp);
575 TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bpp);
576 TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, spp*bpp == 1 ? PHOTOMETRIC_MINISBLACK
577 : PHOTOMETRIC_RGB);
578 TIFFSetField(tif, TIFFTAG_COMPRESSION, compression);
579
580 // scanlinesize if determined by spp and bpp
581 tsize_t linebytes = (tsize_t)image->GetWidth() * spp * bpp / 8;
582
583 if ( (image->GetWidth() % 8 > 0) && (spp * bpp < 8) )
584 linebytes+=1;
585
586 unsigned char *buf;
587
588 if (TIFFScanlineSize(tif) > linebytes || (spp * bpp < 24))
589 {
590 buf = (unsigned char *)_TIFFmalloc(TIFFScanlineSize(tif));
591 if (!buf)
592 {
593 if (verbose)
594 {
595 wxLogError( _("TIFF: Couldn't allocate memory.") );
596 }
597
598 TIFFClose( tif );
599
600 return false;
601 }
602 }
603 else
604 {
605 buf = NULL;
606 }
607
608 TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP,TIFFDefaultStripSize(tif, (uint32) -1));
609
610 unsigned char *ptr = image->GetData();
611 for ( int row = 0; row < image->GetHeight(); row++ )
612 {
613 if ( buf )
614 {
615 if ( spp * bpp > 1 )
616 {
617 // color image
618 memcpy(buf, ptr, image->GetWidth());
619 }
620 else // black and white image
621 {
622 for ( int column = 0; column < linebytes; column++ )
623 {
624 uint8 reverse = 0;
625 for ( int bp = 0; bp < 8; bp++ )
626 {
627 if ( ptr[column*24 + bp*3] > 0 )
628 {
629 // check only red as this is sufficient
630 reverse = (uint8)(reverse | 128 >> bp);
631 }
632 }
633
634 buf[column] = reverse;
635 }
636 }
637 }
638
639 if ( TIFFWriteScanline(tif, buf ? buf : ptr, (uint32)row, 0) < 0 )
640 {
641 if (verbose)
642 {
643 wxLogError( _("TIFF: Error writing image.") );
644 }
645
646 TIFFClose( tif );
647 if (buf)
648 _TIFFfree(buf);
649
650 return false;
651 }
652
653 ptr += image->GetWidth()*3;
654 }
655
656 (void) TIFFClose(tif);
657
658 if (buf)
659 _TIFFfree(buf);
660
661 return true;
662 }
663
664 bool wxTIFFHandler::DoCanRead( wxInputStream& stream )
665 {
666 unsigned char hdr[2];
667
668 if ( !stream.Read(&hdr[0], WXSIZEOF(hdr)) ) // it's ok to modify the stream position here
669 return false;
670
671 return (hdr[0] == 'I' && hdr[1] == 'I') ||
672 (hdr[0] == 'M' && hdr[1] == 'M');
673 }
674
675 #endif // wxUSE_STREAMS
676
677 /*static*/ wxVersionInfo wxTIFFHandler::GetLibraryVersionInfo()
678 {
679 int major,
680 minor,
681 micro;
682
683 const wxString ver(::TIFFGetVersion());
684 if ( wxSscanf(ver, "LIBTIFF, Version %d.%d.%d", &major, &minor, &micro) != 3 )
685 {
686 wxLogDebug("Unrecognized libtiff version string \"%s\"", ver);
687
688 major =
689 minor =
690 micro = 0;
691 }
692
693 wxString copyright;
694 const wxString desc = ver.BeforeFirst('\n', &copyright);
695 copyright.Replace("\n", "");
696
697 return wxVersionInfo("libtiff", major, minor, micro, desc, copyright);
698 }
699
700 #endif // wxUSE_LIBTIFF