2276 lines
72 KiB
C
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);
|
|
}
|