4 * Copyright (c) 1988-1997 Sam Leffler
5 * Copyright (c) 1991-1997 Silicon Graphics, Inc.
7 * Permission to use, copy, modify, distribute, and sell this software and
8 * its documentation for any purpose is hereby granted without fee, provided
9 * that (i) the above copyright notices and this permission notice appear in
10 * all copies of the software and related documentation, and (ii) the names of
11 * Sam Leffler and Silicon Graphics may not be used in any advertising or
12 * publicity relating to the software without the specific, prior written
13 * permission of Sam Leffler and Silicon Graphics.
15 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
17 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
19 * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
20 * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
21 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
22 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
23 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
27 #include "tif_config.h"
44 #define streq(a,b) (strcmp((a),(b)) == 0)
45 #define strneq(a,b,n) (strncmp(a,b,n) == 0)
47 /* x% weighting -> fraction of full color */
48 #define PCT(x) (((x)*255+127)/100)
49 int RED
= PCT(30); /* 30% */
50 int GREEN
= PCT(59); /* 59% */
51 int BLUE
= PCT(11); /* 11% */
53 static void usage(void);
54 static int processCompressOptions(char*);
57 compresscontig(unsigned char* out
, unsigned char* rgb
, uint32 n
)
59 register int v
, red
= RED
, green
= GREEN
, blue
= BLUE
;
70 compresssep(unsigned char* out
,
71 unsigned char* r
, unsigned char* g
, unsigned char* b
, uint32 n
)
73 register uint32 red
= RED
, green
= GREEN
, blue
= BLUE
;
76 *out
++ = (unsigned char)
77 ((red
*(*r
++) + green
*(*g
++) + blue
*(*b
++)) >> 8);
81 checkcmap(TIFF
* tif
, int n
, uint16
* r
, uint16
* g
, uint16
* b
)
84 if (*r
++ >= 256 || *g
++ >= 256 || *b
++ >= 256)
86 TIFFWarning(TIFFFileName(tif
), "Assuming 8-bit colormap");
91 compresspalette(unsigned char* out
, unsigned char* data
, uint32 n
, uint16
* rmap
, uint16
* gmap
, uint16
* bmap
)
93 register int v
, red
= RED
, green
= GREEN
, blue
= BLUE
;
96 unsigned int ix
= *data
++;
104 static uint16 compression
= (uint16
) -1;
105 static uint16 predictor
= 0;
106 static int jpegcolormode
= JPEGCOLORMODE_RGB
;
107 static int quality
= 75; /* JPEG quality */
109 static void cpTags(TIFF
* in
, TIFF
* out
);
112 main(int argc
, char* argv
[])
114 uint32 rowsperstrip
= (uint32
) -1;
117 uint16 samplesperpixel
;
118 uint16 bitspersample
;
126 register tsample_t s
;
127 unsigned char *inbuf
, *outbuf
;
133 while ((c
= getopt(argc
, argv
, "c:r:R:G:B:")) != -1)
135 case 'c': /* compression scheme */
136 if (!processCompressOptions(optarg
))
139 case 'r': /* rows/strip */
140 rowsperstrip
= atoi(optarg
);
143 RED
= PCT(atoi(optarg
));
146 GREEN
= PCT(atoi(optarg
));
149 BLUE
= PCT(atoi(optarg
));
155 if (argc
- optind
< 2)
157 in
= TIFFOpen(argv
[optind
], "r");
161 TIFFGetField(in
, TIFFTAG_PHOTOMETRIC
, &photometric
);
162 if (photometric
!= PHOTOMETRIC_RGB
&& photometric
!= PHOTOMETRIC_PALETTE
) {
164 "%s: Bad photometric; can only handle RGB and Palette images.\n",
168 TIFFGetField(in
, TIFFTAG_SAMPLESPERPIXEL
, &samplesperpixel
);
169 if (samplesperpixel
!= 1 && samplesperpixel
!= 3) {
170 fprintf(stderr
, "%s: Bad samples/pixel %u.\n",
171 argv
[optind
], samplesperpixel
);
174 TIFFGetField(in
, TIFFTAG_BITSPERSAMPLE
, &bitspersample
);
175 if (bitspersample
!= 8) {
177 " %s: Sorry, only handle 8-bit samples.\n", argv
[optind
]);
180 TIFFGetField(in
, TIFFTAG_IMAGEWIDTH
, &w
);
181 TIFFGetField(in
, TIFFTAG_IMAGELENGTH
, &h
);
182 TIFFGetField(in
, TIFFTAG_PLANARCONFIG
, &config
);
184 out
= TIFFOpen(argv
[optind
+1], "w");
187 TIFFSetField(out
, TIFFTAG_IMAGEWIDTH
, w
);
188 TIFFSetField(out
, TIFFTAG_IMAGELENGTH
, h
);
189 TIFFSetField(out
, TIFFTAG_BITSPERSAMPLE
, 8);
190 TIFFSetField(out
, TIFFTAG_SAMPLESPERPIXEL
, 1);
191 TIFFSetField(out
, TIFFTAG_PLANARCONFIG
, PLANARCONFIG_CONTIG
);
193 if (compression
!= (uint16
) -1) {
194 TIFFSetField(out
, TIFFTAG_COMPRESSION
, compression
);
195 switch (compression
) {
196 case COMPRESSION_JPEG
:
197 TIFFSetField(out
, TIFFTAG_JPEGQUALITY
, quality
);
198 TIFFSetField(out
, TIFFTAG_JPEGCOLORMODE
, jpegcolormode
);
200 case COMPRESSION_LZW
:
201 case COMPRESSION_DEFLATE
:
203 TIFFSetField(out
, TIFFTAG_PREDICTOR
, predictor
);
207 TIFFSetField(out
, TIFFTAG_PHOTOMETRIC
, PHOTOMETRIC_MINISBLACK
);
208 sprintf(thing
, "B&W version of %s", argv
[optind
]);
209 TIFFSetField(out
, TIFFTAG_IMAGEDESCRIPTION
, thing
);
210 TIFFSetField(out
, TIFFTAG_SOFTWARE
, "tiff2bw");
211 outbuf
= (unsigned char *)_TIFFmalloc(TIFFScanlineSize(out
));
212 TIFFSetField(out
, TIFFTAG_ROWSPERSTRIP
,
213 TIFFDefaultStripSize(out
, rowsperstrip
));
215 #define pack(a,b) ((a)<<8 | (b))
216 switch (pack(photometric
, config
)) {
217 case pack(PHOTOMETRIC_PALETTE
, PLANARCONFIG_CONTIG
):
218 case pack(PHOTOMETRIC_PALETTE
, PLANARCONFIG_SEPARATE
):
219 TIFFGetField(in
, TIFFTAG_COLORMAP
, &red
, &green
, &blue
);
221 * Convert 16-bit colormap to 8-bit (unless it looks
222 * like an old-style 8-bit colormap).
224 if (checkcmap(in
, 1<<bitspersample
, red
, green
, blue
) == 16) {
226 #define CVT(x) (((x) * 255L) / ((1L<<16)-1))
227 for (i
= (1<<bitspersample
)-1; i
>= 0; i
--) {
228 red
[i
] = CVT(red
[i
]);
229 green
[i
] = CVT(green
[i
]);
230 blue
[i
] = CVT(blue
[i
]);
234 inbuf
= (unsigned char *)_TIFFmalloc(TIFFScanlineSize(in
));
235 for (row
= 0; row
< h
; row
++) {
236 if (TIFFReadScanline(in
, inbuf
, row
, 0) < 0)
238 compresspalette(outbuf
, inbuf
, w
, red
, green
, blue
);
239 if (TIFFWriteScanline(out
, outbuf
, row
, 0) < 0)
243 case pack(PHOTOMETRIC_RGB
, PLANARCONFIG_CONTIG
):
244 inbuf
= (unsigned char *)_TIFFmalloc(TIFFScanlineSize(in
));
245 for (row
= 0; row
< h
; row
++) {
246 if (TIFFReadScanline(in
, inbuf
, row
, 0) < 0)
248 compresscontig(outbuf
, inbuf
, w
);
249 if (TIFFWriteScanline(out
, outbuf
, row
, 0) < 0)
253 case pack(PHOTOMETRIC_RGB
, PLANARCONFIG_SEPARATE
):
254 rowsize
= TIFFScanlineSize(in
);
255 inbuf
= (unsigned char *)_TIFFmalloc(3*rowsize
);
256 for (row
= 0; row
< h
; row
++) {
257 for (s
= 0; s
< 3; s
++)
258 if (TIFFReadScanline(in
,
259 inbuf
+s
*rowsize
, row
, s
) < 0)
262 inbuf
, inbuf
+rowsize
, inbuf
+2*rowsize
, w
);
263 if (TIFFWriteScanline(out
, outbuf
, row
, 0) < 0)
274 processCompressOptions(char* opt
)
276 if (streq(opt
, "none"))
277 compression
= COMPRESSION_NONE
;
278 else if (streq(opt
, "packbits"))
279 compression
= COMPRESSION_PACKBITS
;
280 else if (strneq(opt
, "jpeg", 4)) {
281 char* cp
= strchr(opt
, ':');
283 compression
= COMPRESSION_JPEG
;
286 if (isdigit((int)cp
[1]))
287 quality
= atoi(cp
+1);
288 else if (cp
[1] == 'r' )
289 jpegcolormode
= JPEGCOLORMODE_RAW
;
293 cp
= strchr(cp
+1,':');
295 } else if (strneq(opt
, "lzw", 3)) {
296 char* cp
= strchr(opt
, ':');
298 predictor
= atoi(cp
+1);
299 compression
= COMPRESSION_LZW
;
300 } else if (strneq(opt
, "zip", 3)) {
301 char* cp
= strchr(opt
, ':');
303 predictor
= atoi(cp
+1);
304 compression
= COMPRESSION_DEFLATE
;
310 #define CopyField(tag, v) \
311 if (TIFFGetField(in, tag, &v)) TIFFSetField(out, tag, v)
312 #define CopyField2(tag, v1, v2) \
313 if (TIFFGetField(in, tag, &v1, &v2)) TIFFSetField(out, tag, v1, v2)
314 #define CopyField3(tag, v1, v2, v3) \
315 if (TIFFGetField(in, tag, &v1, &v2, &v3)) TIFFSetField(out, tag, v1, v2, v3)
316 #define CopyField4(tag, v1, v2, v3, v4) \
317 if (TIFFGetField(in, tag, &v1, &v2, &v3, &v4)) TIFFSetField(out, tag, v1, v2, v3, v4)
320 cpTag(TIFF
* in
, TIFF
* out
, uint16 tag
, uint16 count
, TIFFDataType type
)
326 CopyField(tag
, shortv
);
327 } else if (count
== 2) {
328 uint16 shortv1
, shortv2
;
329 CopyField2(tag
, shortv1
, shortv2
);
330 } else if (count
== 4) {
331 uint16
*tr
, *tg
, *tb
, *ta
;
332 CopyField4(tag
, tr
, tg
, tb
, ta
);
333 } else if (count
== (uint16
) -1) {
336 CopyField2(tag
, shortv1
, shortav
);
341 CopyField(tag
, longv
);
347 CopyField(tag
, floatv
);
348 } else if (count
== (uint16
) -1) {
350 CopyField(tag
, floatav
);
355 CopyField(tag
, stringv
);
361 CopyField(tag
, doublev
);
362 } else if (count
== (uint16
) -1) {
364 CopyField(tag
, doubleav
);
368 TIFFError(TIFFFileName(in
),
369 "Data type %d is not supported, tag %d skipped.",
379 static struct cpTag
{
384 { TIFFTAG_SUBFILETYPE
, 1, TIFF_LONG
},
385 { TIFFTAG_THRESHHOLDING
, 1, TIFF_SHORT
},
386 { TIFFTAG_DOCUMENTNAME
, 1, TIFF_ASCII
},
387 { TIFFTAG_IMAGEDESCRIPTION
, 1, TIFF_ASCII
},
388 { TIFFTAG_MAKE
, 1, TIFF_ASCII
},
389 { TIFFTAG_MODEL
, 1, TIFF_ASCII
},
390 { TIFFTAG_MINSAMPLEVALUE
, 1, TIFF_SHORT
},
391 { TIFFTAG_MAXSAMPLEVALUE
, 1, TIFF_SHORT
},
392 { TIFFTAG_XRESOLUTION
, 1, TIFF_RATIONAL
},
393 { TIFFTAG_YRESOLUTION
, 1, TIFF_RATIONAL
},
394 { TIFFTAG_PAGENAME
, 1, TIFF_ASCII
},
395 { TIFFTAG_XPOSITION
, 1, TIFF_RATIONAL
},
396 { TIFFTAG_YPOSITION
, 1, TIFF_RATIONAL
},
397 { TIFFTAG_RESOLUTIONUNIT
, 1, TIFF_SHORT
},
398 { TIFFTAG_SOFTWARE
, 1, TIFF_ASCII
},
399 { TIFFTAG_DATETIME
, 1, TIFF_ASCII
},
400 { TIFFTAG_ARTIST
, 1, TIFF_ASCII
},
401 { TIFFTAG_HOSTCOMPUTER
, 1, TIFF_ASCII
},
402 { TIFFTAG_WHITEPOINT
, 2, TIFF_RATIONAL
},
403 { TIFFTAG_PRIMARYCHROMATICITIES
,(uint16
) -1,TIFF_RATIONAL
},
404 { TIFFTAG_HALFTONEHINTS
, 2, TIFF_SHORT
},
405 { TIFFTAG_INKSET
, 1, TIFF_SHORT
},
406 { TIFFTAG_DOTRANGE
, 2, TIFF_SHORT
},
407 { TIFFTAG_TARGETPRINTER
, 1, TIFF_ASCII
},
408 { TIFFTAG_SAMPLEFORMAT
, 1, TIFF_SHORT
},
409 { TIFFTAG_YCBCRCOEFFICIENTS
, (uint16
) -1,TIFF_RATIONAL
},
410 { TIFFTAG_YCBCRSUBSAMPLING
, 2, TIFF_SHORT
},
411 { TIFFTAG_YCBCRPOSITIONING
, 1, TIFF_SHORT
},
412 { TIFFTAG_REFERENCEBLACKWHITE
, (uint16
) -1,TIFF_RATIONAL
},
413 { TIFFTAG_EXTRASAMPLES
, (uint16
) -1, TIFF_SHORT
},
414 { TIFFTAG_SMINSAMPLEVALUE
, 1, TIFF_DOUBLE
},
415 { TIFFTAG_SMAXSAMPLEVALUE
, 1, TIFF_DOUBLE
},
416 { TIFFTAG_STONITS
, 1, TIFF_DOUBLE
},
418 #define NTAGS (sizeof (tags) / sizeof (tags[0]))
421 cpTags(TIFF
* in
, TIFF
* out
)
424 for (p
= tags
; p
< &tags
[NTAGS
]; p
++)
425 cpTag(in
, out
, p
->tag
, p
->count
, p
->type
);
430 "usage: tiff2bw [options] input.tif output.tif",
431 "where options are:",
432 " -R % use #% from red channel",
433 " -G % use #% from green channel",
434 " -B % use #% from blue channel",
436 " -r # make each strip have no more than # rows",
438 " -c lzw[:opts] compress output with Lempel-Ziv & Welch encoding",
439 " -c zip[:opts] compress output with deflate encoding",
440 " -c packbits compress output with packbits encoding",
441 " -c g3[:opts] compress output with CCITT Group 3 encoding",
442 " -c g4 compress output with CCITT Group 4 encoding",
443 " -c none use no compression algorithm on output",
445 "LZW and deflate options:",
446 " # set predictor value",
447 "For example, -c lzw:2 to get LZW-encoded data with horizontal differencing",
458 fprintf(stderr
, "%s\n\n", TIFFGetVersion());
459 for (i
= 0; stuff
[i
] != NULL
; i
++)
460 fprintf(stderr
, "%s\n", stuff
[i
]);
464 /* vim: set ts=8 sts=8 sw=8 noet: */