Blame imageio/tiffdec.c

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