2012-12-06 23:04:36 +01:00
|
|
|
// Copyright 2012 Google Inc. All Rights Reserved.
|
|
|
|
//
|
2013-06-07 08:05:58 +02:00
|
|
|
// 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.
|
2012-12-06 23:04:36 +01:00
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// TIFF decode.
|
|
|
|
|
|
|
|
#include "./tiffdec.h"
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
2014-06-11 02:53:44 +02:00
|
|
|
#include "webp/config.h"
|
2012-12-06 23:04:36 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <stdio.h>
|
2015-12-08 08:22:41 +01:00
|
|
|
#include <string.h>
|
2012-12-06 23:04:36 +01:00
|
|
|
|
|
|
|
#ifdef WEBP_HAVE_TIFF
|
|
|
|
#include <tiffio.h>
|
|
|
|
|
|
|
|
#include "webp/encode.h"
|
2016-10-07 22:18:29 +02:00
|
|
|
#include "./imageio_util.h"
|
2012-12-16 04:41:03 +01:00
|
|
|
#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;
|
2013-01-11 06:33:24 +01:00
|
|
|
toff_t exif_ifd_offset;
|
2012-12-16 04:41:03 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2012-12-06 23:04:36 +01:00
|
|
|
|
2015-12-08 08:22:41 +01:00
|
|
|
// 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 = my_data->size - my_data->pos;
|
|
|
|
}
|
|
|
|
if (size > 0) {
|
|
|
|
memcpy(dst, my_data->data + my_data->pos, size);
|
|
|
|
my_data->pos += size;
|
|
|
|
}
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2017-02-25 18:10:38 +01:00
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-08 08:22:41 +01:00
|
|
|
int ReadTIFF(const uint8_t* const data, size_t data_size,
|
2012-12-16 04:41:03 +01:00
|
|
|
WebPPicture* const pic, int keep_alpha,
|
|
|
|
Metadata* const metadata) {
|
2015-12-08 08:22:41 +01:00
|
|
|
MyData my_data = { data, (toff_t)data_size, 0 };
|
2016-10-13 05:43:50 +02:00
|
|
|
TIFF* tif;
|
2017-02-25 18:10:38 +01:00
|
|
|
uint32_t width, height;
|
|
|
|
uint16_t samples_per_px = 0;
|
|
|
|
uint16_t extra_samples = 0;
|
|
|
|
uint16_t* extra_samples_ptr = NULL;
|
|
|
|
uint32_t* raster;
|
2016-10-07 22:18:29 +02:00
|
|
|
int64_t alloc_size;
|
2012-12-06 23:04:36 +01:00
|
|
|
int ok = 0;
|
2012-12-16 03:43:36 +01:00
|
|
|
tdir_t dircount;
|
2012-12-06 23:04:36 +01:00
|
|
|
|
2016-10-13 05:43:50 +02:00
|
|
|
if (data == NULL || data_size == 0 || pic == NULL) return 0;
|
|
|
|
|
|
|
|
tif = TIFFClientOpen("Memory", "r", &my_data,
|
|
|
|
MyRead, MyRead, MySeek, MyClose,
|
|
|
|
MySize, MyMapFile, MyUnmapFile);
|
2012-12-06 23:04:36 +01:00
|
|
|
if (tif == NULL) {
|
2015-12-08 08:22:41 +01:00
|
|
|
fprintf(stderr, "Error! Cannot parse TIFF file\n");
|
2012-12-06 23:04:36 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-12-16 03:43:36 +01:00
|
|
|
dircount = TIFFNumberOfDirectories(tif);
|
2012-12-06 23:04:36 +01:00
|
|
|
if (dircount > 1) {
|
|
|
|
fprintf(stderr, "Warning: multi-directory TIFF files are not supported.\n"
|
2013-01-08 04:06:20 +01:00
|
|
|
"Only the first will be used, %d will be ignored.\n",
|
2012-12-06 23:04:36 +01:00
|
|
|
dircount - 1);
|
|
|
|
}
|
2017-02-25 18:10:38 +01:00
|
|
|
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
|
2012-12-06 23:04:36 +01:00
|
|
|
|
2012-12-21 07:33:52 +01:00
|
|
|
if (!(TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width) &&
|
|
|
|
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height))) {
|
|
|
|
fprintf(stderr, "Error! Cannot retrieve TIFF image dimensions.\n");
|
2015-12-08 08:22:41 +01:00
|
|
|
goto End;
|
2012-12-21 07:33:52 +01:00
|
|
|
}
|
2016-10-07 22:18:29 +02:00
|
|
|
if (!ImgIoUtilCheckSizeArgumentsOverflow((uint64_t)width * height,
|
|
|
|
sizeof(*raster))) {
|
|
|
|
goto End;
|
|
|
|
}
|
2017-02-25 18:10:38 +01:00
|
|
|
if (!TIFFGetField(tif, TIFFTAG_EXTRASAMPLES,
|
|
|
|
&extra_samples, &extra_samples_ptr)) {
|
|
|
|
fprintf(stderr, "Error! Cannot retrieve TIFF ExtraSamples info.\n");
|
|
|
|
goto End;
|
|
|
|
}
|
|
|
|
|
2016-10-07 22:18:29 +02:00
|
|
|
// _Tiffmalloc uses a signed type for size.
|
|
|
|
alloc_size = (int64_t)((uint64_t)width * height * sizeof(*raster));
|
2016-12-14 07:50:28 +01:00
|
|
|
if (alloc_size < 0 || alloc_size != (tsize_t)alloc_size) goto End;
|
2016-10-07 22:18:29 +02:00
|
|
|
|
2016-12-14 07:50:28 +01:00
|
|
|
raster = (uint32*)_TIFFmalloc((tsize_t)alloc_size);
|
2012-12-06 23:04:36 +01:00
|
|
|
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
|
2014-06-28 21:27:17 +02:00
|
|
|
#ifdef WORDS_BIGENDIAN
|
2012-12-06 23:04:36 +01:00
|
|
|
TIFFSwabArrayOfLong(raster, width * height);
|
|
|
|
#endif
|
2017-02-25 18:10:38 +01:00
|
|
|
// if we have an alpha channel, we must un-multiply from rgbA to RGBA
|
|
|
|
if (samples_per_px > 3 && extra_samples == 1) {
|
|
|
|
uint32_t y;
|
|
|
|
uint8_t* tmp = (uint8_t*)raster;
|
|
|
|
for (y = 0; y < height; ++y) {
|
|
|
|
MultARGBRow(tmp, width);
|
|
|
|
tmp += stride;
|
|
|
|
}
|
|
|
|
}
|
2012-12-06 23:04:36 +01:00
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
2012-12-16 04:41:03 +01:00
|
|
|
if (ok) {
|
|
|
|
if (metadata != NULL) {
|
|
|
|
ok = ExtractMetadataFromTIFF(tif, metadata);
|
|
|
|
if (!ok) {
|
|
|
|
fprintf(stderr, "Error extracting TIFF metadata!\n");
|
|
|
|
MetadataFree(metadata);
|
|
|
|
WebPPictureFree(pic);
|
|
|
|
}
|
|
|
|
}
|
2012-12-06 23:04:36 +01:00
|
|
|
}
|
2015-12-08 08:22:41 +01:00
|
|
|
End:
|
2012-12-06 23:04:36 +01:00
|
|
|
TIFFClose(tif);
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
#else // !WEBP_HAVE_TIFF
|
2015-12-08 08:22:41 +01:00
|
|
|
int ReadTIFF(const uint8_t* const data, size_t data_size,
|
2012-12-16 04:41:03 +01:00
|
|
|
struct WebPPicture* const pic, int keep_alpha,
|
|
|
|
struct Metadata* const metadata) {
|
2015-12-08 08:22:41 +01:00
|
|
|
(void)data;
|
|
|
|
(void)data_size;
|
2012-12-06 23:04:36 +01:00
|
|
|
(void)pic;
|
|
|
|
(void)keep_alpha;
|
2012-12-16 04:41:03 +01:00
|
|
|
(void)metadata;
|
2012-12-06 23:04:36 +01:00
|
|
|
fprintf(stderr, "TIFF support not compiled. Please install the libtiff "
|
|
|
|
"development package before building.\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif // WEBP_HAVE_TIFF
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|