singe/thirdparty/SDL2_image/external/libtiff/tools/tiffcp.c
2023-10-23 19:38:18 -05:00

2276 lines
72 KiB
C

/*
* Copyright (c) 1988-1997 Sam Leffler
* Copyright (c) 1991-1997 Silicon Graphics, Inc.
*
* Revised: 2/18/01 BAR -- added syntax for extracting single images from
* multi-image TIFF files.
*
* New syntax is: sourceFileName,image#
*
* image# ranges from 0..<n-1> where n is the # of images in the file.
* There may be no white space between the comma and the filename or
* image number.
*
* Example: tiffcp source.tif,1 destination.tif
*
* Copies the 2nd image in source.tif to the destination.
*
*****
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
* that (i) the above copyright notices and this permission notice appear in
* all copies of the software and related documentation, and (ii) the names of
* Sam Leffler and Silicon Graphics may not be used in any advertising or
* publicity relating to the software without the specific, prior written
* permission of Sam Leffler and Silicon Graphics.
*
* THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
*
* IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
* ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
#include "libport.h"
#include "tif_config.h"
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "tiffio.h"
#ifndef EXIT_SUCCESS
#define EXIT_SUCCESS 0
#endif
#ifndef EXIT_FAILURE
#define EXIT_FAILURE 1
#endif
#define streq(a, b) (strcmp(a, b) == 0)
#define strneq(a, b, n) (strncmp(a, b, n) == 0)
#define TRUE 1
#define FALSE 0
#define DEFAULT_MAX_MALLOC (256 * 1024 * 1024)
/* malloc size limit (in bytes)
* disabled when set to 0 */
static tmsize_t maxMalloc = DEFAULT_MAX_MALLOC;
static int outtiled = -1;
static uint32_t tilewidth;
static uint32_t tilelength;
static uint16_t config;
static uint16_t compression;
static double max_z_error = 0.0;
static uint16_t predictor;
static int preset;
static uint16_t fillorder;
static uint16_t orientation;
static uint32_t rowsperstrip;
static uint32_t g3opts;
static int ignore = FALSE; /* if true, ignore read errors */
static uint32_t defg3opts = (uint32_t)-1;
static int quality = 75; /* JPEG quality */
static int jpegcolormode = JPEGCOLORMODE_RGB;
static uint16_t defcompression = (uint16_t)-1;
static uint16_t defpredictor = (uint16_t)-1;
static int defpreset = -1;
static int subcodec = -1;
static int tiffcp(TIFF *, TIFF *);
static int processCompressOptions(char *);
static void usage(int code);
static char comma = ','; /* (default) comma separator character */
static TIFF *bias = NULL;
static int pageNum = 0;
static int pageInSeq = 0;
/**
* This custom malloc function enforce a maximum allocation size
*/
static void *limitMalloc(tmsize_t s)
{
if (maxMalloc && (s > maxMalloc))
{
fprintf(stderr,
"MemoryLimitError: allocation of %" TIFF_SSIZE_FORMAT
" bytes is forbidden. Limit is %" TIFF_SSIZE_FORMAT ".\n",
s, maxMalloc);
fprintf(stderr, " use -m option to change limit.\n");
return NULL;
}
return _TIFFmalloc(s);
}
static int nextSrcImage(TIFF *tif, char **imageSpec)
/*
seek to the next image specified in *imageSpec
returns 1 if success, 0 if no more images to process
*imageSpec=NULL if subsequent images should be processed in sequence
*/
{
if (**imageSpec == comma)
{ /* if not @comma, we've done all images */
char *start = *imageSpec + 1;
tdir_t nextImage = (tdir_t)strtol(start, imageSpec, 0);
if (start == *imageSpec)
nextImage = TIFFCurrentDirectory(tif);
if (**imageSpec)
{
if (**imageSpec == comma)
{
/* a trailing comma denotes remaining images in sequence */
if ((*imageSpec)[1] == '\0')
*imageSpec = NULL;
}
else
{
fprintf(stderr,
"Expected a %c separated image # list after %s\n",
comma, TIFFFileName(tif));
exit(EXIT_FAILURE); /* syntax error */
}
}
if (TIFFSetDirectory(tif, nextImage))
return 1;
fprintf(stderr, "%s%c%" PRIu16 " not found!\n", TIFFFileName(tif),
comma, nextImage);
}
return 0;
}
static TIFF *openSrcImage(char **imageSpec)
/*
imageSpec points to a pointer to a filename followed by optional ,image#'s
Open the TIFF file and assign *imageSpec to either NULL if there are
no images specified, or a pointer to the next image number text
*/
{
/* disable strip shopping when using jbig compression */
const char *mode = (defcompression == COMPRESSION_JBIG) ? "rc" : "r";
TIFF *tif;
char *fn = *imageSpec;
*imageSpec = strchr(fn, comma);
TIFFOpenOptions *opts = TIFFOpenOptionsAlloc();
if (opts == NULL)
{
return NULL;
}
TIFFOpenOptionsSetMaxSingleMemAlloc(opts, maxMalloc);
if (*imageSpec)
{ /* there is at least one image number specifier */
**imageSpec = '\0';
tif = TIFFOpenExt(fn, mode, opts);
/* but, ignore any single trailing comma */
if (!(*imageSpec)[1])
{
*imageSpec = NULL;
TIFFOpenOptionsFree(opts);
return tif;
}
if (tif)
{
**imageSpec = comma; /* replace the comma */
if (!nextSrcImage(tif, imageSpec))
{
TIFFClose(tif);
tif = NULL;
}
}
}
else
tif = TIFFOpenExt(fn, mode, opts);
TIFFOpenOptionsFree(opts);
return tif;
}
int main(int argc, char *argv[])
{
uint16_t defconfig = (uint16_t)-1;
uint16_t deffillorder = 0;
uint32_t deftilewidth = (uint32_t)-1;
uint32_t deftilelength = (uint32_t)-1;
uint32_t defrowsperstrip = (uint32_t)0;
uint64_t diroff = 0;
TIFF *in;
TIFF *out;
char mode[10];
char *mp = mode;
int c;
#if !HAVE_DECL_OPTARG
extern int optind;
extern char *optarg;
#endif
*mp++ = 'w';
*mp = '\0';
while ((c = getopt(argc, argv, "m:,:b:c:f:l:o:p:r:w:aistBLMC8xh")) != -1)
switch (c)
{
case 'm':
maxMalloc = (tmsize_t)strtoul(optarg, NULL, 0) << 20;
break;
case ',':
if (optarg[0] != '=')
usage(EXIT_FAILURE);
comma = optarg[1];
break;
case 'b': /* this file is bias image subtracted from others */
if (bias)
{
fputs("Only 1 bias image may be specified\n", stderr);
exit(EXIT_FAILURE);
}
{
uint16_t samples = (uint16_t)-1;
char **biasFn = &optarg;
bias = openSrcImage(biasFn);
if (!bias)
exit(EXIT_FAILURE);
if (TIFFIsTiled(bias))
{
fputs("Bias image must be organized in strips\n",
stderr);
exit(EXIT_FAILURE);
}
TIFFGetField(bias, TIFFTAG_SAMPLESPERPIXEL, &samples);
if (samples != 1)
{
fputs("Bias image must be monochrome\n", stderr);
exit(EXIT_FAILURE);
}
}
break;
case 'a': /* append to output */
mode[0] = 'a';
break;
case 'c': /* compression scheme */
if (!processCompressOptions(optarg))
usage(EXIT_FAILURE);
break;
case 'f': /* fill order */
if (streq(optarg, "lsb2msb"))
deffillorder = FILLORDER_LSB2MSB;
else if (streq(optarg, "msb2lsb"))
deffillorder = FILLORDER_MSB2LSB;
else
usage(EXIT_FAILURE);
break;
case 'i': /* ignore errors */
ignore = TRUE;
break;
case 'l': /* tile length */
outtiled = TRUE;
deftilelength = atoi(optarg);
break;
case 'o': /* initial directory offset */
diroff = strtoul(optarg, NULL, 0);
break;
case 'p': /* planar configuration */
if (streq(optarg, "separate"))
defconfig = PLANARCONFIG_SEPARATE;
else if (streq(optarg, "contig"))
defconfig = PLANARCONFIG_CONTIG;
else
usage(EXIT_FAILURE);
break;
case 'r': /* rows/strip */
defrowsperstrip = atol(optarg);
break;
case 's': /* generate stripped output */
outtiled = FALSE;
break;
case 't': /* generate tiled output */
outtiled = TRUE;
break;
case 'w': /* tile width */
outtiled = TRUE;
deftilewidth = atoi(optarg);
break;
case 'B':
if (strlen(mode) < (sizeof(mode) - 1))
{
*mp++ = 'b';
*mp = '\0';
}
break;
case 'L':
if (strlen(mode) < (sizeof(mode) - 1))
{
*mp++ = 'l';
*mp = '\0';
}
break;
case 'M':
if (strlen(mode) < (sizeof(mode) - 1))
{
*mp++ = 'm';
*mp = '\0';
}
break;
case 'C':
if (strlen(mode) < (sizeof(mode) - 1))
{
*mp++ = 'c';
*mp = '\0';
}
break;
case '8':
if (strlen(mode) < (sizeof(mode) - 1))
{
*mp++ = '8';
*mp = '\0';
}
break;
case 'x':
pageInSeq = 1;
break;
case 'h':
usage(EXIT_SUCCESS);
/*NOTREACHED*/
break;
case '?':
usage(EXIT_FAILURE);
/*NOTREACHED*/
break;
}
if (argc - optind < 2)
usage(EXIT_FAILURE);
TIFFOpenOptions *opts = TIFFOpenOptionsAlloc();
if (opts == NULL)
{
return EXIT_FAILURE;
}
TIFFOpenOptionsSetMaxSingleMemAlloc(opts, maxMalloc);
out = TIFFOpenExt(argv[argc - 1], mode, opts);
TIFFOpenOptionsFree(opts);
if (out == NULL)
return (EXIT_FAILURE);
if ((argc - optind) == 2)
pageNum = -1;
for (; optind < argc - 1; optind++)
{
char *imageCursor = argv[optind];
in = openSrcImage(&imageCursor);
if (in == NULL)
{
(void)TIFFClose(out);
return (EXIT_FAILURE);
}
if (diroff != 0 && !TIFFSetSubDirectory(in, diroff))
{
TIFFError(TIFFFileName(in),
"Error, setting subdirectory at %" PRIu64, diroff);
(void)TIFFClose(in);
(void)TIFFClose(out);
return (EXIT_FAILURE);
}
for (;;)
{
config = defconfig;
compression = defcompression;
predictor = defpredictor;
preset = defpreset;
fillorder = deffillorder;
rowsperstrip = defrowsperstrip;
tilewidth = deftilewidth;
tilelength = deftilelength;
g3opts = defg3opts;
if (!tiffcp(in, out) || !TIFFWriteDirectory(out))
{
(void)TIFFClose(in);
(void)TIFFClose(out);
return (EXIT_FAILURE);
}
if (imageCursor)
{ /* seek next image directory */
if (!nextSrcImage(in, &imageCursor))
break;
}
else if (!TIFFReadDirectory(in))
break;
}
(void)TIFFClose(in);
}
(void)TIFFClose(out);
return (EXIT_SUCCESS);
}
static void processZIPOptions(char *cp)
{
if ((cp = strchr(cp, ':')))
{
do
{
cp++;
if (isdigit((int)*cp))
defpredictor = atoi(cp);
else if (*cp == 'p')
defpreset = atoi(++cp);
else if (*cp == 's')
subcodec = atoi(++cp);
else
usage(EXIT_FAILURE);
} while ((cp = strchr(cp, ':')));
}
}
static void processLERCOptions(char *cp)
{
if ((cp = strchr(cp, ':')))
{
do
{
cp++;
if (isdigit((int)*cp))
max_z_error = atof(cp);
else if (*cp == 's')
subcodec = atoi(++cp);
else if (*cp == 'p')
defpreset = atoi(++cp);
else
usage(EXIT_FAILURE);
} while ((cp = strchr(cp, ':')));
}
}
static void processG3Options(char *cp)
{
if ((cp = strchr(cp, ':')))
{
if (defg3opts == (uint32_t)-1)
defg3opts = 0;
do
{
cp++;
if (strneq(cp, "1d", 2))
defg3opts &= ~GROUP3OPT_2DENCODING;
else if (strneq(cp, "2d", 2))
defg3opts |= GROUP3OPT_2DENCODING;
else if (strneq(cp, "fill", 4))
defg3opts |= GROUP3OPT_FILLBITS;
else
usage(EXIT_FAILURE);
} while ((cp = strchr(cp, ':')));
}
}
static int processCompressOptions(char *opt)
{
if (streq(opt, "none"))
{
defcompression = COMPRESSION_NONE;
}
else if (streq(opt, "packbits"))
{
defcompression = COMPRESSION_PACKBITS;
}
else if (strneq(opt, "jpeg", 4))
{
char *cp = strchr(opt, ':');
defcompression = COMPRESSION_JPEG;
while (cp)
{
if (isdigit((int)cp[1]))
quality = atoi(cp + 1);
else if (cp[1] == 'r')
jpegcolormode = JPEGCOLORMODE_RAW;
else
usage(EXIT_FAILURE);
cp = strchr(cp + 1, ':');
}
}
else if (strneq(opt, "g3", 2))
{
processG3Options(opt);
defcompression = COMPRESSION_CCITTFAX3;
}
else if (streq(opt, "g4"))
{
defcompression = COMPRESSION_CCITTFAX4;
}
else if (strneq(opt, "lzw", 3))
{
char *cp = strchr(opt, ':');
if (cp)
defpredictor = atoi(cp + 1);
defcompression = COMPRESSION_LZW;
}
else if (strneq(opt, "zip", 3))
{
processZIPOptions(opt);
defcompression = COMPRESSION_ADOBE_DEFLATE;
}
else if (strneq(opt, "lerc", 4))
{
processLERCOptions(opt);
defcompression = COMPRESSION_LERC;
}
else if (strneq(opt, "lzma", 4))
{
processZIPOptions(opt);
defcompression = COMPRESSION_LZMA;
}
else if (strneq(opt, "zstd", 4))
{
processZIPOptions(opt);
defcompression = COMPRESSION_ZSTD;
}
else if (strneq(opt, "webp", 4))
{
processZIPOptions(opt);
defcompression = COMPRESSION_WEBP;
}
else if (strneq(opt, "jbig", 4))
{
defcompression = COMPRESSION_JBIG;
}
else if (strneq(opt, "sgilog", 6))
{
defcompression = COMPRESSION_SGILOG;
}
else
return (0);
return (1);
}
static const char usage_info[] =
"Copy, convert, or combine TIFF files\n\n"
"usage: tiffcp [options] input... output\n"
"where options are:\n"
" -a append to output instead of overwriting\n"
" -o offset set initial directory offset\n"
" -p contig pack samples contiguously (e.g. RGBRGB...)\n"
" -p separate store samples separately (e.g. RRR...GGG...BBB...)\n"
" -s write output in strips\n"
" -t write output in tiles\n"
" -x force the merged tiff pages in sequence\n"
" -8 write BigTIFF instead of default ClassicTIFF\n"
" -B write big-endian instead of native byte order\n"
" -L write little-endian instead of native byte order\n"
" -M disable use of memory-mapped files\n"
" -C disable strip chopping\n"
" -i ignore read errors\n"
" -b file[,#] bias (dark) monochrome image to be subtracted from all "
"others\n"
" -,=% use % rather than , to separate image #'s (per Note "
"below)\n"
" -m size set maximum memory allocation size (MiB). 0 to disable "
"limit.\n"
"\n"
" -r # make each strip have no more than # rows\n"
" -w # set output tile width (pixels)\n"
" -l # set output tile length (pixels)\n"
"\n"
" -f lsb2msb force lsb-to-msb FillOrder for output\n"
" -f msb2lsb force msb-to-lsb FillOrder for output\n"
"\n"
#ifdef LZW_SUPPORT
" -c lzw[:opts] compress output with Lempel-Ziv & Welch encoding\n"
/* " LZW options:" */
" # set predictor value\n"
" For example, -c lzw:2 for LZW-encoded data with horizontal "
"differencing\n"
#endif
#ifdef ZIP_SUPPORT
" -c zip[:opts] compress output with deflate encoding\n"
/* " Deflate (ZIP) options:", */
" # set predictor value\n"
" p# set compression level (preset)\n"
" For example, -c zip:3:p9 for maximum compression level and floating\n"
" point predictor.\n"
#endif
#if defined(ZIP_SUPPORT) && defined(LIBDEFLATE_SUPPORT)
" s# set subcodec: 0=zlib, 1=libdeflate (default 1)\n"
/* " (only for Deflate/ZIP)", */
#endif
#ifdef LERC_SUPPORT
" -c lerc[:opts] compress output with LERC encoding\n"
/* " LERC options:", */
" # set max_z_error value\n"
" p# set compression level (preset)\n"
#ifdef ZSTD_SUPPORT
" s# set subcodec: 0=none, 1=deflate, 2=zstd (default 0)\n"
" For example, -c lerc:0.5:s2:p22 for max_z_error 0.5,\n"
" zstd additional compression with maximum compression level.\n"
#else
" s# set subcodec: 0=none, 1=deflate (default 0)\n"
" For example, -c lerc:0.5:s1:p12 for max_z_error 0.5,\n"
" deflate additional compression with maximum compression level.\n"
#endif
#endif
#ifdef LZMA_SUPPORT
" -c lzma[:opts] compress output with LZMA2 encoding\n"
/* " LZMA options:", */
" # set predictor value\n"
" p# set compression level (preset)\n"
#endif
#ifdef ZSTD_SUPPORT
" -c zstd[:opts] compress output with ZSTD encoding\n"
/* " ZSTD options:", */
" # set predictor value\n"
" p# set compression level (preset)\n"
#endif
#ifdef WEBP_SUPPORT
" -c webp[:opts] compress output with WEBP encoding\n"
/* " WEBP options:", */
" # set predictor value\n"
" p# set compression level (preset)\n"
#endif
#ifdef JPEG_SUPPORT
" -c jpeg[:opts] compress output with JPEG encoding\n"
/* " JPEG options:", */
" # set compression quality level (0-100, default 75)\n"
" r output color image as RGB rather than YCbCr\n"
" For example, -c jpeg:r:50 for JPEG-encoded RGB with 50% comp. "
"quality\n"
#endif
#ifdef JBIG_SUPPORT
" -c jbig compress output with ISO JBIG encoding\n"
#endif
#ifdef PACKBITS_SUPPORT
" -c packbits compress output with packbits encoding\n"
#endif
#ifdef CCITT_SUPPORT
" -c g3[:opts] compress output with CCITT Group 3 encoding\n"
/* " CCITT Group 3 options:", */
" 1d use default CCITT Group 3 1D-encoding\n"
" 2d use optional CCITT Group 3 2D-encoding\n"
" fill byte-align EOL codes\n"
" For example, -c g3:2d:fill for G3-2D-encoded data with byte-aligned "
"EOLs\n"
" -c g4 compress output with CCITT Group 4 encoding\n"
#endif
#ifdef LOGLUV_SUPPORT
" -c sgilog compress output with SGILOG encoding\n"
#endif
#if defined(LZW_SUPPORT) || defined(ZIP_SUPPORT) || defined(LZMA_SUPPORT) || \
defined(ZSTD_SUPPORT) || defined(WEBP_SUPPORT) || defined(JPEG_SUPPORT) || \
defined(JBIG_SUPPORT) || defined(PACKBITS_SUPPORT) || \
defined(CCITT_SUPPORT) || defined(LOGLUV_SUPPORT) || defined(LERC_SUPPORT)
" -c none use no compression algorithm on output\n"
#endif
"\n"
"Note that input filenames may be of the form filename,x,y,z\n"
"where x, y, and z specify image numbers in the filename to copy.\n"
"example: tiffcp -b esp.tif,1 esp.tif,0 test.tif\n"
" subtract 2nd image in esp.tif from 1st yielding result test.tif\n";
static void usage(int code)
{
FILE *out = (code == EXIT_SUCCESS) ? stdout : stderr;
fprintf(out, "%s\n\n", TIFFGetVersion());
fprintf(out, "%s", usage_info);
exit(code);
}
#define CopyField(tag, v) \
do \
{ \
if (TIFFGetField(in, tag, &v)) \
TIFFSetField(out, tag, v); \
} while (0)
#define CopyField2(tag, v1, v2) \
do \
{ \
if (TIFFGetField(in, tag, &v1, &v2)) \
TIFFSetField(out, tag, v1, v2); \
} while (0)
#define CopyField3(tag, v1, v2, v3) \
do \
{ \
if (TIFFGetField(in, tag, &v1, &v2, &v3)) \
TIFFSetField(out, tag, v1, v2, v3); \
} while (0)
#define CopyField4(tag, v1, v2, v3, v4) \
do \
{ \
if (TIFFGetField(in, tag, &v1, &v2, &v3, &v4)) \
TIFFSetField(out, tag, v1, v2, v3, v4); \
} while (0)
static void cpTag(TIFF *in, TIFF *out, uint16_t tag, uint16_t count,
TIFFDataType type)
{
switch (type)
{
case TIFF_SHORT:
if (count == 1)
{
uint16_t shortv;
CopyField(tag, shortv);
}
else if (count == 2)
{
uint16_t shortv1, shortv2;
CopyField2(tag, shortv1, shortv2);
}
else if (count == 4)
{
uint16_t *tr, *tg, *tb, *ta;
CopyField4(tag, tr, tg, tb, ta);
}
else if (count == (uint16_t)-1)
{
uint16_t shortv1;
uint16_t *shortav;
CopyField2(tag, shortv1, shortav);
}
break;
case TIFF_LONG:
{
uint32_t longv;
CopyField(tag, longv);
}
break;
case TIFF_RATIONAL:
if (count == 1)
{
float floatv;
CopyField(tag, floatv);
}
else if (count == (uint16_t)-1)
{
float *floatav;
CopyField(tag, floatav);
}
break;
case TIFF_ASCII:
{
char *stringv;
CopyField(tag, stringv);
}
break;
case TIFF_DOUBLE:
if (count == 1)
{
double doublev;
CopyField(tag, doublev);
}
else if (count == (uint16_t)-1)
{
double *doubleav;
CopyField(tag, doubleav);
}
break;
default:
TIFFError(TIFFFileName(in),
"Data type %" PRIu16 " is not supported, tag %d skipped.",
tag, type);
}
}
static const struct cpTag
{
uint16_t tag;
uint16_t count;
TIFFDataType type;
} tags[] = {
{TIFFTAG_SUBFILETYPE, 1, TIFF_LONG},
{TIFFTAG_THRESHHOLDING, 1, TIFF_SHORT},
{TIFFTAG_DOCUMENTNAME, 1, TIFF_ASCII},
{TIFFTAG_IMAGEDESCRIPTION, 1, TIFF_ASCII},
{TIFFTAG_MAKE, 1, TIFF_ASCII},
{TIFFTAG_MODEL, 1, TIFF_ASCII},
{TIFFTAG_MINSAMPLEVALUE, 1, TIFF_SHORT},
{TIFFTAG_MAXSAMPLEVALUE, 1, TIFF_SHORT},
{TIFFTAG_XRESOLUTION, 1, TIFF_RATIONAL},
{TIFFTAG_YRESOLUTION, 1, TIFF_RATIONAL},
{TIFFTAG_PAGENAME, 1, TIFF_ASCII},
{TIFFTAG_XPOSITION, 1, TIFF_RATIONAL},
{TIFFTAG_YPOSITION, 1, TIFF_RATIONAL},
{TIFFTAG_RESOLUTIONUNIT, 1, TIFF_SHORT},
{TIFFTAG_SOFTWARE, 1, TIFF_ASCII},
{TIFFTAG_DATETIME, 1, TIFF_ASCII},
{TIFFTAG_ARTIST, 1, TIFF_ASCII},
{TIFFTAG_HOSTCOMPUTER, 1, TIFF_ASCII},
{TIFFTAG_WHITEPOINT, (uint16_t)-1, TIFF_RATIONAL},
{TIFFTAG_PRIMARYCHROMATICITIES, (uint16_t)-1, TIFF_RATIONAL},
{TIFFTAG_HALFTONEHINTS, 2, TIFF_SHORT},
{TIFFTAG_INKSET, 1, TIFF_SHORT},
{TIFFTAG_DOTRANGE, 2, TIFF_SHORT},
{TIFFTAG_TARGETPRINTER, 1, TIFF_ASCII},
{TIFFTAG_SAMPLEFORMAT, 1, TIFF_SHORT},
{TIFFTAG_YCBCRCOEFFICIENTS, (uint16_t)-1, TIFF_RATIONAL},
{TIFFTAG_YCBCRSUBSAMPLING, 2, TIFF_SHORT},
{TIFFTAG_YCBCRPOSITIONING, 1, TIFF_SHORT},
{TIFFTAG_REFERENCEBLACKWHITE, (uint16_t)-1, TIFF_RATIONAL},
{TIFFTAG_EXTRASAMPLES, (uint16_t)-1, TIFF_SHORT},
{TIFFTAG_SMINSAMPLEVALUE, 1, TIFF_DOUBLE},
{TIFFTAG_SMAXSAMPLEVALUE, 1, TIFF_DOUBLE},
{TIFFTAG_STONITS, 1, TIFF_DOUBLE},
};
#define NTAGS (sizeof(tags) / sizeof(tags[0]))
#define CopyTag(tag, count, type) cpTag(in, out, tag, count, type)
typedef int (*copyFunc)(TIFF *in, TIFF *out, uint32_t l, uint32_t w,
uint16_t samplesperpixel);
static copyFunc pickCopyFunc(TIFF *, TIFF *, uint16_t, uint16_t);
/* PODD */
static int tiffcp(TIFF *in, TIFF *out)
{
uint16_t bitspersample = 1, samplesperpixel = 1;
uint16_t input_compression, input_photometric = PHOTOMETRIC_MINISBLACK;
copyFunc cf;
uint32_t width, length;
const struct cpTag *p;
CopyField(TIFFTAG_IMAGEWIDTH, width);
CopyField(TIFFTAG_IMAGELENGTH, length);
CopyField(TIFFTAG_BITSPERSAMPLE, bitspersample);
CopyField(TIFFTAG_SAMPLESPERPIXEL, samplesperpixel);
if (compression != (uint16_t)-1)
TIFFSetField(out, TIFFTAG_COMPRESSION, compression);
else
CopyField(TIFFTAG_COMPRESSION, compression);
if (!TIFFIsCODECConfigured(compression))
return FALSE;
TIFFGetFieldDefaulted(in, TIFFTAG_COMPRESSION, &input_compression);
TIFFGetFieldDefaulted(in, TIFFTAG_PHOTOMETRIC, &input_photometric);
if (input_compression == COMPRESSION_JPEG)
{
/* Force conversion to RGB */
TIFFSetField(in, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
}
else if (input_photometric == PHOTOMETRIC_YCBCR)
{
/* Otherwise, can't handle subsampled input */
uint16_t subsamplinghor, subsamplingver;
TIFFGetFieldDefaulted(in, TIFFTAG_YCBCRSUBSAMPLING, &subsamplinghor,
&subsamplingver);
if (subsamplinghor != 1 || subsamplingver != 1)
{
fprintf(stderr,
"tiffcp: %s: Can't copy/convert subsampled image.\n",
TIFFFileName(in));
return FALSE;
}
}
if (compression == COMPRESSION_JPEG)
{
if (input_photometric == PHOTOMETRIC_RGB &&
jpegcolormode == JPEGCOLORMODE_RGB)
TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_YCBCR);
else
TIFFSetField(out, TIFFTAG_PHOTOMETRIC, input_photometric);
}
else if (compression == COMPRESSION_SGILOG ||
compression == COMPRESSION_SGILOG24)
TIFFSetField(out, TIFFTAG_PHOTOMETRIC,
samplesperpixel == 1 ? PHOTOMETRIC_LOGL
: PHOTOMETRIC_LOGLUV);
else if (input_compression == COMPRESSION_JPEG && samplesperpixel == 3)
{
/* RGB conversion was forced above
hence the output will be of the same type */
TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
}
else
CopyTag(TIFFTAG_PHOTOMETRIC, 1, TIFF_SHORT);
if (fillorder != 0)
TIFFSetField(out, TIFFTAG_FILLORDER, fillorder);
else
CopyTag(TIFFTAG_FILLORDER, 1, TIFF_SHORT);
/*
* Will copy `Orientation' tag from input image
*/
TIFFGetFieldDefaulted(in, TIFFTAG_ORIENTATION, &orientation);
TIFFSetField(out, TIFFTAG_ORIENTATION, orientation);
/*
* Choose tiles/strip for the output image according to
* the command line arguments (-tiles, -strips) and the
* structure of the input image.
*/
if (outtiled == -1)
outtiled = TIFFIsTiled(in);
if (outtiled)
{
/*
* Setup output file's tile width&height. If either
* is not specified, use either the value from the
* input image or, if nothing is defined, use the
* library default.
*/
if (tilewidth == (uint32_t)-1)
TIFFGetField(in, TIFFTAG_TILEWIDTH, &tilewidth);
if (tilelength == (uint32_t)-1)
TIFFGetField(in, TIFFTAG_TILELENGTH, &tilelength);
TIFFDefaultTileSize(out, &tilewidth, &tilelength);
TIFFSetField(out, TIFFTAG_TILEWIDTH, tilewidth);
TIFFSetField(out, TIFFTAG_TILELENGTH, tilelength);
}
else
{
/*
* RowsPerStrip is left unspecified: use either the
* value from the input image or, if nothing is defined,
* use the library default.
*/
if (rowsperstrip == (uint32_t)0)
{
if (!TIFFGetField(in, TIFFTAG_ROWSPERSTRIP, &rowsperstrip))
{
rowsperstrip = TIFFDefaultStripSize(out, rowsperstrip);
}
if (rowsperstrip > length && rowsperstrip != (uint32_t)-1)
rowsperstrip = length;
}
else if (rowsperstrip == (uint32_t)-1)
rowsperstrip = length;
TIFFSetField(out, TIFFTAG_ROWSPERSTRIP, rowsperstrip);
}
if (config != (uint16_t)-1)
TIFFSetField(out, TIFFTAG_PLANARCONFIG, config);
else
CopyField(TIFFTAG_PLANARCONFIG, config);
if (samplesperpixel <= 4)
CopyTag(TIFFTAG_TRANSFERFUNCTION, 4, TIFF_SHORT);
CopyTag(TIFFTAG_COLORMAP, 4, TIFF_SHORT);
/* SMinSampleValue & SMaxSampleValue */
switch (compression)
{
case COMPRESSION_JPEG:
TIFFSetField(out, TIFFTAG_JPEGQUALITY, quality);
TIFFSetField(out, TIFFTAG_JPEGCOLORMODE, jpegcolormode);
break;
case COMPRESSION_JBIG:
CopyTag(TIFFTAG_FAXRECVPARAMS, 1, TIFF_LONG);
CopyTag(TIFFTAG_FAXRECVTIME, 1, TIFF_LONG);
CopyTag(TIFFTAG_FAXSUBADDRESS, 1, TIFF_ASCII);
CopyTag(TIFFTAG_FAXDCS, 1, TIFF_ASCII);
break;
case COMPRESSION_LERC:
if (max_z_error > 0)
{
if (TIFFSetField(out, TIFFTAG_LERC_MAXZERROR, max_z_error) != 1)
{
return FALSE;
}
}
if (subcodec != -1)
{
if (TIFFSetField(out, TIFFTAG_LERC_ADD_COMPRESSION, subcodec) !=
1)
{
return FALSE;
}
}
if (preset != -1)
{
switch (subcodec)
{
case LERC_ADD_COMPRESSION_DEFLATE:
if (TIFFSetField(out, TIFFTAG_ZIPQUALITY, preset) != 1)
{
return FALSE;
}
break;
case LERC_ADD_COMPRESSION_ZSTD:
if (TIFFSetField(out, TIFFTAG_ZSTD_LEVEL, preset) != 1)
{
return FALSE;
}
break;
}
}
break;
case COMPRESSION_LZW:
case COMPRESSION_ADOBE_DEFLATE:
case COMPRESSION_DEFLATE:
case COMPRESSION_LZMA:
case COMPRESSION_ZSTD:
if (predictor != (uint16_t)-1)
TIFFSetField(out, TIFFTAG_PREDICTOR, predictor);
else if (input_compression == COMPRESSION_LZW ||
input_compression == COMPRESSION_ADOBE_DEFLATE ||
input_compression == COMPRESSION_DEFLATE ||
input_compression == COMPRESSION_LZMA ||
input_compression == COMPRESSION_ZSTD)
{
CopyField(TIFFTAG_PREDICTOR, predictor);
}
if (compression == COMPRESSION_ADOBE_DEFLATE ||
compression == COMPRESSION_DEFLATE)
{
if (subcodec != -1)
{
if (TIFFSetField(out, TIFFTAG_DEFLATE_SUBCODEC, subcodec) !=
1)
{
return FALSE;
}
}
}
/*fallthrough*/
case COMPRESSION_WEBP:
if (preset != -1)
{
if (preset == 100)
{
TIFFSetField(out, TIFFTAG_WEBP_LOSSLESS, TRUE);
}
else
{
TIFFSetField(out, TIFFTAG_WEBP_LEVEL, preset);
}
}
break;
case COMPRESSION_CCITTFAX3:
case COMPRESSION_CCITTFAX4:
if (compression == COMPRESSION_CCITTFAX3)
{
if (g3opts != (uint32_t)-1)
TIFFSetField(out, TIFFTAG_GROUP3OPTIONS, g3opts);
else if (input_compression == COMPRESSION_CCITTFAX3)
CopyField(TIFFTAG_GROUP3OPTIONS, g3opts);
}
else if (input_compression == COMPRESSION_CCITTFAX4)
CopyTag(TIFFTAG_GROUP4OPTIONS, 1, TIFF_LONG);
if (input_compression == COMPRESSION_CCITTFAX3 ||
input_compression == COMPRESSION_CCITTFAX4)
{
CopyTag(TIFFTAG_BADFAXLINES, 1, TIFF_LONG);
CopyTag(TIFFTAG_CLEANFAXDATA, 1, TIFF_LONG);
CopyTag(TIFFTAG_CONSECUTIVEBADFAXLINES, 1, TIFF_LONG);
}
CopyTag(TIFFTAG_FAXRECVPARAMS, 1, TIFF_LONG);
CopyTag(TIFFTAG_FAXRECVTIME, 1, TIFF_LONG);
CopyTag(TIFFTAG_FAXSUBADDRESS, 1, TIFF_ASCII);
break;
}
{
uint32_t len32;
void **data;
if (TIFFGetField(in, TIFFTAG_ICCPROFILE, &len32, &data))
TIFFSetField(out, TIFFTAG_ICCPROFILE, len32, data);
}
{
uint16_t ninks;
const char *inknames;
if (TIFFGetField(in, TIFFTAG_NUMBEROFINKS, &ninks))
{
TIFFSetField(out, TIFFTAG_NUMBEROFINKS, ninks);
if (TIFFGetField(in, TIFFTAG_INKNAMES, &inknames))
{
int inknameslen = strlen(inknames) + 1;
const char *cp = inknames;
while (ninks > 1)
{
cp = strchr(cp, '\0');
cp++;
inknameslen += (strlen(cp) + 1);
ninks--;
}
TIFFSetField(out, TIFFTAG_INKNAMES, inknameslen, inknames);
}
}
}
{
unsigned short pg0, pg1;
if (pageInSeq == 1)
{
if (pageNum < 0) /* only one input file */
{
if (TIFFGetField(in, TIFFTAG_PAGENUMBER, &pg0, &pg1))
TIFFSetField(out, TIFFTAG_PAGENUMBER, pg0, pg1);
}
else
TIFFSetField(out, TIFFTAG_PAGENUMBER, pageNum++, 0);
}
else
{
if (TIFFGetField(in, TIFFTAG_PAGENUMBER, &pg0, &pg1))
{
if (pageNum < 0) /* only one input file */
TIFFSetField(out, TIFFTAG_PAGENUMBER, pg0, pg1);
else
TIFFSetField(out, TIFFTAG_PAGENUMBER, pageNum++, 0);
}
}
}
for (p = tags; p < &tags[NTAGS]; p++)
CopyTag(p->tag, p->count, p->type);
cf = pickCopyFunc(in, out, bitspersample, samplesperpixel);
return (cf ? (*cf)(in, out, length, width, samplesperpixel) : FALSE);
}
/*
* Copy Functions.
*/
#define DECLAREcpFunc(x) \
static int x(TIFF *in, TIFF *out, uint32_t imagelength, \
uint32_t imagewidth, tsample_t spp)
#define DECLAREreadFunc(x) \
static int x(TIFF *in, uint8_t *buf, uint32_t imagelength, \
uint32_t imagewidth, tsample_t spp)
typedef int (*readFunc)(TIFF *, uint8_t *, uint32_t, uint32_t, tsample_t);
#define DECLAREwriteFunc(x) \
static int x(TIFF *out, uint8_t *buf, uint32_t imagelength, \
uint32_t imagewidth, tsample_t spp)
typedef int (*writeFunc)(TIFF *, uint8_t *, uint32_t, uint32_t, tsample_t);
/*
* Contig -> contig by scanline for rows/strip change.
*/
DECLAREcpFunc(cpContig2ContigByRow)
{
tsize_t scanlinesize = TIFFScanlineSize(in);
tdata_t buf;
uint32_t row;
buf = limitMalloc(scanlinesize);
if (!buf)
return 0;
_TIFFmemset(buf, 0, scanlinesize);
(void)imagewidth;
(void)spp;
for (row = 0; row < imagelength; row++)
{
if (TIFFReadScanline(in, buf, row, 0) < 0 && !ignore)
{
TIFFError(TIFFFileName(in), "Error, can't read scanline %" PRIu32,
row);
goto bad;
}
if (TIFFWriteScanline(out, buf, row, 0) < 0)
{
TIFFError(TIFFFileName(out), "Error, can't write scanline %" PRIu32,
row);
goto bad;
}
}
_TIFFfree(buf);
return 1;
bad:
_TIFFfree(buf);
return 0;
}
typedef void biasFn(void *image, void *bias, uint32_t pixels);
#define subtract(bits) \
static void subtract##bits(void *i, void *b, uint32_t pixels) \
{ \
uint##bits##_t *image = i; \
uint##bits##_t *bias = b; \
while (pixels--) \
{ \
*image = *image > *bias ? *image - *bias : 0; \
image++, bias++; \
} \
}
subtract(8) subtract(16) subtract(32)
static biasFn *lineSubtractFn(unsigned bits)
{
switch (bits)
{
case 8:
return subtract8;
case 16:
return subtract16;
case 32:
return subtract32;
}
return NULL;
}
/*
* Contig -> contig by scanline while subtracting a bias image.
*/
DECLAREcpFunc(cpBiasedContig2Contig)
{
if (spp == 1)
{
tsize_t biasSize = TIFFScanlineSize(bias);
tsize_t bufSize = TIFFScanlineSize(in);
tdata_t buf, biasBuf;
uint32_t biasWidth = 0, biasLength = 0;
TIFFGetField(bias, TIFFTAG_IMAGEWIDTH, &biasWidth);
TIFFGetField(bias, TIFFTAG_IMAGELENGTH, &biasLength);
if (biasSize == bufSize && imagelength == biasLength &&
imagewidth == biasWidth)
{
uint16_t sampleBits = 0;
biasFn *subtractLine;
TIFFGetField(in, TIFFTAG_BITSPERSAMPLE, &sampleBits);
subtractLine = lineSubtractFn(sampleBits);
if (subtractLine)
{
uint32_t row;
buf = limitMalloc(bufSize);
biasBuf = limitMalloc(bufSize);
for (row = 0; row < imagelength; row++)
{
if (TIFFReadScanline(in, buf, row, 0) < 0 && !ignore)
{
TIFFError(TIFFFileName(in),
"Error, can't read scanline %" PRIu32, row);
goto bad;
}
if (TIFFReadScanline(bias, biasBuf, row, 0) < 0 && !ignore)
{
TIFFError(TIFFFileName(in),
"Error, can't read biased scanline %" PRIu32,
row);
goto bad;
}
subtractLine(buf, biasBuf, imagewidth);
if (TIFFWriteScanline(out, buf, row, 0) < 0)
{
TIFFError(TIFFFileName(out),
"Error, can't write scanline %" PRIu32, row);
goto bad;
}
}
_TIFFfree(buf);
_TIFFfree(biasBuf);
TIFFSetDirectory(bias, TIFFCurrentDirectory(bias)); /* rewind */
return 1;
bad:
_TIFFfree(buf);
_TIFFfree(biasBuf);
return 0;
}
else
{
TIFFError(TIFFFileName(in),
"No support for biasing %" PRIu16 " bit pixels\n",
sampleBits);
return 0;
}
}
TIFFError(TIFFFileName(in),
"Bias image %s,%" PRIu16
"\nis not the same size as %s,%" PRIu16 "\n",
TIFFFileName(bias), TIFFCurrentDirectory(bias),
TIFFFileName(in), TIFFCurrentDirectory(in));
return 0;
}
else
{
TIFFError(TIFFFileName(in),
"Can't bias %s,%" PRIu16 " as it has >1 Sample/Pixel\n",
TIFFFileName(in), TIFFCurrentDirectory(in));
return 0;
}
}
/*
* Strip -> strip for change in encoding.
*/
DECLAREcpFunc(cpDecodedStrips)
{
tsize_t stripsize = TIFFStripSize(in);
tdata_t buf = limitMalloc(stripsize);
(void)imagewidth;
(void)spp;
if (buf)
{
tstrip_t s, ns = TIFFNumberOfStrips(in);
uint32_t row = 0;
_TIFFmemset(buf, 0, stripsize);
for (s = 0; s < ns && row < imagelength; s++)
{
tsize_t cc = (row + rowsperstrip > imagelength)
? TIFFVStripSize(in, imagelength - row)
: stripsize;
if (TIFFReadEncodedStrip(in, s, buf, cc) < 0 && !ignore)
{
TIFFError(TIFFFileName(in), "Error, can't read strip %" PRIu32,
s);
goto bad;
}
if (TIFFWriteEncodedStrip(out, s, buf, cc) < 0)
{
TIFFError(TIFFFileName(out),
"Error, can't write strip %" PRIu32, s);
goto bad;
}
row += rowsperstrip;
}
_TIFFfree(buf);
return 1;
}
else
{
TIFFError(
TIFFFileName(in),
"Error, can't allocate memory buffer of size %" TIFF_SSIZE_FORMAT
" to read strips",
stripsize);
return 0;
}
bad:
_TIFFfree(buf);
return 0;
}
/*
* Separate -> separate by row for rows/strip change.
*/
DECLAREcpFunc(cpSeparate2SeparateByRow)
{
tsize_t scanlinesize = TIFFScanlineSize(in);
tdata_t buf;
uint32_t row;
tsample_t s;
(void)imagewidth;
buf = limitMalloc(scanlinesize);
if (!buf)
return 0;
_TIFFmemset(buf, 0, scanlinesize);
for (s = 0; s < spp; s++)
{
for (row = 0; row < imagelength; row++)
{
if (TIFFReadScanline(in, buf, row, s) < 0 && !ignore)
{
TIFFError(TIFFFileName(in),
"Error, can't read scanline %" PRIu32, row);
goto bad;
}
if (TIFFWriteScanline(out, buf, row, s) < 0)
{
TIFFError(TIFFFileName(out),
"Error, can't write scanline %" PRIu32, row);
goto bad;
}
}
}
_TIFFfree(buf);
return 1;
bad:
_TIFFfree(buf);
return 0;
}
/*
* Contig -> separate by row.
*/
DECLAREcpFunc(cpContig2SeparateByRow)
{
tsize_t scanlinesizein = TIFFScanlineSize(in);
tsize_t scanlinesizeout = TIFFScanlineSize(out);
tdata_t inbuf;
tdata_t outbuf;
register uint8_t *inp, *outp;
register uint32_t n;
uint32_t row;
tsample_t s;
uint16_t bps = 0;
(void)TIFFGetField(in, TIFFTAG_BITSPERSAMPLE, &bps);
if (bps != 8)
{
TIFFError(TIFFFileName(in),
"Error, can only handle BitsPerSample=8 in %s",
"cpContig2SeparateByRow");
return 0;
}
inbuf = limitMalloc(scanlinesizein);
outbuf = limitMalloc(scanlinesizeout);
if (!inbuf || !outbuf)
goto bad;
_TIFFmemset(inbuf, 0, scanlinesizein);
_TIFFmemset(outbuf, 0, scanlinesizeout);
/* unpack channels */
for (s = 0; s < spp; s++)
{
for (row = 0; row < imagelength; row++)
{
if (TIFFReadScanline(in, inbuf, row, 0) < 0 && !ignore)
{
TIFFError(TIFFFileName(in),
"Error, can't read scanline %" PRIu32, row);
goto bad;
}
inp = ((uint8_t *)inbuf) + s;
outp = (uint8_t *)outbuf;
for (n = imagewidth; n-- > 0;)
{
*outp++ = *inp;
inp += spp;
}
if (TIFFWriteScanline(out, outbuf, row, s) < 0)
{
TIFFError(TIFFFileName(out),
"Error, can't write scanline %" PRIu32, row);
goto bad;
}
}
}
if (inbuf)
_TIFFfree(inbuf);
if (outbuf)
_TIFFfree(outbuf);
return 1;
bad:
if (inbuf)
_TIFFfree(inbuf);
if (outbuf)
_TIFFfree(outbuf);
return 0;
}
/*
* Separate -> contig by row.
*/
DECLAREcpFunc(cpSeparate2ContigByRow)
{
tsize_t scanlinesizein = TIFFScanlineSize(in);
tsize_t scanlinesizeout = TIFFScanlineSize(out);
tdata_t inbuf;
tdata_t outbuf;
register uint8_t *inp, *outp;
register uint32_t n;
uint32_t row;
tsample_t s;
uint16_t bps = 0;
(void)TIFFGetField(in, TIFFTAG_BITSPERSAMPLE, &bps);
if (bps != 8)
{
TIFFError(TIFFFileName(in),
"Error, can only handle BitsPerSample=8 in %s",
"cpSeparate2ContigByRow");
return 0;
}
inbuf = limitMalloc(scanlinesizein);
outbuf = limitMalloc(scanlinesizeout);
if (!inbuf || !outbuf)
goto bad;
_TIFFmemset(inbuf, 0, scanlinesizein);
_TIFFmemset(outbuf, 0, scanlinesizeout);
for (row = 0; row < imagelength; row++)
{
/* merge channels */
for (s = 0; s < spp; s++)
{
if (TIFFReadScanline(in, inbuf, row, s) < 0 && !ignore)
{
TIFFError(TIFFFileName(in),
"Error, can't read scanline %" PRIu32, row);
goto bad;
}
inp = (uint8_t *)inbuf;
outp = ((uint8_t *)outbuf) + s;
for (n = imagewidth; n-- > 0;)
{
*outp = *inp++;
outp += spp;
}
}
if (TIFFWriteScanline(out, outbuf, row, 0) < 0)
{
TIFFError(TIFFFileName(out), "Error, can't write scanline %" PRIu32,
row);
goto bad;
}
}
if (inbuf)
_TIFFfree(inbuf);
if (outbuf)
_TIFFfree(outbuf);
return 1;
bad:
if (inbuf)
_TIFFfree(inbuf);
if (outbuf)
_TIFFfree(outbuf);
return 0;
}
static void cpStripToTile(uint8_t *out, uint8_t *in, uint32_t rows,
uint32_t cols, int outskew, int64_t inskew)
{
while (rows-- > 0)
{
uint32_t j = cols;
while (j-- > 0)
*out++ = *in++;
out += outskew;
in += inskew;
}
}
static void cpContigBufToSeparateBuf(uint8_t *out, uint8_t *in, uint32_t rows,
uint32_t cols, int outskew, int inskew,
tsample_t spp, int bytes_per_sample)
{
while (rows-- > 0)
{
uint32_t j = cols;
while (j-- > 0)
{
int n = bytes_per_sample;
while (n--)
{
*out++ = *in++;
}
in += (spp - 1) * bytes_per_sample;
}
out += outskew;
in += inskew;
}
}
static void cpSeparateBufToContigBuf(uint8_t *out, uint8_t *in, uint32_t rows,
uint32_t cols, int outskew, int inskew,
tsample_t spp, int bytes_per_sample)
{
while (rows-- > 0)
{
uint32_t j = cols;
while (j-- > 0)
{
int n = bytes_per_sample;
while (n--)
{
*out++ = *in++;
}
out += (spp - 1) * bytes_per_sample;
}
out += outskew;
in += inskew;
}
}
static int cpImage(TIFF *in, TIFF *out, readFunc fin, writeFunc fout,
uint32_t imagelength, uint32_t imagewidth, tsample_t spp)
{
int status = 0;
tdata_t buf = NULL;
tsize_t scanlinesize = TIFFRasterScanlineSize(in);
tsize_t bytes = scanlinesize * (tsize_t)imagelength;
/*
* XXX: Check for integer overflow.
*/
if (scanlinesize && imagelength &&
bytes / (tsize_t)imagelength == scanlinesize)
{
buf = limitMalloc(bytes);
if (buf)
{
if ((*fin)(in, (uint8_t *)buf, imagelength, imagewidth, spp))
{
status =
(*fout)(out, (uint8_t *)buf, imagelength, imagewidth, spp);
}
_TIFFfree(buf);
}
else
{
TIFFError(TIFFFileName(in),
"Error, can't allocate space for image buffer");
}
}
else
{
TIFFError(TIFFFileName(in), "Error, no space for image buffer");
}
return status;
}
DECLAREreadFunc(readContigStripsIntoBuffer)
{
tsize_t scanlinesize = TIFFScanlineSize(in);
uint8_t *bufp = buf;
uint32_t row;
(void)imagewidth;
(void)spp;
for (row = 0; row < imagelength; row++)
{
if (TIFFReadScanline(in, (tdata_t)bufp, row, 0) < 0 && !ignore)
{
TIFFError(TIFFFileName(in), "Error, can't read scanline %" PRIu32,
row);
return 0;
}
bufp += scanlinesize;
}
return 1;
}
DECLAREreadFunc(readSeparateStripsIntoBuffer)
{
int status = 1;
tsize_t scanlinesize = TIFFScanlineSize(in);
tdata_t scanline;
if (!scanlinesize)
return 0;
scanline = limitMalloc(scanlinesize);
if (!scanline)
return 0;
_TIFFmemset(scanline, 0, scanlinesize);
(void)imagewidth;
if (scanline)
{
uint8_t *bufp = (uint8_t *)buf;
uint32_t row;
tsample_t s;
for (row = 0; row < imagelength; row++)
{
/* merge channels */
for (s = 0; s < spp; s++)
{
uint8_t *bp = bufp + s;
tsize_t n = scanlinesize;
uint8_t *sbuf = scanline;
if (TIFFReadScanline(in, scanline, row, s) < 0 && !ignore)
{
TIFFError(TIFFFileName(in),
"Error, can't read scanline %" PRIu32, row);
status = 0;
goto done;
}
while (n-- > 0)
*bp = *sbuf++, bp += spp;
}
bufp += scanlinesize * spp;
}
}
done:
_TIFFfree(scanline);
return status;
}
DECLAREreadFunc(readContigTilesIntoBuffer)
{
int status = 1;
tsize_t tilesize = TIFFTileSize(in);
tdata_t tilebuf;
uint32_t imagew = TIFFScanlineSize(in);
uint32_t tilew = TIFFTileRowSize(in);
int64_t iskew = (int64_t)imagew - (int64_t)tilew;
uint8_t *bufp = (uint8_t *)buf;
uint32_t tw, tl;
uint32_t row;
(void)spp;
tilebuf = limitMalloc(tilesize);
if (tilebuf == 0)
return 0;
_TIFFmemset(tilebuf, 0, tilesize);
(void)TIFFGetField(in, TIFFTAG_TILEWIDTH, &tw);
(void)TIFFGetField(in, TIFFTAG_TILELENGTH, &tl);
for (row = 0; row < imagelength; row += tl)
{
uint32_t nrow = (row + tl > imagelength) ? imagelength - row : tl;
uint32_t colb = 0;
uint32_t col;
for (col = 0; col < imagewidth && colb < imagew; col += tw)
{
if (TIFFReadTile(in, tilebuf, col, row, 0, 0) < 0 && !ignore)
{
TIFFError(TIFFFileName(in),
"Error, can't read tile at %" PRIu32 " %" PRIu32, col,
row);
status = 0;
goto done;
}
if (colb > iskew)
{
uint32_t width = imagew - colb;
uint32_t oskew = tilew - width;
cpStripToTile(bufp + colb, tilebuf, nrow, width, oskew + iskew,
oskew);
}
else
cpStripToTile(bufp + colb, tilebuf, nrow, tilew, iskew, 0);
colb += tilew;
}
bufp += imagew * nrow;
}
done:
_TIFFfree(tilebuf);
return status;
}
DECLAREreadFunc(readSeparateTilesIntoBuffer)
{
int status = 1;
uint32_t imagew = TIFFRasterScanlineSize(in);
uint32_t tilew = TIFFTileRowSize(in);
int iskew;
tsize_t tilesize = TIFFTileSize(in);
tdata_t tilebuf;
uint8_t *bufp = (uint8_t *)buf;
uint32_t tw, tl;
uint32_t row;
uint16_t bps = 0, bytes_per_sample;
if (tilew && spp > (INT_MAX / tilew))
{
TIFFError(TIFFFileName(in),
"Error, cannot handle that much samples per tile row (Tile "
"Width * Samples/Pixel)");
return 0;
}
iskew = imagew - tilew * spp;
tilebuf = limitMalloc(tilesize);
if (tilebuf == 0)
return 0;
_TIFFmemset(tilebuf, 0, tilesize);
(void)TIFFGetField(in, TIFFTAG_TILEWIDTH, &tw);
(void)TIFFGetField(in, TIFFTAG_TILELENGTH, &tl);
(void)TIFFGetField(in, TIFFTAG_BITSPERSAMPLE, &bps);
if (bps == 0)
{
TIFFError(TIFFFileName(in), "Error, cannot read BitsPerSample");
status = 0;
goto done;
}
if ((bps % 8) != 0)
{
TIFFError(
TIFFFileName(in),
"Error, cannot handle BitsPerSample that is not a multiple of 8");
status = 0;
goto done;
}
bytes_per_sample = bps / 8;
for (row = 0; row < imagelength; row += tl)
{
uint32_t nrow = (row + tl > imagelength) ? imagelength - row : tl;
uint32_t colb = 0;
uint32_t col;
for (col = 0; col < imagewidth; col += tw)
{
tsample_t s;
for (s = 0; s < spp; s++)
{
if (TIFFReadTile(in, tilebuf, col, row, 0, s) < 0 && !ignore)
{
TIFFError(TIFFFileName(in),
"Error, can't read tile at %" PRIu32 " %" PRIu32
", "
"sample %" PRIu16,
col, row, s);
status = 0;
goto done;
}
/*
* Tile is clipped horizontally. Calculate
* visible portion and skewing factors.
*/
if (colb + tilew * spp > imagew)
{
uint32_t width = imagew - colb;
int oskew = tilew * spp - width;
cpSeparateBufToContigBuf(
bufp + colb + s * bytes_per_sample, tilebuf, nrow,
width / (spp * bytes_per_sample), oskew + iskew,
oskew / spp, spp, bytes_per_sample);
}
else
cpSeparateBufToContigBuf(bufp + colb + s * bytes_per_sample,
tilebuf, nrow, tw, iskew, 0, spp,
bytes_per_sample);
}
colb += tilew * spp;
}
bufp += imagew * nrow;
}
done:
_TIFFfree(tilebuf);
return status;
}
DECLAREwriteFunc(writeBufferToContigStrips)
{
uint32_t row, rowsperstrip;
tstrip_t strip = 0;
(void)imagewidth;
(void)spp;
(void)TIFFGetFieldDefaulted(out, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
for (row = 0; row < imagelength; row += rowsperstrip)
{
uint32_t nrows = (row + rowsperstrip > imagelength) ? imagelength - row
: rowsperstrip;
tsize_t stripsize = TIFFVStripSize(out, nrows);
if (TIFFWriteEncodedStrip(out, strip++, buf, stripsize) < 0)
{
TIFFError(TIFFFileName(out), "Error, can't write strip %" PRIu32,
strip - 1u);
return 0;
}
buf += stripsize;
}
return 1;
}
DECLAREwriteFunc(writeBufferToSeparateStrips)
{
uint32_t rowsize = imagewidth * spp;
uint32_t rowsperstrip;
tsize_t stripsize = TIFFStripSize(out);
tdata_t obuf;
tstrip_t strip = 0;
tsample_t s;
uint16_t bps = 0, bytes_per_sample;
obuf = limitMalloc(stripsize);
if (obuf == NULL)
return (0);
_TIFFmemset(obuf, 0, stripsize);
(void)TIFFGetFieldDefaulted(out, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
(void)TIFFGetField(out, TIFFTAG_BITSPERSAMPLE, &bps);
if (bps == 0)
{
TIFFError(TIFFFileName(out), "Error, cannot read BitsPerSample");
_TIFFfree(obuf);
return 0;
}
if ((bps % 8) != 0)
{
TIFFError(
TIFFFileName(out),
"Error, cannot handle BitsPerSample that is not a multiple of 8");
_TIFFfree(obuf);
return 0;
}
bytes_per_sample = bps / 8;
for (s = 0; s < spp; s++)
{
uint32_t row;
for (row = 0; row < imagelength; row += rowsperstrip)
{
uint32_t nrows = (row + rowsperstrip > imagelength)
? imagelength - row
: rowsperstrip;
tsize_t stripsize = TIFFVStripSize(out, nrows);
cpContigBufToSeparateBuf(obuf, (uint8_t *)buf + row * rowsize + s,
nrows, imagewidth, 0, 0, spp,
bytes_per_sample);
if (TIFFWriteEncodedStrip(out, strip++, obuf, stripsize) < 0)
{
TIFFError(TIFFFileName(out),
"Error, can't write strip %" PRIu32, strip - 1u);
_TIFFfree(obuf);
return 0;
}
}
}
_TIFFfree(obuf);
return 1;
}
DECLAREwriteFunc(writeBufferToContigTiles)
{
uint32_t imagew = TIFFScanlineSize(out);
uint32_t tilew = TIFFTileRowSize(out);
int iskew = imagew - tilew;
tsize_t tilesize = TIFFTileSize(out);
tdata_t obuf;
uint8_t *bufp = (uint8_t *)buf;
uint32_t tl, tw;
uint32_t row;
(void)spp;
obuf = limitMalloc(TIFFTileSize(out));
if (obuf == NULL)
return 0;
_TIFFmemset(obuf, 0, tilesize);
(void)TIFFGetField(out, TIFFTAG_TILELENGTH, &tl);
(void)TIFFGetField(out, TIFFTAG_TILEWIDTH, &tw);
for (row = 0; row < imagelength; row += tilelength)
{
uint32_t nrow = (row + tl > imagelength) ? imagelength - row : tl;
uint32_t colb = 0;
uint32_t col;
for (col = 0; col < imagewidth && colb < imagew; col += tw)
{
/*
* Tile is clipped horizontally. Calculate
* visible portion and skewing factors.
*/
if (colb + tilew > imagew)
{
uint32_t width = imagew - colb;
int oskew = tilew - width;
cpStripToTile(obuf, bufp + colb, nrow, width, oskew,
oskew + iskew);
}
else
cpStripToTile(obuf, bufp + colb, nrow, tilew, 0, iskew);
if (TIFFWriteTile(out, obuf, col, row, 0, 0) < 0)
{
TIFFError(TIFFFileName(out),
"Error, can't write tile at %" PRIu32 " %" PRIu32,
col, row);
_TIFFfree(obuf);
return 0;
}
colb += tilew;
}
bufp += nrow * imagew;
}
_TIFFfree(obuf);
return 1;
}
DECLAREwriteFunc(writeBufferToSeparateTiles)
{
uint32_t imagew = TIFFScanlineSize(out);
tsize_t tilew = TIFFTileRowSize(out);
uint32_t iimagew = TIFFRasterScanlineSize(out);
int iskew = iimagew - tilew * spp;
tsize_t tilesize = TIFFTileSize(out);
tdata_t obuf;
uint8_t *bufp = (uint8_t *)buf;
uint32_t tl, tw;
uint32_t row;
uint16_t bps = 0, bytes_per_sample;
obuf = limitMalloc(TIFFTileSize(out));
if (obuf == NULL)
return 0;
_TIFFmemset(obuf, 0, tilesize);
(void)TIFFGetField(out, TIFFTAG_TILELENGTH, &tl);
(void)TIFFGetField(out, TIFFTAG_TILEWIDTH, &tw);
(void)TIFFGetField(out, TIFFTAG_BITSPERSAMPLE, &bps);
if (bps == 0)
{
TIFFError(TIFFFileName(out), "Error, cannot read BitsPerSample");
_TIFFfree(obuf);
return 0;
}
if ((bps % 8) != 0)
{
TIFFError(
TIFFFileName(out),
"Error, cannot handle BitsPerSample that is not a multiple of 8");
_TIFFfree(obuf);
return 0;
}
bytes_per_sample = bps / 8;
for (row = 0; row < imagelength; row += tl)
{
uint32_t nrow = (row + tl > imagelength) ? imagelength - row : tl;
uint32_t colb = 0;
uint32_t col;
for (col = 0; col < imagewidth; col += tw)
{
tsample_t s;
for (s = 0; s < spp; s++)
{
/*
* Tile is clipped horizontally. Calculate
* visible portion and skewing factors.
*/
if (colb + tilew > imagew)
{
uint32_t width = (imagew - colb);
int oskew = tilew - width;
cpContigBufToSeparateBuf(obuf, bufp + (colb * spp) + s,
nrow, width / bytes_per_sample,
oskew, (oskew * spp) + iskew, spp,
bytes_per_sample);
}
else
cpContigBufToSeparateBuf(obuf, bufp + (colb * spp) + s,
nrow, tilewidth, 0, iskew, spp,
bytes_per_sample);
if (TIFFWriteTile(out, obuf, col, row, 0, s) < 0)
{
TIFFError(TIFFFileName(out),
"Error, can't write tile at %" PRIu32 " %" PRIu32
" sample %" PRIu16,
col, row, s);
_TIFFfree(obuf);
return 0;
}
}
colb += tilew;
}
bufp += nrow * iimagew;
}
_TIFFfree(obuf);
return 1;
}
/*
* Contig strips -> contig tiles.
*/
DECLAREcpFunc(cpContigStrips2ContigTiles)
{
return cpImage(in, out, readContigStripsIntoBuffer,
writeBufferToContigTiles, imagelength, imagewidth, spp);
}
/*
* Contig strips -> separate tiles.
*/
DECLAREcpFunc(cpContigStrips2SeparateTiles)
{
return cpImage(in, out, readContigStripsIntoBuffer,
writeBufferToSeparateTiles, imagelength, imagewidth, spp);
}
/*
* Separate strips -> contig tiles.
*/
DECLAREcpFunc(cpSeparateStrips2ContigTiles)
{
return cpImage(in, out, readSeparateStripsIntoBuffer,
writeBufferToContigTiles, imagelength, imagewidth, spp);
}
/*
* Separate strips -> separate tiles.
*/
DECLAREcpFunc(cpSeparateStrips2SeparateTiles)
{
return cpImage(in, out, readSeparateStripsIntoBuffer,
writeBufferToSeparateTiles, imagelength, imagewidth, spp);
}
/*
* Contig strips -> contig tiles.
*/
DECLAREcpFunc(cpContigTiles2ContigTiles)
{
return cpImage(in, out, readContigTilesIntoBuffer, writeBufferToContigTiles,
imagelength, imagewidth, spp);
}
/*
* Contig tiles -> separate tiles.
*/
DECLAREcpFunc(cpContigTiles2SeparateTiles)
{
return cpImage(in, out, readContigTilesIntoBuffer,
writeBufferToSeparateTiles, imagelength, imagewidth, spp);
}
/*
* Separate tiles -> contig tiles.
*/
DECLAREcpFunc(cpSeparateTiles2ContigTiles)
{
return cpImage(in, out, readSeparateTilesIntoBuffer,
writeBufferToContigTiles, imagelength, imagewidth, spp);
}
/*
* Separate tiles -> separate tiles (tile dimension change).
*/
DECLAREcpFunc(cpSeparateTiles2SeparateTiles)
{
return cpImage(in, out, readSeparateTilesIntoBuffer,
writeBufferToSeparateTiles, imagelength, imagewidth, spp);
}
/*
* Contig tiles -> contig tiles (tile dimension change).
*/
DECLAREcpFunc(cpContigTiles2ContigStrips)
{
return cpImage(in, out, readContigTilesIntoBuffer,
writeBufferToContigStrips, imagelength, imagewidth, spp);
}
/*
* Contig tiles -> separate strips.
*/
DECLAREcpFunc(cpContigTiles2SeparateStrips)
{
return cpImage(in, out, readContigTilesIntoBuffer,
writeBufferToSeparateStrips, imagelength, imagewidth, spp);
}
/*
* Separate tiles -> contig strips.
*/
DECLAREcpFunc(cpSeparateTiles2ContigStrips)
{
return cpImage(in, out, readSeparateTilesIntoBuffer,
writeBufferToContigStrips, imagelength, imagewidth, spp);
}
/*
* Separate tiles -> separate strips.
*/
DECLAREcpFunc(cpSeparateTiles2SeparateStrips)
{
return cpImage(in, out, readSeparateTilesIntoBuffer,
writeBufferToSeparateStrips, imagelength, imagewidth, spp);
}
/*
* Select the appropriate copy function to use.
*/
static copyFunc pickCopyFunc(TIFF *in, TIFF *out, uint16_t bitspersample,
uint16_t samplesperpixel)
{
uint16_t shortv;
uint32_t w, l, tw, tl;
int bychunk;
(void)TIFFGetFieldDefaulted(in, TIFFTAG_PLANARCONFIG, &shortv);
if (shortv != config && bitspersample != 8 && samplesperpixel > 1)
{
fprintf(stderr,
"%s: Cannot handle different planar configuration w/ "
"bits/sample != 8\n",
TIFFFileName(in));
return (NULL);
}
TIFFGetField(in, TIFFTAG_IMAGEWIDTH, &w);
TIFFGetField(in, TIFFTAG_IMAGELENGTH, &l);
if (!(TIFFIsTiled(out) || TIFFIsTiled(in)))
{
uint32_t irps = (uint32_t)-1L;
TIFFGetField(in, TIFFTAG_ROWSPERSTRIP, &irps);
/* if biased, force decoded copying to allow image subtraction */
bychunk = !bias && (rowsperstrip == irps);
}
else
{ /* either in or out is tiled */
if (bias)
{
fprintf(stderr,
"%s: Cannot handle tiled configuration w/bias image\n",
TIFFFileName(in));
return (NULL);
}
if (TIFFIsTiled(out))
{
if (!TIFFGetField(in, TIFFTAG_TILEWIDTH, &tw))
tw = w;
if (!TIFFGetField(in, TIFFTAG_TILELENGTH, &tl))
tl = l;
bychunk = (tw == tilewidth && tl == tilelength);
}
else
{ /* out's not, so in must be tiled */
TIFFGetField(in, TIFFTAG_TILEWIDTH, &tw);
TIFFGetField(in, TIFFTAG_TILELENGTH, &tl);
bychunk = (tw == w && tl == rowsperstrip);
}
}
#define T 1
#define F 0
#define pack(a, b, c, d, e) \
((long)(((a) << 11) | ((b) << 3) | ((c) << 2) | ((d) << 1) | (e)))
switch (pack(shortv, config, TIFFIsTiled(in), TIFFIsTiled(out), bychunk))
{
/* Strips -> Tiles */
case pack(PLANARCONFIG_CONTIG, PLANARCONFIG_CONTIG, F, T, F):
case pack(PLANARCONFIG_CONTIG, PLANARCONFIG_CONTIG, F, T, T):
return cpContigStrips2ContigTiles;
case pack(PLANARCONFIG_CONTIG, PLANARCONFIG_SEPARATE, F, T, F):
case pack(PLANARCONFIG_CONTIG, PLANARCONFIG_SEPARATE, F, T, T):
return cpContigStrips2SeparateTiles;
case pack(PLANARCONFIG_SEPARATE, PLANARCONFIG_CONTIG, F, T, F):
case pack(PLANARCONFIG_SEPARATE, PLANARCONFIG_CONTIG, F, T, T):
return cpSeparateStrips2ContigTiles;
case pack(PLANARCONFIG_SEPARATE, PLANARCONFIG_SEPARATE, F, T, F):
case pack(PLANARCONFIG_SEPARATE, PLANARCONFIG_SEPARATE, F, T, T):
return cpSeparateStrips2SeparateTiles;
/* Tiles -> Tiles */
case pack(PLANARCONFIG_CONTIG, PLANARCONFIG_CONTIG, T, T, F):
case pack(PLANARCONFIG_CONTIG, PLANARCONFIG_CONTIG, T, T, T):
return cpContigTiles2ContigTiles;
case pack(PLANARCONFIG_CONTIG, PLANARCONFIG_SEPARATE, T, T, F):
case pack(PLANARCONFIG_CONTIG, PLANARCONFIG_SEPARATE, T, T, T):
return cpContigTiles2SeparateTiles;
case pack(PLANARCONFIG_SEPARATE, PLANARCONFIG_CONTIG, T, T, F):
case pack(PLANARCONFIG_SEPARATE, PLANARCONFIG_CONTIG, T, T, T):
return cpSeparateTiles2ContigTiles;
case pack(PLANARCONFIG_SEPARATE, PLANARCONFIG_SEPARATE, T, T, F):
case pack(PLANARCONFIG_SEPARATE, PLANARCONFIG_SEPARATE, T, T, T):
return cpSeparateTiles2SeparateTiles;
/* Tiles -> Strips */
case pack(PLANARCONFIG_CONTIG, PLANARCONFIG_CONTIG, T, F, F):
case pack(PLANARCONFIG_CONTIG, PLANARCONFIG_CONTIG, T, F, T):
return cpContigTiles2ContigStrips;
case pack(PLANARCONFIG_CONTIG, PLANARCONFIG_SEPARATE, T, F, F):
case pack(PLANARCONFIG_CONTIG, PLANARCONFIG_SEPARATE, T, F, T):
return cpContigTiles2SeparateStrips;
case pack(PLANARCONFIG_SEPARATE, PLANARCONFIG_CONTIG, T, F, F):
case pack(PLANARCONFIG_SEPARATE, PLANARCONFIG_CONTIG, T, F, T):
return cpSeparateTiles2ContigStrips;
case pack(PLANARCONFIG_SEPARATE, PLANARCONFIG_SEPARATE, T, F, F):
case pack(PLANARCONFIG_SEPARATE, PLANARCONFIG_SEPARATE, T, F, T):
return cpSeparateTiles2SeparateStrips;
/* Strips -> Strips */
case pack(PLANARCONFIG_CONTIG, PLANARCONFIG_CONTIG, F, F, F):
return bias ? cpBiasedContig2Contig : cpContig2ContigByRow;
case pack(PLANARCONFIG_CONTIG, PLANARCONFIG_CONTIG, F, F, T):
return cpDecodedStrips;
case pack(PLANARCONFIG_CONTIG, PLANARCONFIG_SEPARATE, F, F, F):
case pack(PLANARCONFIG_CONTIG, PLANARCONFIG_SEPARATE, F, F, T):
return cpContig2SeparateByRow;
case pack(PLANARCONFIG_SEPARATE, PLANARCONFIG_CONTIG, F, F, F):
case pack(PLANARCONFIG_SEPARATE, PLANARCONFIG_CONTIG, F, F, T):
return cpSeparate2ContigByRow;
case pack(PLANARCONFIG_SEPARATE, PLANARCONFIG_SEPARATE, F, F, F):
case pack(PLANARCONFIG_SEPARATE, PLANARCONFIG_SEPARATE, F, F, T):
return cpSeparate2SeparateByRow;
}
#undef pack
#undef F
#undef T
fprintf(stderr, "tiffcp: %s: Don't know how to copy/convert image.\n",
TIFFFileName(in));
return (NULL);
}