tiffdec.c (9324B)
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 struct Metadata* const metadata) { 283 (void)data; 284 (void)data_size; 285 (void)pic; 286 (void)keep_alpha; 287 (void)metadata; 288 fprintf(stderr, "TIFF support not compiled. Please install the libtiff " 289 "development package before building.\n"); 290 return 0; 291 } 292 #endif // WEBP_HAVE_TIFF 293 294 // -----------------------------------------------------------------------------