| // Copyright 2012 Google Inc. All Rights Reserved. |
| // |
| // Use of this source code is governed by a BSD-style license |
| // that can be found in the COPYING file in the root of the source |
| // tree. An additional intellectual property rights grant can be found |
| // in the file PATENTS. All contributing project authors may |
| // be found in the AUTHORS file in the root of the source tree. |
| // ----------------------------------------------------------------------------- |
| // |
| // TIFF decode. |
| |
| #include "./tiffdec.h" |
| |
| #ifdef HAVE_CONFIG_H |
| #include "webp/config.h" |
| #endif |
| |
| #include <limits.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| #ifdef WEBP_HAVE_TIFF |
| #include <tiffio.h> |
| |
| #include "webp/encode.h" |
| #include "./imageio_util.h" |
| #include "./metadata.h" |
| |
| static const struct { |
| ttag_t tag; |
| size_t storage_offset; |
| } kTIFFMetadataMap[] = { |
| { TIFFTAG_ICCPROFILE, METADATA_OFFSET(iccp) }, |
| { TIFFTAG_XMLPACKET, METADATA_OFFSET(xmp) }, |
| { 0, 0 }, |
| }; |
| |
| // Returns true on success. The caller must use MetadataFree() on 'metadata' in |
| // all cases. |
| static int ExtractMetadataFromTIFF(TIFF* const tif, Metadata* const metadata) { |
| int i; |
| toff_t exif_ifd_offset; |
| |
| for (i = 0; kTIFFMetadataMap[i].tag != 0; ++i) { |
| MetadataPayload* const payload = |
| (MetadataPayload*)((uint8_t*)metadata + |
| kTIFFMetadataMap[i].storage_offset); |
| void* tag_data; |
| uint32 tag_data_len; |
| |
| if (TIFFGetField(tif, kTIFFMetadataMap[i].tag, &tag_data_len, &tag_data) && |
| !MetadataCopy((const char*)tag_data, tag_data_len, payload)) { |
| return 0; |
| } |
| } |
| |
| // TODO(jzern): To extract the raw EXIF directory some parsing of it would be |
| // necessary to determine the overall size. In addition, value offsets in |
| // individual directory entries may need to be updated as, depending on the |
| // type, they are file based. |
| // Exif 2.2 Section 4.6.2 Tag Structure |
| // TIFF Revision 6.0 Part 1 Section 2 TIFF Structure #Image File Directory |
| if (TIFFGetField(tif, TIFFTAG_EXIFIFD, &exif_ifd_offset)) { |
| fprintf(stderr, "Warning: EXIF extraction from TIFF is unsupported.\n"); |
| } |
| return 1; |
| } |
| |
| // Ad-hoc structure to supply read-from-memory functionalities. |
| typedef struct { |
| const uint8_t* data; |
| toff_t size; |
| toff_t pos; |
| } MyData; |
| |
| static int MyClose(thandle_t opaque) { |
| (void)opaque; |
| return 0; |
| } |
| |
| static toff_t MySize(thandle_t opaque) { |
| const MyData* const my_data = (MyData*)opaque; |
| return my_data->size; |
| } |
| |
| static toff_t MySeek(thandle_t opaque, toff_t offset, int whence) { |
| MyData* const my_data = (MyData*)opaque; |
| offset += (whence == SEEK_CUR) ? my_data->pos |
| : (whence == SEEK_SET) ? 0 |
| : my_data->size; |
| if (offset > my_data->size) return (toff_t)-1; |
| my_data->pos = offset; |
| return offset; |
| } |
| |
| static int MyMapFile(thandle_t opaque, void** base, toff_t* size) { |
| (void)opaque; |
| (void)base; |
| (void)size; |
| return 0; |
| } |
| static void MyUnmapFile(thandle_t opaque, void* base, toff_t size) { |
| (void)opaque; |
| (void)base; |
| (void)size; |
| } |
| |
| static tsize_t MyRead(thandle_t opaque, void* dst, tsize_t size) { |
| MyData* const my_data = (MyData*)opaque; |
| if (my_data->pos + size > my_data->size) { |
| size = (tsize_t)(my_data->size - my_data->pos); |
| } |
| if (size > 0) { |
| memcpy(dst, my_data->data + my_data->pos, size); |
| my_data->pos += size; |
| } |
| return size; |
| } |
| |
| // Unmultiply Argb data. Taken from dsp/alpha_processing |
| // (we don't want to force a dependency to a libdspdec library). |
| #define MFIX 24 // 24bit fixed-point arithmetic |
| #define HALF ((1u << MFIX) >> 1) |
| #define KINV_255 ((1u << MFIX) / 255u) |
| |
| static uint32_t Unmult(uint8_t x, uint32_t mult) { |
| const uint32_t v = (x * mult + HALF) >> MFIX; |
| return (v > 255u) ? 255u : v; |
| } |
| |
| static WEBP_INLINE uint32_t GetScale(uint32_t a) { |
| return (255u << MFIX) / a; |
| } |
| |
| static void MultARGBRow(uint8_t* ptr, int width) { |
| int x; |
| for (x = 0; x < width; ++x, ptr += 4) { |
| const uint32_t alpha = ptr[3]; |
| if (alpha < 255) { |
| if (alpha == 0) { // alpha == 0 |
| ptr[0] = ptr[1] = ptr[2] = 0; |
| } else { |
| const uint32_t scale = GetScale(alpha); |
| ptr[0] = Unmult(ptr[0], scale); |
| ptr[1] = Unmult(ptr[1], scale); |
| ptr[2] = Unmult(ptr[2], scale); |
| } |
| } |
| } |
| } |
| |
| int ReadTIFF(const uint8_t* const data, size_t data_size, |
| WebPPicture* const pic, int keep_alpha, |
| Metadata* const metadata) { |
| MyData my_data = { data, (toff_t)data_size, 0 }; |
| TIFF* tif; |
| uint32_t width, height; |
| uint16_t samples_per_px = 0; |
| uint16_t extra_samples = 0; |
| uint16_t* extra_samples_ptr = NULL; |
| uint32_t* raster; |
| int64_t alloc_size; |
| int ok = 0; |
| tdir_t dircount; |
| |
| if (data == NULL || data_size == 0 || data_size > INT_MAX || pic == NULL) { |
| return 0; |
| } |
| |
| tif = TIFFClientOpen("Memory", "r", &my_data, |
| MyRead, MyRead, MySeek, MyClose, |
| MySize, MyMapFile, MyUnmapFile); |
| if (tif == NULL) { |
| fprintf(stderr, "Error! Cannot parse TIFF file\n"); |
| return 0; |
| } |
| |
| dircount = TIFFNumberOfDirectories(tif); |
| if (dircount > 1) { |
| fprintf(stderr, "Warning: multi-directory TIFF files are not supported.\n" |
| "Only the first will be used, %d will be ignored.\n", |
| dircount - 1); |
| } |
| if (!TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &samples_per_px)) { |
| fprintf(stderr, "Error! Cannot retrieve TIFF samples-per-pixel info.\n"); |
| goto End; |
| } |
| if (samples_per_px < 3 || samples_per_px > 4) goto End; // not supported |
| |
| if (!(TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width) && |
| TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height))) { |
| fprintf(stderr, "Error! Cannot retrieve TIFF image dimensions.\n"); |
| goto End; |
| } |
| if (!ImgIoUtilCheckSizeArgumentsOverflow((uint64_t)width * height, |
| sizeof(*raster))) { |
| goto End; |
| } |
| if (samples_per_px > 3 && !TIFFGetField(tif, TIFFTAG_EXTRASAMPLES, |
| &extra_samples, &extra_samples_ptr)) { |
| fprintf(stderr, "Error! Cannot retrieve TIFF ExtraSamples info.\n"); |
| goto End; |
| } |
| |
| // _Tiffmalloc uses a signed type for size. |
| alloc_size = (int64_t)((uint64_t)width * height * sizeof(*raster)); |
| if (alloc_size < 0 || alloc_size != (tsize_t)alloc_size) goto End; |
| |
| raster = (uint32*)_TIFFmalloc((tsize_t)alloc_size); |
| if (raster != NULL) { |
| if (TIFFReadRGBAImageOriented(tif, width, height, raster, |
| ORIENTATION_TOPLEFT, 1)) { |
| const int stride = width * sizeof(*raster); |
| pic->width = width; |
| pic->height = height; |
| // TIFF data is ABGR |
| #ifdef WORDS_BIGENDIAN |
| TIFFSwabArrayOfLong(raster, width * height); |
| #endif |
| // if we have an alpha channel, we must un-multiply from rgbA to RGBA |
| if (extra_samples == 1 && extra_samples_ptr != NULL && |
| extra_samples_ptr[0] == EXTRASAMPLE_ASSOCALPHA) { |
| uint32_t y; |
| uint8_t* tmp = (uint8_t*)raster; |
| for (y = 0; y < height; ++y) { |
| MultARGBRow(tmp, width); |
| tmp += stride; |
| } |
| } |
| ok = keep_alpha |
| ? WebPPictureImportRGBA(pic, (const uint8_t*)raster, stride) |
| : WebPPictureImportRGBX(pic, (const uint8_t*)raster, stride); |
| } |
| _TIFFfree(raster); |
| } else { |
| fprintf(stderr, "Error allocating TIFF RGBA memory!\n"); |
| } |
| |
| if (ok) { |
| if (metadata != NULL) { |
| ok = ExtractMetadataFromTIFF(tif, metadata); |
| if (!ok) { |
| fprintf(stderr, "Error extracting TIFF metadata!\n"); |
| MetadataFree(metadata); |
| WebPPictureFree(pic); |
| } |
| } |
| } |
| End: |
| TIFFClose(tif); |
| return ok; |
| } |
| #else // !WEBP_HAVE_TIFF |
| int ReadTIFF(const uint8_t* const data, size_t data_size, |
| struct WebPPicture* const pic, int keep_alpha, |
| struct Metadata* const metadata) { |
| (void)data; |
| (void)data_size; |
| (void)pic; |
| (void)keep_alpha; |
| (void)metadata; |
| fprintf(stderr, "TIFF support not compiled. Please install the libtiff " |
| "development package before building.\n"); |
| return 0; |
| } |
| #endif // WEBP_HAVE_TIFF |
| |
| // ----------------------------------------------------------------------------- |