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