• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 Google Inc. All Rights Reserved.
2 //
3 // Use of this source code is governed by a BSD-style license
4 // that can be found in the COPYING file in the root of the source
5 // tree. An additional intellectual property rights grant can be found
6 // in the file PATENTS. All contributing project authors may
7 // be found in the AUTHORS file in the root of the source tree.
8 // -----------------------------------------------------------------------------
9 //
10 // TIFF decode.
11 
12 #include "./tiffdec.h"
13 
14 #ifdef HAVE_CONFIG_H
15 #include "webp/config.h"
16 #endif
17 
18 #include <limits.h>
19 #include <stdio.h>
20 #include <string.h>
21 
22 #ifdef WEBP_HAVE_TIFF
23 #include <tiffio.h>
24 
25 #include "webp/encode.h"
26 #include "./imageio_util.h"
27 #include "./metadata.h"
28 
29 static const struct {
30   ttag_t tag;
31   size_t storage_offset;
32 } kTIFFMetadataMap[] = {
33   { TIFFTAG_ICCPROFILE, METADATA_OFFSET(iccp) },
34   { TIFFTAG_XMLPACKET,  METADATA_OFFSET(xmp) },
35   { 0, 0 },
36 };
37 
38 // Returns true on success. The caller must use MetadataFree() on 'metadata' in
39 // all cases.
ExtractMetadataFromTIFF(TIFF * const tif,Metadata * const metadata)40 static int ExtractMetadataFromTIFF(TIFF* const tif, Metadata* const metadata) {
41   int i;
42   toff_t exif_ifd_offset;
43 
44   for (i = 0; kTIFFMetadataMap[i].tag != 0; ++i) {
45     MetadataPayload* const payload =
46         (MetadataPayload*)((uint8_t*)metadata +
47                            kTIFFMetadataMap[i].storage_offset);
48     void* tag_data;
49     uint32 tag_data_len;
50 
51     if (TIFFGetField(tif, kTIFFMetadataMap[i].tag, &tag_data_len, &tag_data) &&
52         !MetadataCopy((const char*)tag_data, tag_data_len, payload)) {
53       return 0;
54     }
55   }
56 
57   // TODO(jzern): To extract the raw EXIF directory some parsing of it would be
58   // necessary to determine the overall size. In addition, value offsets in
59   // individual directory entries may need to be updated as, depending on the
60   // type, they are file based.
61   // Exif 2.2 Section 4.6.2 Tag Structure
62   // TIFF Revision 6.0 Part 1 Section 2 TIFF Structure #Image File Directory
63   if (TIFFGetField(tif, TIFFTAG_EXIFIFD, &exif_ifd_offset)) {
64     fprintf(stderr, "Warning: EXIF extraction from TIFF is unsupported.\n");
65   }
66   return 1;
67 }
68 
69 // Ad-hoc structure to supply read-from-memory functionalities.
70 typedef struct {
71   const uint8_t* data;
72   toff_t size;
73   toff_t pos;
74 } MyData;
75 
MyClose(thandle_t opaque)76 static int MyClose(thandle_t opaque) {
77   (void)opaque;
78   return 0;
79 }
80 
MySize(thandle_t opaque)81 static toff_t MySize(thandle_t opaque) {
82   const MyData* const my_data = (MyData*)opaque;
83   return my_data->size;
84 }
85 
MySeek(thandle_t opaque,toff_t offset,int whence)86 static toff_t MySeek(thandle_t opaque, toff_t offset, int whence) {
87   MyData* const my_data = (MyData*)opaque;
88   offset += (whence == SEEK_CUR) ? my_data->pos
89           : (whence == SEEK_SET) ? 0
90           : my_data->size;
91   if (offset > my_data->size) return (toff_t)-1;
92   my_data->pos = offset;
93   return offset;
94 }
95 
MyMapFile(thandle_t opaque,void ** base,toff_t * size)96 static int MyMapFile(thandle_t opaque, void** base, toff_t* size) {
97   (void)opaque;
98   (void)base;
99   (void)size;
100   return 0;
101 }
MyUnmapFile(thandle_t opaque,void * base,toff_t size)102 static void MyUnmapFile(thandle_t opaque, void* base, toff_t size) {
103   (void)opaque;
104   (void)base;
105   (void)size;
106 }
107 
MyRead(thandle_t opaque,void * dst,tsize_t size)108 static tsize_t MyRead(thandle_t opaque, void* dst, tsize_t size) {
109   MyData* const my_data = (MyData*)opaque;
110   if (my_data->pos + size > my_data->size) {
111     size = (tsize_t)(my_data->size - my_data->pos);
112   }
113   if (size > 0) {
114     memcpy(dst, my_data->data + my_data->pos, size);
115     my_data->pos += size;
116   }
117   return size;
118 }
119 
120 // Unmultiply Argb data. Taken from dsp/alpha_processing
121 // (we don't want to force a dependency to a libdspdec library).
122 #define MFIX 24    // 24bit fixed-point arithmetic
123 #define HALF ((1u << MFIX) >> 1)
124 
Unmult(uint8_t x,uint32_t mult)125 static uint32_t Unmult(uint8_t x, uint32_t mult) {
126   const uint32_t v = (x * mult + HALF) >> MFIX;
127   return (v > 255u) ? 255u : v;
128 }
129 
GetScale(uint32_t a)130 static WEBP_INLINE uint32_t GetScale(uint32_t a) {
131   return (255u << MFIX) / a;
132 }
133 
134 #undef MFIX
135 #undef HALF
136 
MultARGBRow(uint8_t * ptr,int width)137 static void MultARGBRow(uint8_t* ptr, int width) {
138   int x;
139   for (x = 0; x < width; ++x, ptr += 4) {
140     const uint32_t alpha = ptr[3];
141     if (alpha < 255) {
142       if (alpha == 0) {   // alpha == 0
143         ptr[0] = ptr[1] = ptr[2] = 0;
144       } else {
145         const uint32_t scale = GetScale(alpha);
146         ptr[0] = Unmult(ptr[0], scale);
147         ptr[1] = Unmult(ptr[1], scale);
148         ptr[2] = Unmult(ptr[2], scale);
149       }
150     }
151   }
152 }
153 
ReadTIFF(const uint8_t * const data,size_t data_size,WebPPicture * const pic,int keep_alpha,Metadata * const metadata)154 int ReadTIFF(const uint8_t* const data, size_t data_size,
155              WebPPicture* const pic, int keep_alpha,
156              Metadata* const metadata) {
157   MyData my_data = { data, (toff_t)data_size, 0 };
158   TIFF* tif;
159   uint32_t image_width, image_height, tile_width, tile_height;
160   uint64_t stride;
161   uint16_t samples_per_px = 0;
162   uint16_t extra_samples = 0;
163   uint16_t* extra_samples_ptr = NULL;
164   uint32_t* raster;
165   int64_t alloc_size;
166   int ok = 0;
167   tdir_t dircount;
168 
169   if (data == NULL || data_size == 0 || data_size > INT_MAX || pic == NULL) {
170     return 0;
171   }
172 
173   tif = TIFFClientOpen("Memory", "r", &my_data,
174                        MyRead, MyRead, MySeek, MyClose,
175                        MySize, MyMapFile, MyUnmapFile);
176   if (tif == NULL) {
177     fprintf(stderr, "Error! Cannot parse TIFF file\n");
178     return 0;
179   }
180 
181   dircount = TIFFNumberOfDirectories(tif);
182   if (dircount > 1) {
183     fprintf(stderr, "Warning: multi-directory TIFF files are not supported.\n"
184                     "Only the first will be used, %d will be ignored.\n",
185                     dircount - 1);
186   }
187   if (!TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &samples_per_px)) {
188     fprintf(stderr, "Error! Cannot retrieve TIFF samples-per-pixel info.\n");
189     goto End;
190   }
191   if (samples_per_px < 3 || samples_per_px > 4) goto End;  // not supported
192 
193   if (!(TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &image_width) &&
194         TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &image_height))) {
195     fprintf(stderr, "Error! Cannot retrieve TIFF image dimensions.\n");
196     goto End;
197   }
198   stride = (uint64_t)image_width * sizeof(*raster);
199   if (!ImgIoUtilCheckSizeArgumentsOverflow(stride, image_height)) {
200     fprintf(stderr, "Error! TIFF image dimension (%d x %d) is too large.\n",
201             image_width, image_height);
202     goto End;
203   }
204 
205   // According to spec, a tile can be bigger than the image. However it should
206   // be a multiple of 16 and not way too large, so check that it's not more
207   // than twice the image size, for dimensions above some arbitrary minimum
208   // 32. We also check that they respect WebP's dimension and memory limit.
209   // Note that a tile can be 6byte/px in some cases. Here we assume
210   // 4byte/px with sizeof(*raster), to be conservative.
211   if (TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tile_width) &&
212       TIFFGetField(tif, TIFFTAG_TILELENGTH, &tile_height)) {
213     if ((tile_width > 32 && tile_width / 2 > image_width) ||
214         (tile_height > 32 && tile_height / 2 > image_height) ||
215         ImgIoUtilCheckSizeArgumentsOverflow(
216             (uint64_t)tile_width * sizeof(*raster), tile_height)) {
217       fprintf(stderr, "Error! TIFF tile dimension (%d x %d) is too large.\n",
218               tile_width, tile_height);
219       goto End;
220     }
221   }
222 
223   if (samples_per_px > 3 && !TIFFGetField(tif, TIFFTAG_EXTRASAMPLES,
224                                           &extra_samples, &extra_samples_ptr)) {
225     fprintf(stderr, "Error! Cannot retrieve TIFF ExtraSamples info.\n");
226     goto End;
227   }
228 
229   // _Tiffmalloc uses a signed type for size.
230   alloc_size = (int64_t)(stride * image_height);
231   if (alloc_size < 0 || alloc_size != (tsize_t)alloc_size) goto End;
232 
233   raster = (uint32*)_TIFFmalloc((tsize_t)alloc_size);
234   if (raster != NULL) {
235     if (TIFFReadRGBAImageOriented(tif, image_width, image_height, raster,
236                                   ORIENTATION_TOPLEFT, 1)) {
237       pic->width = image_width;
238       pic->height = image_height;
239       // TIFF data is ABGR
240 #ifdef WORDS_BIGENDIAN
241       TIFFSwabArrayOfLong(raster, image_width * image_height);
242 #endif
243       // if we have an alpha channel, we must un-multiply from rgbA to RGBA
244       if (extra_samples == 1 && extra_samples_ptr != NULL &&
245           extra_samples_ptr[0] == EXTRASAMPLE_ASSOCALPHA) {
246         uint32_t y;
247         uint8_t* tmp = (uint8_t*)raster;
248         for (y = 0; y < image_height; ++y) {
249           MultARGBRow(tmp, image_width);
250           tmp += stride;
251         }
252       }
253       ok = keep_alpha
254          ? WebPPictureImportRGBA(pic, (const uint8_t*)raster, (int)stride)
255          : WebPPictureImportRGBX(pic, (const uint8_t*)raster, (int)stride);
256     }
257     _TIFFfree(raster);
258   } else {
259     fprintf(stderr, "Error allocating TIFF RGBA memory!\n");
260   }
261 
262   if (ok) {
263     if (metadata != NULL) {
264       ok = ExtractMetadataFromTIFF(tif, metadata);
265       if (!ok) {
266         fprintf(stderr, "Error extracting TIFF metadata!\n");
267         MetadataFree(metadata);
268         WebPPictureFree(pic);
269       }
270     }
271   }
272  End:
273   TIFFClose(tif);
274   return ok;
275 }
276 #else  // !WEBP_HAVE_TIFF
ReadTIFF(const uint8_t * const data,size_t data_size,struct WebPPicture * const pic,int keep_alpha,struct Metadata * const metadata)277 int ReadTIFF(const uint8_t* const data, size_t data_size,
278              struct WebPPicture* const pic, int keep_alpha,
279              struct Metadata* const metadata) {
280   (void)data;
281   (void)data_size;
282   (void)pic;
283   (void)keep_alpha;
284   (void)metadata;
285   fprintf(stderr, "TIFF support not compiled. Please install the libtiff "
286           "development package before building.\n");
287   return 0;
288 }
289 #endif  // WEBP_HAVE_TIFF
290 
291 // -----------------------------------------------------------------------------
292