1 /*
2 ** tifftopnm.c - converts a Tagged Image File to a portable anymap
3 **
4 ** Derived by Jef Poskanzer from tif2ras.c, which is:
5 **
6 ** Copyright (c) 1990 by Sun Microsystems, Inc.
7 **
8 ** Author: Patrick J. Naughton
9 ** naughton@wind.sun.com
10 **
11 ** Permission to use, copy, modify, and distribute this software and its
12 ** documentation for any purpose and without fee is hereby granted,
13 ** provided that the above copyright notice appear in all copies and that
14 ** both that copyright notice and this permission notice appear in
15 ** supporting documentation.
16 **
17 ** This file is provided AS IS with no warranties of any kind. The author
18 ** shall have no liability with respect to the infringement of copyrights,
19 ** trade secrets or any patents by this file or any part thereof. In no
20 ** event will the author be liable for any lost revenue or profits or
21 ** other special, indirect and consequential damages.
22 */
23
24 /* Design note:
25
26 We have two different ways of converting from Tiff, as provided by the
27 Tiff library:
28
29 1) decode the entire image into memory at once, using
30 TIFFRGBAImageGet(), then convert to PNM and output row by row.
31
32 2) read, convert, and output one row at a time using TIFFReadScanline().
33
34 (1) is preferable because the Tiff library does more of the work, which
35 means it understands more of the Tiff format possibilities now and in
36 the future. Also, some compressed TIFF formats don't allow you to
37 extract an individual row.
38
39 (2) uses far less memory, and because our code does more of the work,
40 it's possible that it can be more flexible or at least give better
41 diagnostic information if there's something wrong with the TIFF.
42
43 In Netpbm, we stress function over performance, so by default we
44 try (1) first, and if we can't get enough memory for the decoded
45 image or TIFFRGBAImageGet() fails, we fall back to (2). But we
46 give the user the -byrow option to order (2) only.
47 */
48
49 #define _DEFAULT_SOURCE 1 /* New name for SVID & BSD source defines */
50 #define _BSD_SOURCE 1 /* Make sure strdup() is in string.h */
51 #define _XOPEN_SOURCE 500 /* Make sure strdup() is in string.h */
52
53 #include <assert.h>
54 #include <string.h>
55 #include <stdio.h>
56 #include <sys/wait.h> /* WIFSIGNALED, etc. */
57
58 #include "pm_c_util.h"
59 #include "shhopt.h"
60 #include "mallocvar.h"
61 #include "nstring.h"
62 #include "pnm.h"
63
64 /* See warning about tiffio.h in pamtotiff.c */
65 #include <tiffio.h>
66
67 /* The following are in current tiff.h, but so that we can compile against
68 older tiff libraries, we define them here.
69 */
70
71 #ifndef PHOTOMETRIC_LOGL
72 #define PHOTOMETRIC_LOGL 32844
73 #endif
74 #ifndef PHOTOMETRIC_LOGLUV
75 #define PHOTOMETRIC_LOGLUV 32845
76 #endif
77
78 #define MAXCOLORS 1024
79 #ifndef PHOTOMETRIC_DEPTH
80 #define PHOTOMETRIC_DEPTH 32768
81 #endif
82
83 struct CmdlineInfo {
84 /* All the information the user supplied in the command line,
85 in a form easy for the program to use.
86 */
87 char * inputFilename;
88 unsigned int headerdump;
89 char * alphaFilename;
90 bool alphaStdout;
91 unsigned int respectfillorder; /* -respectfillorder option */
92 unsigned int byrow;
93 unsigned int orientraw;
94 unsigned int verbose;
95 };
96
97
98
99 static void
parseCommandLine(int argc,const char ** const argv,struct CmdlineInfo * const cmdlineP)100 parseCommandLine(int argc, const char ** const argv,
101 struct CmdlineInfo * const cmdlineP) {
102 /*----------------------------------------------------------------------------
103 Note that many of the strings that this function returns in the
104 *cmdlineP structure are actually in the supplied argv array. And
105 sometimes, one of these strings is actually just a suffix of an entry
106 in argv!
107 -----------------------------------------------------------------------------*/
108 optStruct3 opt;
109 optEntry *option_def;
110 unsigned int option_def_index;
111 unsigned int alphaSpec;
112
113 MALLOCARRAY_NOFAIL(option_def, 100);
114
115 opt.opt_table = option_def;
116 opt.short_allowed = FALSE;
117 opt.allowNegNum = FALSE;
118
119 option_def_index = 0; /* incremented by OPTENT3 */
120 OPTENT3(0, "verbose",
121 OPT_FLAG, NULL, &cmdlineP->verbose, 0);
122 OPTENT3(0, "respectfillorder",
123 OPT_FLAG, NULL, &cmdlineP->respectfillorder, 0);
124 OPTENT3(0, "byrow",
125 OPT_FLAG, NULL, &cmdlineP->byrow, 0);
126 OPTENT3(0, "orientraw",
127 OPT_FLAG, NULL, &cmdlineP->orientraw, 0);
128 OPTENT3('h', "headerdump",
129 OPT_FLAG, NULL, &cmdlineP->headerdump, 0);
130 OPTENT3(0, "alphaout",
131 OPT_STRING, &cmdlineP->alphaFilename, &alphaSpec, 0);
132
133 pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
134
135 if (argc - 1 == 0)
136 cmdlineP->inputFilename = strdup("-"); /* he wants stdin */
137 else if (argc - 1 == 1)
138 cmdlineP->inputFilename = strdup(argv[1]);
139 else
140 pm_error("Too many arguments. The only argument accepted "
141 "is the input file name");
142
143 if (alphaSpec) {
144 if (streq(cmdlineP->alphaFilename, "-"))
145 cmdlineP->alphaStdout = TRUE;
146 else
147 cmdlineP->alphaStdout = FALSE;
148 } else {
149 cmdlineP->alphaFilename = NULL;
150 cmdlineP->alphaStdout = FALSE;
151 }
152 }
153
154
155
156 static TIFF *
newTiffImageObject(const char * const inputFileName)157 newTiffImageObject(const char * const inputFileName) {
158 /*----------------------------------------------------------------------------
159 Create a TIFF library object for accessing the TIFF input in file
160 named 'inputFileName'. If 'inputFileName' is "-", that means
161 Standard Input.
162 -----------------------------------------------------------------------------*/
163 const char * const tiffSourceName =
164 streq(inputFileName, "-") ? "Standard Input" : inputFileName;
165
166 TIFF * retval;
167
168 retval = TIFFFdOpen(fileno(pm_openr_seekable(inputFileName)),
169 tiffSourceName,
170 "r");
171
172 if (retval == NULL)
173 pm_error("Failed to access input file. The OS opened the file fine, "
174 "but the TIFF library's TIFFFdOpen rejected the open file.");
175
176 return retval;
177 }
178
179
180
181 static void
getBps(TIFF * const tif,unsigned short * const bpsP)182 getBps(TIFF * const tif,
183 unsigned short * const bpsP) {
184
185 unsigned short tiffBps;
186 unsigned short bps;
187 int fldPresent;
188
189 fldPresent = TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &tiffBps);
190 bps = fldPresent ? tiffBps : 1;
191
192 if (bps < 1 || (bps > 8 && bps != 16 && bps != 32))
193 pm_error("This program can process Tiff images with only "
194 "1-8 or 16 or 32 bits per sample. The input Tiff image "
195 "has %hu bits per sample.", bps);
196 else
197 *bpsP = bps;
198 }
199
200
201
202 struct tiffDirInfo {
203 /* 'width' and 'height' are the dimensions of the raster matrix in
204 the TIFF stream -- what the TIFF spec calls the image. The
205 dimensions of the actual visual image represented may be the
206 reverse, because the raster can represent the visual image in
207 various orientations, as described by 'orientation'.
208 */
209 unsigned int width;
210 unsigned int height;
211 unsigned short bps;
212 unsigned short spp;
213 unsigned short photomet;
214 unsigned short planarconfig;
215 unsigned short fillorder;
216 unsigned short orientation;
217 };
218
219
220
221 static void
tiffToImageDim(unsigned int const tiffWidth,unsigned int const tiffHeight,unsigned short const orientation,unsigned int * const imageColsP,unsigned int * const imageRowsP)222 tiffToImageDim(unsigned int const tiffWidth,
223 unsigned int const tiffHeight,
224 unsigned short const orientation,
225 unsigned int * const imageColsP,
226 unsigned int * const imageRowsP) {
227 /*----------------------------------------------------------------------------
228 Determine the image dimensions (as *imageColsP and *imageRowsP) from the
229 width, height, and orientation of the TIFF raster ('tiffWidth',
230 'tiffHeight', and 'orientation', respectively.
231 -----------------------------------------------------------------------------*/
232 switch (orientation) {
233 case ORIENTATION_TOPLEFT:
234 case ORIENTATION_TOPRIGHT:
235 case ORIENTATION_BOTRIGHT:
236 case ORIENTATION_BOTLEFT:
237 *imageColsP = tiffWidth;
238 *imageRowsP = tiffHeight;
239 break;
240 case ORIENTATION_LEFTTOP:
241 case ORIENTATION_RIGHTTOP:
242 case ORIENTATION_RIGHTBOT:
243 case ORIENTATION_LEFTBOT:
244 *imageColsP = tiffHeight;
245 *imageRowsP = tiffWidth;
246 break;
247 default:
248 pm_error("Invalid value for orientation tag in TIFF directory: %u",
249 orientation);
250 }
251 }
252
253
254
255 static void
getTiffDimensions(TIFF * const tiffP,unsigned int * const colsP,unsigned int * const rowsP)256 getTiffDimensions(TIFF * const tiffP,
257 unsigned int * const colsP,
258 unsigned int * const rowsP) {
259 /*----------------------------------------------------------------------------
260 Return the dimensions of the image represented by *tiffP. Not the
261 dimensions of the internal raster matrix -- the dimensions of the
262 actual visual image.
263 -----------------------------------------------------------------------------*/
264 int fldPresent;
265
266 unsigned int width, length;
267 unsigned short tiffOrientation;
268 unsigned short orientation;
269
270 fldPresent = TIFFGetField(tiffP, TIFFTAG_IMAGEWIDTH, &width);
271 if (!fldPresent)
272 pm_error("Input Tiff file is invalid. It has no IMAGEWIDTH tag.");
273 fldPresent = TIFFGetField(tiffP, TIFFTAG_IMAGELENGTH, &length);
274 if (!fldPresent)
275 pm_error("Input Tiff file is invalid. It has no IMAGELENGTH tag.");
276
277 fldPresent = TIFFGetField(tiffP, TIFFTAG_ORIENTATION, &tiffOrientation);
278 orientation = fldPresent ? tiffOrientation : ORIENTATION_TOPLEFT;
279
280 tiffToImageDim(width, length, orientation, colsP, rowsP);
281 }
282
283
284
285 static unsigned short
planarConfigFmTiff(TIFF * const tiffP)286 planarConfigFmTiff(TIFF * const tiffP) {
287
288 int fldPresent;
289 unsigned short retval;
290
291 fldPresent = TIFFGetField(tiffP, TIFFTAG_PLANARCONFIG, &retval);
292
293 if (!fldPresent)
294 pm_error("PLANARCONFIG tag is not in Tiff file, though it "
295 "has more than one sample per pixel. "
296 "TIFFGetField() of it failed. This means the input "
297 "is not valid Tiff.");
298
299 return retval;
300 }
301
302
303
304 static void
validatePlanarConfig(unsigned short const planarconfig,unsigned short const photomet)305 validatePlanarConfig(unsigned short const planarconfig,
306 unsigned short const photomet) {
307
308 switch (planarconfig) {
309 case PLANARCONFIG_CONTIG:
310 break;
311 case PLANARCONFIG_SEPARATE:
312 if (photomet != PHOTOMETRIC_RGB &&
313 photomet != PHOTOMETRIC_SEPARATED)
314 pm_error("This program can handle separate planes only "
315 "with RGB (PHOTOMETRIC tag = %u) or SEPARATED "
316 "(PHOTOMETRIC tag = %u) data. The input Tiff file "
317 "has PHOTOMETRIC tag = %hu.",
318 PHOTOMETRIC_RGB, PHOTOMETRIC_SEPARATED,
319 photomet);
320 break;
321 default:
322 pm_error("Unrecognized PLANARCONFIG tag value in Tiff input: %u",
323 planarconfig);
324 }
325 }
326
327
328
329 static unsigned short
orientationFmTiff(TIFF * const tiffP)330 orientationFmTiff(TIFF * const tiffP) {
331
332 unsigned short tiffOrientation;
333 int fldPresent;
334
335 fldPresent = TIFFGetField(tiffP, TIFFTAG_ORIENTATION, &tiffOrientation);
336
337 return fldPresent ? tiffOrientation : ORIENTATION_TOPLEFT;
338 }
339
340
341
342 static void
dumpHeader(const struct tiffDirInfo * const headerP)343 dumpHeader(const struct tiffDirInfo * const headerP) {
344
345 pm_message("%ux%ux%u raster matrix, oriented %u",
346 headerP->width, headerP->height,
347 headerP->bps * headerP->spp, headerP->orientation);
348 pm_message("%hu bits/sample, %hu samples/pixel",
349 headerP->bps, headerP->spp);
350 }
351
352
353
354 static void
readDirectory(TIFF * const tiffP,bool const headerdump,struct tiffDirInfo * const headerP)355 readDirectory(TIFF * const tiffP,
356 bool const headerdump,
357 struct tiffDirInfo * const headerP) {
358 /*----------------------------------------------------------------------------
359 Read various values of TIFF tags from the TIFF directory, and
360 default them if not in there and make guesses where values are
361 invalid. Exit program with error message if required tags aren't
362 there or values are inconsistent or beyond our capabilities. If
363 'headerdump' is true, issue informational messages about what we
364 find.
365
366 The TIFF library is capable of returning invalid values (if the
367 input file contains invalid values). We generally return those
368 invalid values to our caller.
369 -----------------------------------------------------------------------------*/
370 int fldPresent;
371 unsigned short tiffSpp;
372
373 if (headerdump)
374 TIFFPrintDirectory(tiffP, stderr, TIFFPRINT_NONE);
375
376 getBps(tiffP, &headerP->bps);
377
378 fldPresent =
379 TIFFGetFieldDefaulted(tiffP, TIFFTAG_FILLORDER, &headerP->fillorder);
380 fldPresent = TIFFGetField(tiffP, TIFFTAG_SAMPLESPERPIXEL, &tiffSpp);
381 headerP->spp = fldPresent ? tiffSpp: 1;
382
383 fldPresent = TIFFGetField(tiffP, TIFFTAG_PHOTOMETRIC, &headerP->photomet);
384 if (!fldPresent)
385 pm_error("PHOTOMETRIC tag is not in Tiff file. "
386 "TIFFGetField() of it failed.\n"
387 "This means the input is not valid Tiff.");
388
389 if (headerP->spp > 1)
390 headerP->planarconfig = planarConfigFmTiff(tiffP);
391 else
392 headerP->planarconfig = PLANARCONFIG_CONTIG;
393
394 validatePlanarConfig(headerP->planarconfig, headerP->photomet);
395
396 fldPresent = TIFFGetField(tiffP, TIFFTAG_IMAGEWIDTH, &headerP->width);
397 if (!fldPresent)
398 pm_error("Input Tiff file is invalid. It has no IMAGEWIDTH tag.");
399 fldPresent = TIFFGetField(tiffP, TIFFTAG_IMAGELENGTH, &headerP->height);
400 if (!fldPresent)
401 pm_error("Input Tiff file is invalid. It has no IMAGELENGTH tag.");
402
403 headerP->orientation = orientationFmTiff(tiffP);
404
405 if (headerdump)
406 dumpHeader(headerP);
407 }
408
409
410
411 static void
readscanline(TIFF * const tif,unsigned char * const scanbuf,int const row,int const plane,unsigned int const cols,unsigned short const bps,unsigned short const spp,unsigned short const fillorder,unsigned int * const samplebuf)412 readscanline(TIFF * const tif,
413 unsigned char * const scanbuf,
414 int const row,
415 int const plane,
416 unsigned int const cols,
417 unsigned short const bps,
418 unsigned short const spp,
419 unsigned short const fillorder,
420 unsigned int * const samplebuf) {
421 /*----------------------------------------------------------------------------
422 Read one scanline out of the Tiff input and store it into samplebuf[].
423 Unlike the scanline returned by the Tiff library function, samplebuf[]
424 is composed of one sample per array element, which makes it easier for
425 our caller to process.
426
427 scanbuf[] is a scratch array for our use, which is big enough to hold
428 a Tiff scanline.
429 -----------------------------------------------------------------------------*/
430 int rc;
431 unsigned int const bpsmask = (1 << bps) - 1;
432 /* A mask for taking the lowest 'bps' bits of a number */
433
434 /* The TIFFReadScanline man page doesn't tell the format of its
435 'buf' return value, but it is exactly the same format as the 'buf'
436 input to TIFFWriteScanline. The man page for that doesn't say
437 anything either, but the source code for Pamtotiff contains a
438 specification.
439 */
440
441 rc = TIFFReadScanline(tif, scanbuf, row, plane);
442
443 if (rc < 0)
444 pm_error( "Unable to read row %d, plane %d of input Tiff image. "
445 "TIFFReadScanline() failed.",
446 row, plane);
447 else if (bps == 8) {
448 unsigned int sample;
449 for (sample = 0; sample < cols * spp; ++sample)
450 samplebuf[sample] = scanbuf[sample];
451 } else if (bps < 8) {
452 /* Note that in this format, samples do not span bytes. Rather,
453 each byte may have don't-care bits in the right-end positions.
454 At least that's how I infer the format from reading pnmtotiff.c
455 -Bryan 00.11.18
456 */
457 unsigned int sample;
458 unsigned int bitsleft;
459 unsigned char * inP;
460
461 for (sample = 0, bitsleft = 8, inP = scanbuf;
462 sample < cols * spp;
463 ++sample) {
464 if (bitsleft == 0) {
465 ++inP;
466 bitsleft = 8;
467 }
468 switch (fillorder) {
469 case FILLORDER_MSB2LSB:
470 samplebuf[sample] = (*inP >> (bitsleft-bps)) & bpsmask;
471 break;
472 case FILLORDER_LSB2MSB:
473 samplebuf[sample] = (*inP >> (8-bitsleft)) & bpsmask;
474 break;
475 default:
476 pm_error("Internal error: invalid value for fillorder: %u",
477 fillorder);
478 }
479 assert(bitsleft >= bps);
480 bitsleft -= bps;
481 if (bitsleft < bps)
482 /* Don't count dregs at end of byte */
483 bitsleft = 0;
484 }
485 } else if (bps == 16) {
486 /* Before Netpbm 9.17, this program assumed that scanbuf[]
487 contained an array of bytes as read from the Tiff file. In
488 fact, in this bps == 16 case, it's an array of "shorts",
489 each stored in whatever format this platform uses (which is
490 none of our concern). The pre-9.17 code also presumed that
491 the TIFF "FILLORDER" tag determined the order in which the
492 bytes of each sample appear in a TIFF file, which is
493 contrary to the TIFF spec.
494 */
495 const uint16 * const scanbuf16 = (const uint16 *) scanbuf;
496 unsigned int sample;
497
498 for (sample = 0; sample < cols*spp; ++sample)
499 samplebuf[sample] = scanbuf16[sample];
500 } else if (bps == 32) {
501 const uint32 * const scanbuf32 = (const uint32 *) scanbuf;
502 unsigned int sample;
503
504 for (sample = 0; sample < cols * spp; ++sample)
505 samplebuf[sample] = scanbuf32[sample];
506 } else
507 pm_error("Internal error: invalid bits per sample passed to "
508 "readscanline()");
509 }
510
511
512
513 static void
pick_cmyk_pixel(unsigned int const samplebuf[],int const sampleCursor,xelval * const redP,xelval * const bluP,xelval * const grnP)514 pick_cmyk_pixel(unsigned int const samplebuf[],
515 int const sampleCursor,
516 xelval * const redP,
517 xelval * const bluP,
518 xelval * const grnP) {
519
520 /* Note that the TIFF spec does not say which of the 4 samples is
521 which, but common sense says they're in order C,M,Y,K. Before
522 Netpbm 10.21 (March 2004), we assumed C,Y,M,K for some reason.
523 But combined with a compensating error in the CMYK->RGB
524 calculation, it had the same effect as C,M,Y,K.
525 */
526 unsigned int const c = samplebuf[sampleCursor + 0];
527 unsigned int const m = samplebuf[sampleCursor + 1];
528 unsigned int const y = samplebuf[sampleCursor + 2];
529 unsigned int const k = samplebuf[sampleCursor + 3];
530
531 /* The CMYK->RGB formula used by TIFFRGBAImageGet() in the TIFF
532 library is the following, (with some apparent confusion with
533 the names of the yellow and magenta pigments being reversed).
534
535 R = (1-K)*(1-C) (with C,Y,M,K normalized to 0..1)
536 G = (1-K)*(1-M)
537 B = (1-K)*(1-Y)
538
539 We used that too before Netpbm 10.21 (March 2004).
540
541 Now we use the inverse of what Pnmtotiffcmyk has always used, which
542 makes sense as follows: A microliter of black ink is simply a
543 substitute for a microliter each of cyan, magenta, and yellow ink.
544 Yellow ink removes blue light from what the white paper reflects.
545 */
546
547 *redP = 255 - MIN(255, c + k);
548 *grnP = 255 - MIN(255, m + k);
549 *bluP = 255 - MIN(255, y + k);
550 }
551
552
553
554 static void
computeFillorder(unsigned short const fillorderTag,unsigned short * const fillorderP,bool const respectfillorder)555 computeFillorder(unsigned short const fillorderTag,
556 unsigned short * const fillorderP,
557 bool const respectfillorder) {
558
559 if (respectfillorder) {
560 if (fillorderTag != FILLORDER_MSB2LSB &&
561 fillorderTag != FILLORDER_LSB2MSB)
562 pm_error("Invalid value in Tiff input for the FILLORDER tag: %u. "
563 "Valid values are %u and %u. Try omitting the "
564 "-respectfillorder option.",
565 fillorderTag, FILLORDER_MSB2LSB, FILLORDER_LSB2MSB);
566 else
567 *fillorderP = fillorderTag;
568 } else {
569 *fillorderP = FILLORDER_MSB2LSB;
570 if (fillorderTag != *fillorderP)
571 pm_message("Warning: overriding FILLORDER tag in the tiff input "
572 "and assuming msb-to-lsb. Consider the "
573 "-respectfillorder option.");
574 }
575 }
576
577
578
579 static void
analyzeImageType(TIFF * const tiffP,unsigned short const bps,unsigned short const spp,unsigned short const photomet,xelval * const maxvalP,int * const formatP,xel * const colormap,bool const headerdump,struct CmdlineInfo const cmdline)580 analyzeImageType(TIFF * const tiffP,
581 unsigned short const bps,
582 unsigned short const spp,
583 unsigned short const photomet,
584 xelval * const maxvalP,
585 int * const formatP,
586 xel * const colormap,
587 bool const headerdump,
588 struct CmdlineInfo const cmdline) {
589 /*----------------------------------------------------------------------------
590 Determine from the TIFF header in *tif certain properties of the image
591 as well as the proper format of PNM image for the conversion.
592
593 *formatP and *maxvalP are the basic PNM parameters.
594 -----------------------------------------------------------------------------*/
595 switch (photomet) {
596 case PHOTOMETRIC_MINISBLACK:
597 case PHOTOMETRIC_MINISWHITE:
598 if (spp != 1)
599 pm_error("This grayscale image has %d samples per pixel. "
600 "We understand only 1.", spp);
601
602 *formatP = bps == 1 ? PBM_TYPE : PGM_TYPE;
603
604 *maxvalP = pm_bitstomaxval(MIN(bps, 16));
605
606 if (headerdump)
607 pm_message("grayscale image, (min=%s) output maxval %u ",
608 photomet == PHOTOMETRIC_MINISBLACK ?
609 "black" : "white",
610 *maxvalP
611 );
612 break;
613
614 case PHOTOMETRIC_PALETTE: {
615 int fldPresent;
616 int i;
617 int numcolors;
618 unsigned short* redcolormap;
619 unsigned short* greencolormap;
620 unsigned short* bluecolormap;
621
622 if (headerdump)
623 pm_message("colormapped");
624
625 if (spp != 1)
626 pm_error("This paletted image has %d samples per pixel. "
627 "We understand only 1.", spp);
628
629 fldPresent = TIFFGetField(
630 tiffP, TIFFTAG_COLORMAP,
631 &redcolormap, &greencolormap, &bluecolormap);
632
633 if (!fldPresent)
634 pm_error("error getting colormaps");
635
636 numcolors = 1 << bps;
637 if (numcolors > MAXCOLORS)
638 pm_error("too many colors");
639
640 *formatP = PPM_TYPE;
641
642 *maxvalP = PNM_MAXMAXVAL;
643
644 for (i = 0; i < numcolors; ++i) {
645 xelval r, g, b;
646 r = (long) redcolormap[i] * PNM_MAXMAXVAL / 65535L;
647 g = (long) greencolormap[i] * PNM_MAXMAXVAL / 65535L;
648 b = (long) bluecolormap[i] * PNM_MAXMAXVAL / 65535L;
649 PPM_ASSIGN(colormap[i], r, g, b);
650 }
651 }
652 break;
653
654 case PHOTOMETRIC_SEPARATED: {
655 unsigned short inkset;
656 int fldPresent;
657
658 if (headerdump)
659 pm_message("color separation");
660
661 fldPresent = TIFFGetField(tiffP, TIFFTAG_INKNAMES, &inkset);
662 if (fldPresent && inkset != INKSET_CMYK)
663 pm_error("This color separation file uses an inkset (%d) "
664 "we can't handle. We handle only CMYK.", inkset);
665 if (spp != 4)
666 pm_error("This CMYK color separation file is %d samples per "
667 "pixel. "
668 "We need 4 samples, though: C, M, Y, and K. ",
669 spp);
670
671 *formatP = PPM_TYPE;
672
673 *maxvalP = (1 << bps) - 1;
674 }
675 break;
676
677 case PHOTOMETRIC_RGB:
678 if (headerdump)
679 pm_message("RGB truecolor");
680
681 if (spp != 3 && spp != 4)
682 pm_error("This RGB image has %d samples per pixel. "
683 "We understand only 3 or 4.", spp);
684
685 *formatP = PPM_TYPE;
686
687 *maxvalP = (1 << bps) - 1;
688 break;
689
690 case PHOTOMETRIC_MASK:
691 pm_error("don't know how to handle PHOTOMETRIC_MASK");
692
693 case PHOTOMETRIC_DEPTH:
694 pm_error("don't know how to handle PHOTOMETRIC_DEPTH");
695
696 case PHOTOMETRIC_YCBCR:
697 pm_error("don't know how to handle PHOTOMETRIC_YCBCR");
698
699 case PHOTOMETRIC_CIELAB:
700 pm_error("don't know how to handle PHOTOMETRIC_CIELAB");
701
702 case PHOTOMETRIC_LOGL:
703 pm_error("don't know how to handle PHOTOMETRIC_LOGL");
704
705 case PHOTOMETRIC_LOGLUV:
706 pm_error("don't know how to handle PHOTOMETRIC_LOGLUV");
707
708 default:
709 pm_error("unknown photometric: %d", photomet);
710 }
711 if (*maxvalP > PNM_OVERALLMAXVAL)
712 pm_error("bits/sample (%u) in the input image is too large.", bps);
713 }
714
715
716
717 static void
reportOutputFormat(int const format)718 reportOutputFormat(int const format) {
719
720 const char * formatDesc;
721
722 switch (format) {
723 case PBM_TYPE: formatDesc = "PBM"; break;
724 case PGM_TYPE: formatDesc = "PGM"; break;
725 case PPM_TYPE: formatDesc = "PPM"; break;
726 default: assert(false);
727 }
728
729 pm_message("writing %s file", formatDesc);
730 }
731
732
733
734 typedef struct {
735 FILE * imageoutFileP;
736 /* The stream to which we write the PNM image. Null for none. */
737 FILE * alphaFileP;
738 /* The stream to which we write the alpha channel. Null for none. */
739 unsigned int inCols;
740 /* Width of each row that gets passed to this object */
741 unsigned int inRows;
742 /* Number of rows that get passed to this object */
743 unsigned int outCols;
744 /* Width of each row this object writes out to the file */
745 unsigned int outRows;
746 /* Number of rows this object writes out */
747 xelval maxval;
748 /* Maxval of the output image */
749 int format;
750 /* Format of the output image */
751 gray alphaMaxval;
752 /* Maxval of the alpha channel */
753 bool flipping;
754 /* We're passing rows through a Pamflip process, rather than writing
755 them directly to *imageoutFileP, *alphaFileP.
756 */
757 FILE * imagePipeP;
758 /* Stream hooked up to pipe that goes to a Pamflip process.
759 Meaningful only when 'flipping' is true.
760 */
761 FILE * alphaPipeP;
762 /* Stream hooked up to pipe that goes to a Pamflip process.
763 Meaningful only when 'flipping' is true.
764 */
765 pid_t imageFlipPid;
766 /* Process ID of the Pamflip process.
767 Meaningful only when 'flipping' is true.
768 */
769 pid_t alphaFlipPid;
770 /* Process ID of the Pamflip process.
771 Meaningful only when 'flipping' is true.
772 */
773 } pnmOut;
774
775
776
777 static const char *
xformNeeded(unsigned short const tiffOrientation)778 xformNeeded(unsigned short const tiffOrientation) {
779 /*----------------------------------------------------------------------------
780 Return the value of the Pamflip -xform option that causes Pamflip
781 to change a raster from orientation 'tiffOrientation' to Row 0 top,
782 Column 0 left.
783 -----------------------------------------------------------------------------*/
784 switch (tiffOrientation) {
785 case ORIENTATION_TOPLEFT: return "";
786 case ORIENTATION_TOPRIGHT: return "leftright";
787 case ORIENTATION_BOTRIGHT: return "topbottom,leftright";
788 case ORIENTATION_BOTLEFT: return "topbottom";
789 case ORIENTATION_LEFTTOP: return "transpose";
790 case ORIENTATION_RIGHTTOP: return "transpose,leftright";
791 case ORIENTATION_RIGHTBOT: return "transpose,topbottom,leftright";
792 case ORIENTATION_LEFTBOT: return "transpose,topbottom";
793 default:
794 pm_error("Invalid value for orientation tag in TIFF directory: %u",
795 tiffOrientation);
796 return "";
797 }
798 }
799
800
801
802 /* File descriptors array indices for use with pipe() */
803 #define PIPE_READ 0
804 #define PIPE_WRITE 1
805
806 static void
spawnWithInputPipe(const char * const shellCmd,FILE ** const pipePP,pid_t * const pidP,const char ** const errorP)807 spawnWithInputPipe(const char * const shellCmd,
808 FILE ** const pipePP,
809 pid_t * const pidP,
810 const char ** const errorP) {
811
812 int fd[2];
813 int rc;
814
815 rc = pm_pipe(fd);
816
817 if (rc != 0)
818 pm_asprintf(errorP, "Failed to create pipe for process input. "
819 "Errno=%d (%s)", errno, strerror(errno));
820 else {
821 int iAmParent;
822 pid_t childPid;
823
824 pm_fork(&iAmParent, &childPid, errorP);
825
826 if (!*errorP) {
827 if (iAmParent) {
828 close(fd[PIPE_READ]);
829
830 *pidP = childPid;
831 *pipePP = fdopen(fd[PIPE_WRITE], "w");
832
833 if (*pipePP == NULL)
834 pm_asprintf(errorP,"Unable to create stream from pipe. "
835 "fdopen() fails with errno=%d (%s)",
836 errno, strerror(errno));
837 else
838 *errorP = NULL;
839 } else {
840 int terminationStatus;
841 close(fd[PIPE_WRITE]);
842 close(STDIN_FILENO);
843 dup2(fd[PIPE_READ], STDIN_FILENO);
844
845 terminationStatus = system(shellCmd);
846
847 if (WIFSIGNALED(terminationStatus))
848 pm_error("Shell process was killed "
849 "by a Class %u signal.",
850 WTERMSIG(terminationStatus));
851 else if (!WIFEXITED(terminationStatus))
852 pm_error("Shell process died, but its termination status "
853 "0x%x doesn't make sense", terminationStatus);
854 else
855 exit(WEXITSTATUS(terminationStatus));
856 }
857 }
858 }
859 }
860
861
862
863 static void
createFlipProcess(FILE * const outFileP,unsigned short const orientation,bool const verbose,FILE ** const inPipePP,pid_t * const pidP)864 createFlipProcess(FILE * const outFileP,
865 unsigned short const orientation,
866 bool const verbose,
867 FILE ** const inPipePP,
868 pid_t * const pidP) {
869 /*----------------------------------------------------------------------------
870 Create a process that runs the program Pamflip and writes its output
871 to *imageoutFileP.
872
873 The process takes its input from a pipe that we create. We return as
874 *inPipePP a file stream connected to the other end of that pipe.
875
876 I.e. Caller will write a Netpbm file stream to **inPipePP and a flipped
877 version of it will go to *outFileP.
878
879 The flipping it does turns the input from orientation 'orientation'
880 to Netpbm orientation, i.e. raster row 0 top, raster column 0 left,
881 where the raster stream is divided into rows, with row 0 being first
882 in the stream, and column 0 being first within each row.
883
884 Caller must close *inPipePP when he is done.
885 -----------------------------------------------------------------------------*/
886 const char * pamflipCmd;
887 const char * error;
888
889 /* Hooking up the process to the output stream is kind of tricky
890 because the stream (FILE *) is an entity local to this process.
891 We just assume that nothing in this process actually touches
892 the stream so that having the process write to the underlying
893 file descriptor is equivalent to writing to the stream.
894 */
895
896 pm_asprintf(&pamflipCmd, "pamflip -xform=%s >&%u",
897 xformNeeded(orientation), fileno(outFileP));
898
899 if (verbose)
900 pm_message("Reorienting raster with shell command '%s'", pamflipCmd);
901
902 spawnWithInputPipe(pamflipCmd, inPipePP, pidP, &error);
903
904 if (error) {
905 pm_error("Shell command '%s', to reorient the TIFF "
906 "raster, failed. %s. To work around this, you can use "
907 "the -orientraw option.", pamflipCmd, error);
908
909 pm_strfree(error);
910 }
911 }
912
913
914
915 static void
setupFlipper(pnmOut * const pnmOutP,unsigned short const orientation,bool const flipIfNeeded,bool const orientraw,bool const verbose,bool * const flipOkP,bool * const noflipOkP)916 setupFlipper(pnmOut * const pnmOutP,
917 unsigned short const orientation,
918 bool const flipIfNeeded,
919 bool const orientraw,
920 bool const verbose,
921 bool * const flipOkP,
922 bool * const noflipOkP) {
923 /*----------------------------------------------------------------------------
924 Set up the Pamflip processes to flip the raster, where needed.
925
926 Whether we need a Pamflip process is a complex decision. For
927 reasons of efficiency and robustness, we don't want one unless
928 flipping is actually required. Flipping is not required if the
929 TIFF image is already oriented like a PNM. It also isn't required
930 if the user explicitly asks for raw orientation. Finally, it's not
931 required when the user is using the TIFF library whole-image
932 conversion services, because they do the flipping and thus the
933 'pnmOut' object will see properly oriented pixels as input.
934
935 'flipIfNeeded' says to set up the flipping pipe if 'orientation'
936 indicates something other than standard PNM orientation.
937
938 'orientation' is the orientation of the TIFF raster.
939
940 'orientraw' says the final output should be in the same
941 orientation, 'orientation', not the proper PNM orientation.
942
943 *flipOkP means that as we set up the pnmOut object,
944 it is OK if Caller flips the raster on his own. *noflipOk means
945 is is OK if Caller does not flip the raster on his own. Note
946 that they are both true if the raster is already in the PNM
947 orientation, because then flipping is idempotent.
948 -----------------------------------------------------------------------------*/
949
950 if (orientation == ORIENTATION_TOPLEFT) {
951 /* Ah, the easy case. Flipping and not flipping are identical,
952 so none of the other parameters matter. Just write directly
953 to the output file and let Caller flip or not flip as he
954 wishes.
955 */
956 pnmOutP->flipping = FALSE;
957 *flipOkP = TRUE;
958 *noflipOkP = TRUE;
959 } else {
960 if (orientraw) {
961 /* Raster is not to be flipped, so go directly to file,
962 and tell Caller not to flip it either.
963 */
964 pnmOutP->flipping = FALSE;
965 *flipOkP = FALSE;
966 *noflipOkP = TRUE;
967 } else {
968 if (flipIfNeeded) {
969 if (verbose)
970 pm_message("Transforming raster with Pamflip");
971
972 if (pnmOutP->alphaFileP)
973 createFlipProcess(pnmOutP->alphaFileP, orientation,
974 verbose,
975 &pnmOutP->alphaPipeP,
976 &pnmOutP->alphaFlipPid);
977 if (pnmOutP->imageoutFileP)
978 createFlipProcess(pnmOutP->imageoutFileP, orientation,
979 verbose,
980 &pnmOutP->imagePipeP,
981 &pnmOutP->imageFlipPid);
982
983 /* The stream will flip it, so Caller must not: */
984 pnmOutP->flipping = TRUE;
985 *flipOkP = FALSE;
986 *noflipOkP = TRUE;
987 } else {
988 /* It needs flipping, but Caller doesn't want us to do it.
989 So Caller must do it:
990 */
991 pnmOutP->flipping = FALSE;
992 *flipOkP = TRUE;
993 *noflipOkP = FALSE;
994 }
995 }
996 }
997 }
998
999
1000
1001 static void
computeOutputDimensions(unsigned int const tiffCols,unsigned int const tiffRows,unsigned short const orientation,bool const orientraw,unsigned int * const colsP,unsigned int * const rowsP,bool const verbose)1002 computeOutputDimensions(unsigned int const tiffCols,
1003 unsigned int const tiffRows,
1004 unsigned short const orientation,
1005 bool const orientraw,
1006 unsigned int * const colsP,
1007 unsigned int * const rowsP,
1008 bool const verbose) {
1009 /*----------------------------------------------------------------------------
1010 Compute the dimensions of the image. We're talking about the
1011 actual image, not what the TIFF spec calls the image. What the
1012 TIFF spec calls the image is matrix of pixels within the TIFF file.
1013 That matrix can represent the actual image in various ways.
1014 E.g. the first row of the matrix might be the right edge of the
1015 image.
1016
1017 'tiffCols' and 'tiffRows' are the images of the TIFF matrix and
1018 'orientation' is the orientation of that matrix.
1019
1020 'orientraw' says we want to output the matrix in its natural
1021 orientation, not the true image.
1022 -----------------------------------------------------------------------------*/
1023 if (orientraw) {
1024 *colsP = tiffCols;
1025 *rowsP = tiffRows;
1026 } else
1027 tiffToImageDim(tiffCols, tiffRows, orientation, colsP, rowsP);
1028
1029 if (verbose)
1030 pm_message("Generating %uw x %uh PNM image", *colsP, *rowsP);
1031 }
1032
1033
1034
1035 static void
pnmOut_init(FILE * const imageoutFileP,FILE * const alphaFileP,unsigned int const cols,unsigned int const rows,unsigned short const orientation,xelval const maxval,int const format,gray const alphaMaxval,bool const flipIfNeeded,bool const orientraw,bool const verbose,bool * const flipOkP,bool * const noflipOkP,pnmOut * const pnmOutP)1036 pnmOut_init(FILE * const imageoutFileP,
1037 FILE * const alphaFileP,
1038 unsigned int const cols,
1039 unsigned int const rows,
1040 unsigned short const orientation,
1041 xelval const maxval,
1042 int const format,
1043 gray const alphaMaxval,
1044 bool const flipIfNeeded,
1045 bool const orientraw,
1046 bool const verbose,
1047 bool * const flipOkP,
1048 bool * const noflipOkP,
1049 pnmOut * const pnmOutP) {
1050 /*----------------------------------------------------------------------------
1051 'cols' and 'rows' are the dimensions of the raster matrix which is
1052 oriented according to 'orientation' with respect to the image.
1053
1054 If the user flips the data before giving it to the pnmOut object,
1055 pnmOut may see the inverse dimensions; if the pnmOut object flips the
1056 data, pnmOut get 'cols' x 'rows' data, but its output file may be
1057 'rows x cols'.
1058
1059 Because we must set up the pnmOut object either to receive flipped or not
1060 flipped input, we have *flipOkP and *noflipOkP outputs that tell Caller
1061 whether he has to flip or not. Note that Caller also influences which way
1062 we set up pnmOut, with his 'flipIfNeeded' argument. In the unique case
1063 that the TIFF matrix is already oriented the way the output PNM file needs
1064 to be, flipping is idempotent, so both *flipOkP and *noflipOkP are true.
1065 -----------------------------------------------------------------------------*/
1066 pnmOutP->imageoutFileP = imageoutFileP;
1067 pnmOutP->alphaFileP = alphaFileP;
1068 pnmOutP->maxval = maxval;
1069 pnmOutP->format = format;
1070 pnmOutP->alphaMaxval = alphaMaxval;
1071
1072 setupFlipper(pnmOutP, orientation, flipIfNeeded, orientraw, verbose,
1073 flipOkP, noflipOkP);
1074
1075 computeOutputDimensions(cols, rows, orientation, orientraw,
1076 &pnmOutP->outCols, &pnmOutP->outRows, verbose);
1077
1078 if (pnmOutP->flipping) {
1079 pnmOutP->inCols = cols; /* Caller won't flip */
1080 pnmOutP->inRows = rows;
1081 } else {
1082 pnmOutP->inCols = pnmOutP->outCols; /* Caller will flip */
1083 pnmOutP->inRows = pnmOutP->outRows;
1084 }
1085 if (pnmOutP->flipping) {
1086 if (pnmOutP->imagePipeP != NULL)
1087 pnm_writepnminit(pnmOutP->imagePipeP,
1088 pnmOutP->inCols, pnmOutP->inRows,
1089 pnmOutP->maxval, pnmOutP->format, 0);
1090 if (pnmOutP->alphaPipeP != NULL)
1091 pgm_writepgminit(pnmOutP->alphaPipeP,
1092 pnmOutP->inCols, pnmOutP->inRows,
1093 pnmOutP->alphaMaxval, 0);
1094 } else {
1095 if (imageoutFileP != NULL)
1096 pnm_writepnminit(pnmOutP->imageoutFileP,
1097 pnmOutP->outCols, pnmOutP->outRows,
1098 pnmOutP->maxval, pnmOutP->format, 0);
1099 if (alphaFileP != NULL)
1100 pgm_writepgminit(pnmOutP->alphaFileP,
1101 pnmOutP->outCols, pnmOutP->outRows,
1102 pnmOutP->alphaMaxval, 0);
1103 }
1104 }
1105
1106
1107
1108 static void
pnmOut_term(pnmOut * const pnmOutP,bool const verbose)1109 pnmOut_term(pnmOut * const pnmOutP,
1110 bool const verbose) {
1111
1112 if (pnmOutP->flipping) {
1113 /* Closing the pipes also causes the Pamflip processes to terminate
1114 and they consequently flush their output to pnmOutP->imageoutFileP
1115 and pnmOutP->alphaFileP and close those file descriptors.
1116
1117 We wait for the processes to exit before returning so that we
1118 know everything is flushed, so the invoker of Tifftopnm is free
1119 to use its output.
1120 */
1121 if (verbose)
1122 pm_message("Flushing data through Pamflip process, "
1123 "waiting for Pamflip to terminate");
1124
1125 if (pnmOutP->imagePipeP) {
1126 fclose(pnmOutP->imagePipeP);
1127 pm_waitpidSimple(pnmOutP->imageFlipPid);
1128 }
1129 if (pnmOutP->alphaPipeP) {
1130 fclose(pnmOutP->alphaPipeP);
1131 pm_waitpidSimple(pnmOutP->alphaFlipPid);
1132 }
1133 } else {
1134 if (pnmOutP->imageoutFileP)
1135 fflush(pnmOutP->imageoutFileP);
1136 if (pnmOutP->alphaFileP)
1137 fflush(pnmOutP->alphaFileP);
1138 }
1139 }
1140
1141
1142
1143 static void
pnmOut_writeRow(pnmOut * const pnmOutP,unsigned int const cols,const xel * const imageRow,const gray * const alphaRow)1144 pnmOut_writeRow(pnmOut * const pnmOutP,
1145 unsigned int const cols,
1146 const xel * const imageRow,
1147 const gray * const alphaRow) {
1148
1149 assert(cols == pnmOutP->inCols);
1150
1151 if (pnmOutP->flipping) {
1152 if (pnmOutP->imagePipeP != NULL)
1153 pnm_writepnmrow(pnmOutP->imagePipeP, (xel *)imageRow,
1154 pnmOutP->inCols, pnmOutP->maxval,
1155 pnmOutP->format, 0);
1156 if (pnmOutP->alphaPipeP != NULL)
1157 pgm_writepgmrow(pnmOutP->alphaPipeP, alphaRow,
1158 pnmOutP->inCols, pnmOutP->alphaMaxval, 0);
1159 } else {
1160 if (pnmOutP->imageoutFileP != NULL)
1161 pnm_writepnmrow(pnmOutP->imageoutFileP, (xel *)imageRow,
1162 pnmOutP->outCols, pnmOutP->maxval,
1163 pnmOutP->format, 0);
1164 if (pnmOutP->alphaFileP != NULL)
1165 pgm_writepgmrow(pnmOutP->alphaFileP, alphaRow,
1166 pnmOutP->outCols, pnmOutP->alphaMaxval, 0);
1167 }
1168 }
1169
1170
1171
1172 static void
convertRow(unsigned int const samplebuf[],xel * const xelrow,gray * const alpharow,int const cols,xelval const maxval,unsigned short const photomet,unsigned short const spp,xel const colormap[])1173 convertRow(unsigned int const samplebuf[],
1174 xel * const xelrow,
1175 gray * const alpharow,
1176 int const cols,
1177 xelval const maxval,
1178 unsigned short const photomet,
1179 unsigned short const spp,
1180 xel const colormap[]) {
1181 /*----------------------------------------------------------------------------
1182 Assuming samplebuf[] is an array of raster values as returned by the Tiff
1183 library, convert it to a libnetpbm row in xelrow[] and alpharow[].
1184 -----------------------------------------------------------------------------*/
1185 switch (photomet) {
1186 case PHOTOMETRIC_MINISBLACK: {
1187 int col;
1188 for (col = 0; col < cols; ++col) {
1189 PNM_ASSIGN1(xelrow[col], samplebuf[col]);
1190 alpharow[col] = 0;
1191 }
1192 }
1193 break;
1194
1195 case PHOTOMETRIC_MINISWHITE: {
1196 int col;
1197 for (col = 0; col < cols; ++col) {
1198 PNM_ASSIGN1(xelrow[col], maxval - samplebuf[col]);
1199 alpharow[col] = 0;
1200 }
1201 }
1202 break;
1203
1204 case PHOTOMETRIC_PALETTE: {
1205 int col;
1206 for ( col = 0; col < cols; ++col ) {
1207 /* We know the following array index is in bounds because
1208 we filled samplebuf with samples of 'bps' bits each and
1209 we verified that the largest number that fits in 'bps'
1210 bits is less than MAXCOLORS, the dimension of the array.
1211 */
1212 xelrow[col] = colormap[samplebuf[col]];
1213 alpharow[col] = 0;
1214 }
1215 }
1216 break;
1217
1218 case PHOTOMETRIC_SEPARATED: {
1219 int col, sample;
1220 for (col = 0, sample = 0; col < cols; ++col, sample+=spp) {
1221 xelval r, g, b;
1222 pick_cmyk_pixel(samplebuf, sample, &r, &b, &g);
1223
1224 PPM_ASSIGN(xelrow[col], r, g, b);
1225 alpharow[col] = 0;
1226 }
1227 }
1228 break;
1229
1230 case PHOTOMETRIC_RGB: {
1231 int col, sample;
1232 for (col = 0, sample = 0; col < cols; ++col, sample+=spp) {
1233 PPM_ASSIGN(xelrow[col], samplebuf[sample+0],
1234 samplebuf[sample+1], samplebuf[sample+2]);
1235 if (spp >= 4)
1236 alpharow[col] = samplebuf[sample+3];
1237 else
1238 alpharow[col] = 0;
1239 }
1240 break;
1241 }
1242 default:
1243 pm_error("internal error: unknown photometric in the picking "
1244 "routine: %d", photomet);
1245 }
1246 }
1247
1248
1249
1250 static void
scale32to16(unsigned int * const samplebuf,unsigned int const cols,unsigned int const spp)1251 scale32to16(unsigned int * const samplebuf,
1252 unsigned int const cols,
1253 unsigned int const spp) {
1254 /*----------------------------------------------------------------------------
1255 Convert every sample in samplebuf[] to something that can be expressed
1256 in 16 bits, assuming it takes 32 bits now.
1257 -----------------------------------------------------------------------------*/
1258 unsigned int i;
1259 for (i = 0; i < cols * spp; ++i)
1260 samplebuf[i] >>= 16;
1261 }
1262
1263
1264
1265 static void
convertMultiPlaneRow(TIFF * const tif,xel * const xelrow,gray * const alpharow,int const cols,xelval const maxval,int const row,unsigned short const photomet,unsigned short const bps,unsigned short const spp,unsigned short const fillorder,unsigned char * const scanbuf,unsigned int * const samplebuf)1266 convertMultiPlaneRow(TIFF * const tif,
1267 xel * const xelrow,
1268 gray * const alpharow,
1269 int const cols,
1270 xelval const maxval,
1271 int const row,
1272 unsigned short const photomet,
1273 unsigned short const bps,
1274 unsigned short const spp,
1275 unsigned short const fillorder,
1276 unsigned char * const scanbuf,
1277 unsigned int * const samplebuf) {
1278
1279 /* The input is in separate planes, so we need to read one
1280 scanline for the reds, another for the greens, then another
1281 for the blues.
1282 */
1283
1284 if (photomet != PHOTOMETRIC_RGB)
1285 pm_error("This is a multiple-plane file, but is not an RGB "
1286 "file. This program does not know how to handle that.");
1287 else {
1288 unsigned int col;
1289
1290 /* First, clear the buffer so we can add red, green,
1291 and blue one at a time.
1292 */
1293 for (col = 0; col < cols; ++col)
1294 PPM_ASSIGN(xelrow[col], 0, 0, 0);
1295
1296 /* Read the reds */
1297 readscanline(tif, scanbuf, row, 0, cols, bps, spp, fillorder,
1298 samplebuf);
1299 if (bps == 32)
1300 scale32to16(samplebuf, cols, spp);
1301 for (col = 0; col < cols; ++col)
1302 PPM_PUTR(xelrow[col], samplebuf[col]);
1303
1304 /* Next the greens */
1305 readscanline(tif, scanbuf, row, 1, cols, bps, spp, fillorder,
1306 samplebuf);
1307 if (bps == 32)
1308 scale32to16(samplebuf, cols, spp);
1309 for (col = 0; col < cols; ++col)
1310 PPM_PUTG( xelrow[col], samplebuf[col] );
1311
1312 /* And finally the blues */
1313 readscanline(tif, scanbuf, row, 2, cols, bps, spp, fillorder,
1314 samplebuf);
1315 if (bps == 32)
1316 scale32to16(samplebuf, cols, spp);
1317 for (col = 0; col < cols; ++col)
1318 PPM_PUTB(xelrow[col], samplebuf[col]);
1319
1320 /* Could there be an alpha plane? (We assume no. But if so,
1321 here is where to read it)
1322 */
1323 for (col = 0; col < cols; ++col)
1324 alpharow[col] = 0;
1325 }
1326 }
1327
1328
1329
1330 static void
convertRasterByRows(pnmOut * const pnmOutP,unsigned int const cols,unsigned int const rows,xelval const maxval,TIFF * const tif,unsigned short const photomet,unsigned short const planarconfig,unsigned short const bps,unsigned short const spp,unsigned short const fillorder,xel const colormap[],bool const verbose)1331 convertRasterByRows(pnmOut * const pnmOutP,
1332 unsigned int const cols,
1333 unsigned int const rows,
1334 xelval const maxval,
1335 TIFF * const tif,
1336 unsigned short const photomet,
1337 unsigned short const planarconfig,
1338 unsigned short const bps,
1339 unsigned short const spp,
1340 unsigned short const fillorder,
1341 xel const colormap[],
1342 bool const verbose) {
1343 /*----------------------------------------------------------------------------
1344 With the TIFF header all processed (and relevant information from it in
1345 our arguments), write out the TIFF raster to the Netpbm output files
1346 as described by *pnmOutP.
1347
1348 Do this one row at a time, employing the TIFF library's
1349 TIFFReadScanline.
1350 -----------------------------------------------------------------------------*/
1351 unsigned char * scanbuf;
1352 /* Buffer for a raster line in the format returned by TIFF library's
1353 TIFFReadScanline
1354 */
1355 unsigned int * samplebuf;
1356 /* Same info as 'scanbuf' above, but with each raster column (sample)
1357 represented as single array element, so it's easy to work with.
1358 */
1359 xel * xelrow;
1360 /* The ppm-format row of the image row we are currently converting */
1361 gray * alpharow;
1362 /* The pgm-format row representing the alpha values for the image
1363 row we are currently converting.
1364 */
1365
1366 unsigned int row;
1367
1368 if (verbose)
1369 pm_message("Converting row by row ...");
1370
1371 MALLOCARRAY(scanbuf, TIFFScanlineSize(tif));
1372 if (scanbuf == NULL)
1373 pm_error("can't allocate memory for scanline buffer");
1374
1375 MALLOCARRAY(samplebuf, cols * spp);
1376 if (samplebuf == NULL)
1377 pm_error("can't allocate memory for row buffer");
1378
1379 xelrow = pnm_allocrow(cols);
1380 alpharow = pgm_allocrow(cols);
1381
1382 for (row = 0; row < rows; ++row) {
1383 /* Read one row of samples into samplebuf[] */
1384
1385 if (planarconfig == PLANARCONFIG_CONTIG) {
1386 readscanline(tif, scanbuf, row, 0, cols, bps, spp, fillorder,
1387 samplebuf);
1388 if (bps == 32)
1389 scale32to16(samplebuf, cols, spp);
1390 convertRow(samplebuf, xelrow, alpharow, cols, maxval,
1391 photomet, spp, colormap);
1392 } else
1393 convertMultiPlaneRow(tif, xelrow, alpharow, cols, maxval, row,
1394 photomet, bps, spp, fillorder,
1395 scanbuf, samplebuf);
1396
1397 pnmOut_writeRow(pnmOutP, cols, xelrow, alpharow);
1398 }
1399 pgm_freerow(alpharow);
1400 pnm_freerow(xelrow);
1401
1402 free(samplebuf);
1403 free(scanbuf);
1404 }
1405
1406
1407
1408 static void
warnBrokenTiffLibrary(TIFF * const tiffP)1409 warnBrokenTiffLibrary(TIFF * const tiffP) {
1410
1411 /* TIFF library bug:
1412
1413 In every version of the TIFF library we've seen, TIFFRGBAImageGet()
1414 fails when the raster orientation (per the TIFF_ORIENTATION tag)
1415 requires a transposition, e.g. ORIENTATION_LEFTBOT. It simply omits
1416 the transposition part, so e.g. it treats ORIENTATION_LEFTBOT as
1417 ORIENTATION_BOTLEFT. And because we provide a raster buffer dimensioned
1418 for the properly transposed image, the result is somewhat of a mess.
1419
1420 We have found no documentation of the TIFF library that suggests
1421 this behavior is as designed, so it's probably not a good idea to
1422 work around it; it might be fixed somewhere.
1423
1424 The user can of course work around just by using -byrow and therefore
1425 not using TIFFRGBAImageGet().
1426
1427 There is some evidence of an interface in the TIFF library that
1428 lets you request that TIFFRGBAImageGet() produce a raster in the
1429 same orientation as the one in the TIFF image.
1430 (tiff.req_orientation). We could conceivably use that and then do
1431 a Pamflip to get the proper orientation, but that somewhat defeats
1432 the philosophy of using TIFFRGBAImageGet(), so I would like to wait
1433 until there's a good practical reason to do it.
1434 */
1435
1436 unsigned short tiffOrientation;
1437 int fldPresent;
1438 fldPresent = TIFFGetField(tiffP, TIFFTAG_ORIENTATION, &tiffOrientation);
1439 if (fldPresent) {
1440 switch (tiffOrientation) {
1441 case ORIENTATION_LEFTTOP:
1442 case ORIENTATION_RIGHTTOP:
1443 case ORIENTATION_RIGHTBOT:
1444 case ORIENTATION_LEFTBOT:
1445 pm_message("WARNING: This TIFF image has an orientation that "
1446 "most TIFF libraries convert incorrectly. "
1447 "Use -byrow to circumvent.");
1448 break;
1449 }
1450 }
1451 }
1452
1453
1454
1455 static void
convertTiffRaster(uint32 * const raster,unsigned int const cols,unsigned int const rows,xelval const maxval,pnmOut * const pnmOutP)1456 convertTiffRaster(uint32 * const raster,
1457 unsigned int const cols,
1458 unsigned int const rows,
1459 xelval const maxval,
1460 pnmOut * const pnmOutP) {
1461 /*----------------------------------------------------------------------------
1462 Convert the raster 'raster' from the format generated by the TIFF library
1463 to PPM (plus PGM alpha mask where applicable) and output it to
1464 the object *pnmOutP. The raster is 'cols' wide by 'rows' high.
1465 -----------------------------------------------------------------------------*/
1466 xel * xelrow;
1467 /* The ppm-format row of the image row we are
1468 currently converting
1469 */
1470 gray * alpharow;
1471 /* The pgm-format row representing the alpha values
1472 for the image row we are currently converting.
1473 */
1474 unsigned int row;
1475
1476 xelrow = pnm_allocrow(cols);
1477 alpharow = pgm_allocrow(cols);
1478
1479 for (row = 0; row < rows; ++row) {
1480 uint32 * rp;
1481 /* Address of pixel in 'raster' we are currently converting */
1482 unsigned int col;
1483
1484 /* Start at beginning of row: */
1485 rp = raster + (rows - row - 1) * cols;
1486
1487 for (col = 0; col < cols; ++col) {
1488 uint32 const tiffPixel = *rp++;
1489
1490 PPM_ASSIGN(xelrow[col],
1491 TIFFGetR(tiffPixel) * maxval / 255,
1492 TIFFGetG(tiffPixel) * maxval / 255,
1493 TIFFGetB(tiffPixel) * maxval / 255);
1494 alpharow[col] = TIFFGetA(tiffPixel) * maxval / 255 ;
1495 }
1496 pnmOut_writeRow(pnmOutP, cols, xelrow, alpharow);
1497 }
1498
1499 pgm_freerow(alpharow);
1500 pnm_freerow(xelrow);
1501 }
1502
1503
1504
1505 enum convertDisp {CONV_DONE,
1506 CONV_OOM,
1507 CONV_UNABLE,
1508 CONV_FAILED,
1509 CONV_NOTATTEMPTED};
1510
1511
1512 static void
convertRasterIntoProvidedMemory(pnmOut * const pnmOutP,unsigned int const cols,unsigned int const rows,xelval const maxval,TIFF * const tif,bool const verbose,uint32 * const raster,enum convertDisp * const statusP)1513 convertRasterIntoProvidedMemory(pnmOut * const pnmOutP,
1514 unsigned int const cols,
1515 unsigned int const rows,
1516 xelval const maxval,
1517 TIFF * const tif,
1518 bool const verbose,
1519 uint32 * const raster,
1520 enum convertDisp * const statusP) {
1521
1522 int const stopOnErrorFalse = false;
1523
1524 TIFFRGBAImage img;
1525 char emsg[1024];
1526 int ok;
1527
1528 ok = TIFFRGBAImageBegin(&img, tif, stopOnErrorFalse, emsg);
1529 if (!ok) {
1530 pm_message("%s", emsg);
1531 *statusP = CONV_FAILED;
1532 } else {
1533 int ok;
1534 ok = TIFFRGBAImageGet(&img, raster, cols, rows);
1535 TIFFRGBAImageEnd(&img) ;
1536 if (!ok) {
1537 pm_message("%s", emsg);
1538 *statusP = CONV_FAILED;
1539 } else {
1540 *statusP = CONV_DONE;
1541 convertTiffRaster(raster, cols, rows, maxval, pnmOutP);
1542 }
1543 }
1544 }
1545
1546
1547
1548 static void
convertRasterInMemory(pnmOut * const pnmOutP,xelval const maxval,TIFF * const tif,bool const verbose,enum convertDisp * const statusP)1549 convertRasterInMemory(pnmOut * const pnmOutP,
1550 xelval const maxval,
1551 TIFF * const tif,
1552 bool const verbose,
1553 enum convertDisp * const statusP) {
1554 /*----------------------------------------------------------------------------
1555 With the TIFF header all processed (and relevant information from
1556 it in our arguments), write out the TIFF raster to the file images
1557 *imageoutFileP and *alphaFileP.
1558
1559 Do this by reading the entire TIFF image into memory at once and formatting
1560 it with the TIFF library's TIFFRGBAImageGet().
1561
1562 'cols' and 'rows' are the dimensions of the actual image, not of the
1563 TIFF raster matrix; ('tif' knows the TIFF raster matrix dimensions).
1564
1565 Return *statusP == CONV_OOM iff we are unable to proceed because we cannot
1566 get memory to store the entire raster. This means Caller may still be able
1567 to do the conversion using a row-by-row strategy. Like typical Netpbm
1568 programs, we simply abort the program if we are unable to allocate
1569 memory for other things.
1570 -----------------------------------------------------------------------------*/
1571 char emsg[1024];
1572 int ok;
1573
1574 if (verbose)
1575 pm_message("Converting in memory ...");
1576
1577 warnBrokenTiffLibrary(tif);
1578
1579 ok = TIFFRGBAImageOK(tif, emsg);
1580 if (!ok) {
1581 pm_message("%s", emsg);
1582 *statusP = CONV_UNABLE;
1583 } else {
1584 unsigned int cols, rows; /* Dimensions of output image */
1585 getTiffDimensions(tif, &cols, &rows);
1586
1587 if (rows == 0 || cols == 0)
1588 *statusP = CONV_DONE;
1589 else {
1590 if (cols > UINT_MAX/rows) {
1591 pm_message("%u rows of %u columns is too large to compute",
1592 rows, cols);
1593 *statusP = CONV_OOM;
1594 } else {
1595 unsigned int const pixelCt = rows * cols;
1596
1597 uint32 * raster;
1598
1599 /* Note that TIFFRGBAImageGet() converts any bits per sample
1600 to 8. Maxval of the raster it returns is always 255.
1601 */
1602 MALLOCARRAY(raster, pixelCt);
1603 if (raster == NULL) {
1604 pm_message("Unable to allocate space for a raster of %u "
1605 "pixels.", pixelCt);
1606 *statusP = CONV_OOM;
1607 } else {
1608 convertRasterIntoProvidedMemory(
1609 pnmOutP, cols, rows, maxval, tif, verbose,
1610 raster, statusP);
1611
1612 free(raster);
1613 }
1614 }
1615 }
1616 }
1617 }
1618
1619
1620
1621 static void
convertRaster(pnmOut * const pnmOutP,TIFF * const tifP,struct tiffDirInfo const tiffDir,xelval const maxval,unsigned short const fillorder,const xel * const colormap,bool const byrow,bool const flipOk,bool const noflipOk,bool const verbose)1622 convertRaster(pnmOut * const pnmOutP,
1623 TIFF * const tifP,
1624 struct tiffDirInfo const tiffDir,
1625 xelval const maxval,
1626 unsigned short const fillorder,
1627 const xel * const colormap,
1628 bool const byrow,
1629 bool const flipOk,
1630 bool const noflipOk,
1631 bool const verbose) {
1632
1633 enum convertDisp status;
1634
1635 if (byrow || !flipOk)
1636 status = CONV_NOTATTEMPTED;
1637 else {
1638 convertRasterInMemory(pnmOutP, maxval, tifP, verbose, &status);
1639 }
1640 if (status == CONV_DONE) {
1641 if (tiffDir.bps > 8)
1642 pm_message("actual resolution has been reduced to 24 bits "
1643 "per pixel in the conversion. You can get the "
1644 "full %u bits that are in the TIFF with the "
1645 "-byrow option.", tiffDir.bps);
1646 } else {
1647 if (status != CONV_NOTATTEMPTED) {
1648 pm_message("In-memory conversion failed; "
1649 "using more primitive row-by-row conversion.");
1650
1651 if (!noflipOk)
1652 pm_error("TIFF raster is in nonstandard orientation, "
1653 "and we already committed to in-memory "
1654 "conversion. To avoid this failure, "
1655 "use -byrow .");
1656 }
1657 convertRasterByRows(
1658 pnmOutP, tiffDir.width, tiffDir.height, maxval,
1659 tifP, tiffDir.photomet, tiffDir.planarconfig,
1660 tiffDir.bps, tiffDir.spp, fillorder, colormap, verbose);
1661 }
1662 }
1663
1664
1665
1666 static void
convertImage(TIFF * const tifP,FILE * const alphaFileP,FILE * const imageoutFileP,struct CmdlineInfo const cmdline)1667 convertImage(TIFF * const tifP,
1668 FILE * const alphaFileP,
1669 FILE * const imageoutFileP,
1670 struct CmdlineInfo const cmdline) {
1671
1672 struct tiffDirInfo tiffDir;
1673 int format;
1674 xelval maxval;
1675 xel colormap[MAXCOLORS];
1676 unsigned short fillorder;
1677 bool flipOk, noflipOk;
1678 pnmOut pnmOut;
1679
1680 readDirectory(tifP, cmdline.headerdump, &tiffDir);
1681
1682 computeFillorder(tiffDir.fillorder, &fillorder, cmdline.respectfillorder);
1683
1684 analyzeImageType(tifP, tiffDir.bps, tiffDir.spp, tiffDir.photomet,
1685 &maxval, &format, colormap, cmdline.headerdump, cmdline);
1686
1687 reportOutputFormat(format);
1688
1689 pnmOut_init(imageoutFileP, alphaFileP, tiffDir.width, tiffDir.height,
1690 tiffDir.orientation, maxval, format, maxval,
1691 cmdline.byrow, cmdline.orientraw,
1692 cmdline.verbose,
1693 &flipOk, &noflipOk,
1694 &pnmOut);
1695
1696 convertRaster(&pnmOut, tifP, tiffDir, maxval,
1697 fillorder, colormap, cmdline.byrow, flipOk, noflipOk,
1698 cmdline.verbose);
1699
1700 pnmOut_term(&pnmOut, cmdline.verbose);
1701 }
1702
1703
1704
1705 static void
convertIt(TIFF * const tifP,FILE * const alphaFile,FILE * const imageoutFile,struct CmdlineInfo const cmdline)1706 convertIt(TIFF * const tifP,
1707 FILE * const alphaFile,
1708 FILE * const imageoutFile,
1709 struct CmdlineInfo const cmdline) {
1710
1711 unsigned int imageSeq;
1712 bool eof;
1713
1714 imageSeq = 0;
1715 eof = FALSE;
1716
1717 while (!eof) {
1718 bool success;
1719
1720 if (cmdline.verbose)
1721 pm_message("Converting Image %u", imageSeq);
1722 convertImage(tifP, alphaFile, imageoutFile, cmdline);
1723 success = TIFFReadDirectory(tifP);
1724 eof = !success;
1725 ++imageSeq;
1726 }
1727 }
1728
1729
1730
1731 int
main(int argc,const char * argv[])1732 main(int argc, const char * argv[]) {
1733
1734 struct CmdlineInfo cmdline;
1735 TIFF * tiffP;
1736 FILE * alphaFile;
1737 FILE * imageoutFile;
1738
1739 pm_proginit(&argc, argv);
1740
1741 parseCommandLine(argc, argv, &cmdline);
1742
1743 tiffP = newTiffImageObject(cmdline.inputFilename);
1744
1745 if (cmdline.alphaStdout)
1746 alphaFile = stdout;
1747 else if (cmdline.alphaFilename == NULL)
1748 alphaFile = NULL;
1749 else
1750 alphaFile = pm_openw(cmdline.alphaFilename);
1751
1752 if (cmdline.alphaStdout)
1753 imageoutFile = NULL;
1754 else
1755 imageoutFile = stdout;
1756
1757 convertIt(tiffP, alphaFile, imageoutFile, cmdline);
1758
1759 if (imageoutFile != NULL)
1760 pm_close( imageoutFile );
1761 if (alphaFile != NULL)
1762 pm_close( alphaFile );
1763
1764 TIFFClose(tiffP);
1765
1766 pm_strfree(cmdline.inputFilename);
1767
1768 /* If the program failed, it previously aborted with nonzero completion
1769 code, via various function calls.
1770 */
1771 return 0;
1772 }
1773
1774
1775
1776