go-libwebp

Experimental translation from libwebp to Go source.
Log | Files | Refs | README | LICENSE

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 // -----------------------------------------------------------------------------