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