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 width, height;
160 uint16_t samples_per_px = 0;
161 uint16_t extra_samples = 0;
162 uint16_t* extra_samples_ptr = NULL;
163 uint32_t* raster;
164 int64_t alloc_size;
165 int ok = 0;
166 tdir_t dircount;
167
168 if (data == NULL || data_size == 0 || data_size > INT_MAX || pic == NULL) {
169 return 0;
170 }
171
172 tif = TIFFClientOpen("Memory", "r", &my_data,
173 MyRead, MyRead, MySeek, MyClose,
174 MySize, MyMapFile, MyUnmapFile);
175 if (tif == NULL) {
176 fprintf(stderr, "Error! Cannot parse TIFF file\n");
177 return 0;
178 }
179
180 dircount = TIFFNumberOfDirectories(tif);
181 if (dircount > 1) {
182 fprintf(stderr, "Warning: multi-directory TIFF files are not supported.\n"
183 "Only the first will be used, %d will be ignored.\n",
184 dircount - 1);
185 }
186 if (!TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &samples_per_px)) {
187 fprintf(stderr, "Error! Cannot retrieve TIFF samples-per-pixel info.\n");
188 goto End;
189 }
190 if (samples_per_px < 3 || samples_per_px > 4) goto End; // not supported
191
192 if (!(TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width) &&
193 TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height))) {
194 fprintf(stderr, "Error! Cannot retrieve TIFF image dimensions.\n");
195 goto End;
196 }
197 if (!ImgIoUtilCheckSizeArgumentsOverflow((uint64_t)width * height,
198 sizeof(*raster))) {
199 goto End;
200 }
201 if (samples_per_px > 3 && !TIFFGetField(tif, TIFFTAG_EXTRASAMPLES,
202 &extra_samples, &extra_samples_ptr)) {
203 fprintf(stderr, "Error! Cannot retrieve TIFF ExtraSamples info.\n");
204 goto End;
205 }
206
207 // _Tiffmalloc uses a signed type for size.
208 alloc_size = (int64_t)((uint64_t)width * height * sizeof(*raster));
209 if (alloc_size < 0 || alloc_size != (tsize_t)alloc_size) goto End;
210
211 raster = (uint32*)_TIFFmalloc((tsize_t)alloc_size);
212 if (raster != NULL) {
213 if (TIFFReadRGBAImageOriented(tif, width, height, raster,
214 ORIENTATION_TOPLEFT, 1)) {
215 const int stride = width * sizeof(*raster);
216 pic->width = width;
217 pic->height = height;
218 // TIFF data is ABGR
219 #ifdef WORDS_BIGENDIAN
220 TIFFSwabArrayOfLong(raster, width * height);
221 #endif
222 // if we have an alpha channel, we must un-multiply from rgbA to RGBA
223 if (extra_samples == 1 && extra_samples_ptr != NULL &&
224 extra_samples_ptr[0] == EXTRASAMPLE_ASSOCALPHA) {
225 uint32_t y;
226 uint8_t* tmp = (uint8_t*)raster;
227 for (y = 0; y < height; ++y) {
228 MultARGBRow(tmp, width);
229 tmp += stride;
230 }
231 }
232 ok = keep_alpha
233 ? WebPPictureImportRGBA(pic, (const uint8_t*)raster, stride)
234 : WebPPictureImportRGBX(pic, (const uint8_t*)raster, stride);
235 }
236 _TIFFfree(raster);
237 } else {
238 fprintf(stderr, "Error allocating TIFF RGBA memory!\n");
239 }
240
241 if (ok) {
242 if (metadata != NULL) {
243 ok = ExtractMetadataFromTIFF(tif, metadata);
244 if (!ok) {
245 fprintf(stderr, "Error extracting TIFF metadata!\n");
246 MetadataFree(metadata);
247 WebPPictureFree(pic);
248 }
249 }
250 }
251 End:
252 TIFFClose(tif);
253 return ok;
254 }
255 #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)256 int ReadTIFF(const uint8_t* const data, size_t data_size,
257 struct WebPPicture* const pic, int keep_alpha,
258 struct Metadata* const metadata) {
259 (void)data;
260 (void)data_size;
261 (void)pic;
262 (void)keep_alpha;
263 (void)metadata;
264 fprintf(stderr, "TIFF support not compiled. Please install the libtiff "
265 "development package before building.\n");
266 return 0;
267 }
268 #endif // WEBP_HAVE_TIFF
269
270 // -----------------------------------------------------------------------------
271