Coverage Report

Created: 2026-05-30 06:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwebp/imageio/tiffdec.c
Line
Count
Source
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) { return (255u << MFIX) / a; }
132
133
#undef MFIX
134
#undef HALF
135
136
static void MultARGBRow(uint8_t* ptr, int width) {
137
  int x;
138
  for (x = 0; x < width; ++x, ptr += 4) {
139
    const uint32_t alpha = ptr[3];
140
    if (alpha < 255) {
141
      if (alpha == 0) {  // alpha == 0
142
        ptr[0] = ptr[1] = ptr[2] = 0;
143
      } else {
144
        const uint32_t scale = GetScale(alpha);
145
        ptr[0] = Unmult(ptr[0], scale);
146
        ptr[1] = Unmult(ptr[1], scale);
147
        ptr[2] = Unmult(ptr[2], scale);
148
      }
149
    }
150
  }
151
}
152
153
int ReadTIFF(const uint8_t* const data, size_t data_size,
154
             WebPPicture* const pic, int keep_alpha, Metadata* const metadata) {
155
  MyData my_data = {data, (toff_t)data_size, 0};
156
  TIFF* tif;
157
  uint32_t image_width, image_height, tile_width, tile_height;
158
  uint64_t stride;
159
  uint16_t samples_per_px = 0;
160
  uint16_t extra_samples = 0;
161
  uint16_t* extra_samples_ptr = NULL;
162
  uint32_t* raster;
163
  int64_t alloc_size;
164
  int ok = 0;
165
  tdir_t dircount;
166
167
  if (data == NULL || data_size == 0 || data_size > INT_MAX || pic == NULL) {
168
    return 0;
169
  }
170
171
  tif = TIFFClientOpen("Memory", "r", &my_data, MyRead, MyRead, MySeek, MyClose,
172
                       MySize, MyMapFile, MyUnmapFile);
173
  if (tif == NULL) {
174
    fprintf(stderr, "Error! Cannot parse TIFF file\n");
175
    return 0;
176
  }
177
178
  dircount = TIFFNumberOfDirectories(tif);
179
  if (dircount > 1) {
180
    fprintf(stderr,
181
            "Warning: multi-directory TIFF files are not supported.\n"
182
            "Only the first will be used, %d will be ignored.\n",
183
            dircount - 1);
184
  }
185
  if (!TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &samples_per_px)) {
186
    fprintf(stderr, "Error! Cannot retrieve TIFF samples-per-pixel info.\n");
187
    goto End;
188
  }
189
  if (!(samples_per_px == 1 || samples_per_px == 3 || samples_per_px == 4)) {
190
    goto End;  // not supported
191
  }
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_t*)_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 =
254
          keep_alpha
255
              ? WebPPictureImportRGBA(pic, (const uint8_t*)raster, (int)stride)
256
              : WebPPictureImportRGBX(pic, (const uint8_t*)raster, (int)stride);
257
    }
258
    _TIFFfree(raster);
259
  } else {
260
    fprintf(stderr, "Error allocating TIFF RGBA memory!\n");
261
  }
262
263
  if (ok) {
264
    if (metadata != NULL) {
265
      ok = ExtractMetadataFromTIFF(tif, metadata);
266
      if (!ok) {
267
        fprintf(stderr, "Error extracting TIFF metadata!\n");
268
        MetadataFree(metadata);
269
        WebPPictureFree(pic);
270
      }
271
    }
272
  }
273
End:
274
  TIFFClose(tif);
275
  return ok;
276
}
277
#else   // !WEBP_HAVE_TIFF
278
int ReadTIFF(const uint8_t* const data, size_t data_size,
279
             struct WebPPicture* const pic, int keep_alpha,
280
119
             struct Metadata* const metadata) {
281
119
  (void)data;
282
119
  (void)data_size;
283
119
  (void)pic;
284
119
  (void)keep_alpha;
285
119
  (void)metadata;
286
  fprintf(stderr,
287
119
          "TIFF support not compiled. Please install the libtiff "
288
119
          "development package before building.\n");
289
119
  return 0;
290
119
}
291
#endif  // WEBP_HAVE_TIFF
292
293
// -----------------------------------------------------------------------------