int ReadTIFF()

in Extended/libwebp/imageio/tiffdec.c [154:265]


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 image_width, image_height, tile_width, tile_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, &image_width) &&
        TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &image_height))) {
    fprintf(stderr, "Error! Cannot retrieve TIFF image dimensions.\n");
    goto End;
  }
  if (!ImgIoUtilCheckSizeArgumentsOverflow((uint64_t)image_width * image_height,
                                           sizeof(*raster))) {
    goto End;
  }
  // According to spec, a tile can be bigger than the image. However it should
  // be a multiple of 16 and not way too large, so check that it's not more than
  // twice the image size, for dimensions above some arbitrary minimum 32.
  if ((TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tile_width) &&
       tile_width > 32 && tile_width / 2 > image_width) ||
      (TIFFGetField(tif, TIFFTAG_TILELENGTH, &tile_height) &&
       tile_height > 32 && tile_height / 2 > image_height)) {
    fprintf(stderr, "Error! TIFF tile dimensions are too big.\n");
    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)image_width * image_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, image_width, image_height, raster,
                                  ORIENTATION_TOPLEFT, 1)) {
      const int stride = image_width * sizeof(*raster);
      pic->width = image_width;
      pic->height = image_height;
      // TIFF data is ABGR
#ifdef WORDS_BIGENDIAN
      TIFFSwabArrayOfLong(raster, image_width * image_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 < image_height; ++y) {
          MultARGBRow(tmp, image_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;
}