1 /* $Id: pal2rgb.c,v 1.15 2015-06-21 01:09:10 bfriesen Exp $ */
2 
3 /*
4  * Copyright (c) 1988-1997 Sam Leffler
5  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
6  *
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.
14  *
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.
18  *
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
24  * OF THIS SOFTWARE.
25  */
26 
27 #include "tif_config.h"
28 
29 #include <stdio.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <ctype.h>
33 
34 #ifdef HAVE_UNISTD_H
35 # include <unistd.h>
36 #endif
37 
38 #ifdef NEED_LIBPORT
39 # include "libport.h"
40 #endif
41 
42 #include "tiffio.h"
43 
44 #define	streq(a,b)	(strcmp(a,b) == 0)
45 #define	strneq(a,b,n)	(strncmp(a,b,n) == 0)
46 
47 static	void usage(void);
48 static	void cpTags(TIFF* in, TIFF* out);
49 
50 static int
checkcmap(int n,uint16 * r,uint16 * g,uint16 * b)51 checkcmap(int n, uint16* r, uint16* g, uint16* b)
52 {
53 	while (n-- > 0)
54 	    if (*r++ >= 256 || *g++ >= 256 || *b++ >= 256)
55 		return (16);
56 	fprintf(stderr, "Warning, assuming 8-bit colormap.\n");
57 	return (8);
58 }
59 
60 #define	CopyField(tag, v) \
61     if (TIFFGetField(in, tag, &v)) TIFFSetField(out, tag, v)
62 #define	CopyField3(tag, v1, v2, v3) \
63     if (TIFFGetField(in, tag, &v1, &v2, &v3)) TIFFSetField(out, tag, v1, v2, v3)
64 
65 static	uint16 compression = (uint16) -1;
66 static	uint16 predictor = 0;
67 static	int quality = 75;	/* JPEG quality */
68 static	int jpegcolormode = JPEGCOLORMODE_RGB;
69 static	int processCompressOptions(char*);
70 
71 int
main(int argc,char * argv[])72 main(int argc, char* argv[])
73 {
74 	uint16 bitspersample, shortv;
75 	uint32 imagewidth, imagelength;
76 	uint16 config = PLANARCONFIG_CONTIG;
77 	uint32 rowsperstrip = (uint32) -1;
78 	uint16 photometric = PHOTOMETRIC_RGB;
79 	uint16 *rmap, *gmap, *bmap;
80 	uint32 row;
81 	int cmap = -1;
82 	TIFF *in, *out;
83 	int c;
84 
85 #if !HAVE_DECL_OPTARG
86 	extern int optind;
87 	extern char* optarg;
88 #endif
89 
90 	while ((c = getopt(argc, argv, "C:c:p:r:")) != -1)
91 		switch (c) {
92 		case 'C':		/* force colormap interpretation */
93 			cmap = atoi(optarg);
94 			break;
95 		case 'c':		/* compression scheme */
96 			if (!processCompressOptions(optarg))
97 				usage();
98 			break;
99 		case 'p':		/* planar configuration */
100 			if (streq(optarg, "separate"))
101 				config = PLANARCONFIG_SEPARATE;
102 			else if (streq(optarg, "contig"))
103 				config = PLANARCONFIG_CONTIG;
104 			else
105 				usage();
106 			break;
107 		case 'r':		/* rows/strip */
108 			rowsperstrip = atoi(optarg);
109 			break;
110 		case '?':
111 			usage();
112 			/*NOTREACHED*/
113 		}
114 	if (argc - optind != 2)
115 		usage();
116 	in = TIFFOpen(argv[optind], "r");
117 	if (in == NULL)
118 		return (-1);
119 	if (!TIFFGetField(in, TIFFTAG_PHOTOMETRIC, &shortv) ||
120 	    shortv != PHOTOMETRIC_PALETTE) {
121 		fprintf(stderr, "%s: Expecting a palette image.\n",
122 		    argv[optind]);
123 		return (-1);
124 	}
125 	if (!TIFFGetField(in, TIFFTAG_COLORMAP, &rmap, &gmap, &bmap)) {
126 		fprintf(stderr,
127 		    "%s: No colormap (not a valid palette image).\n",
128 		    argv[optind]);
129 		return (-1);
130 	}
131 	bitspersample = 0;
132 	TIFFGetField(in, TIFFTAG_BITSPERSAMPLE, &bitspersample);
133 	if (bitspersample != 8) {
134 		fprintf(stderr, "%s: Sorry, can only handle 8-bit images.\n",
135 		    argv[optind]);
136 		return (-1);
137 	}
138 	out = TIFFOpen(argv[optind+1], "w");
139 	if (out == NULL)
140 		return (-2);
141 	cpTags(in, out);
142 	TIFFGetField(in, TIFFTAG_IMAGEWIDTH, &imagewidth);
143 	TIFFGetField(in, TIFFTAG_IMAGELENGTH, &imagelength);
144 	if (compression != (uint16)-1)
145 		TIFFSetField(out, TIFFTAG_COMPRESSION, compression);
146 	else
147 		TIFFGetField(in, TIFFTAG_COMPRESSION, &compression);
148 	switch (compression) {
149 	case COMPRESSION_JPEG:
150 		if (jpegcolormode == JPEGCOLORMODE_RGB)
151 			photometric = PHOTOMETRIC_YCBCR;
152 		else
153 			photometric = PHOTOMETRIC_RGB;
154 		TIFFSetField(out, TIFFTAG_JPEGQUALITY, quality);
155 		TIFFSetField(out, TIFFTAG_JPEGCOLORMODE, jpegcolormode);
156 		break;
157 	case COMPRESSION_LZW:
158 	case COMPRESSION_DEFLATE:
159 		if (predictor != 0)
160 			TIFFSetField(out, TIFFTAG_PREDICTOR, predictor);
161 		break;
162 	}
163 	TIFFSetField(out, TIFFTAG_PHOTOMETRIC, photometric);
164 	TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, 3);
165 	TIFFSetField(out, TIFFTAG_PLANARCONFIG, config);
166 	TIFFSetField(out, TIFFTAG_ROWSPERSTRIP,
167 	    rowsperstrip = TIFFDefaultStripSize(out, rowsperstrip));
168 	(void) TIFFGetField(in, TIFFTAG_PLANARCONFIG, &shortv);
169 	if (cmap == -1)
170 		cmap = checkcmap(1<<bitspersample, rmap, gmap, bmap);
171 	if (cmap == 16) {
172 		/*
173 		 * Convert 16-bit colormap to 8-bit.
174 		 */
175 		int i;
176 
177 		for (i = (1<<bitspersample)-1; i >= 0; i--) {
178 #define	CVT(x)		(((x) * 255) / ((1L<<16)-1))
179 			rmap[i] = CVT(rmap[i]);
180 			gmap[i] = CVT(gmap[i]);
181 			bmap[i] = CVT(bmap[i]);
182 		}
183 	}
184 	{ unsigned char *ibuf, *obuf;
185 	  register unsigned char* pp;
186 	  register uint32 x;
187 	  ibuf = (unsigned char*)_TIFFmalloc(TIFFScanlineSize(in));
188 	  obuf = (unsigned char*)_TIFFmalloc(TIFFScanlineSize(out));
189 	  switch (config) {
190 	  case PLANARCONFIG_CONTIG:
191 		for (row = 0; row < imagelength; row++) {
192 			if (!TIFFReadScanline(in, ibuf, row, 0))
193 				goto done;
194 			pp = obuf;
195 			for (x = 0; x < imagewidth; x++) {
196 				*pp++ = (unsigned char) rmap[ibuf[x]];
197 				*pp++ = (unsigned char) gmap[ibuf[x]];
198 				*pp++ = (unsigned char) bmap[ibuf[x]];
199 			}
200 			if (!TIFFWriteScanline(out, obuf, row, 0))
201 				goto done;
202 		}
203 		break;
204 	  case PLANARCONFIG_SEPARATE:
205 		for (row = 0; row < imagelength; row++) {
206 			if (!TIFFReadScanline(in, ibuf, row, 0))
207 				goto done;
208 			for (pp = obuf, x = 0; x < imagewidth; x++)
209 				*pp++ = (unsigned char) rmap[ibuf[x]];
210 			if (!TIFFWriteScanline(out, obuf, row, 0))
211 				goto done;
212 			for (pp = obuf, x = 0; x < imagewidth; x++)
213 				*pp++ = (unsigned char) gmap[ibuf[x]];
214 			if (!TIFFWriteScanline(out, obuf, row, 0))
215 				goto done;
216 			for (pp = obuf, x = 0; x < imagewidth; x++)
217 				*pp++ = (unsigned char) bmap[ibuf[x]];
218 			if (!TIFFWriteScanline(out, obuf, row, 0))
219 				goto done;
220 		}
221 		break;
222 	  }
223 	  _TIFFfree(ibuf);
224 	  _TIFFfree(obuf);
225 	}
226 done:
227 	(void) TIFFClose(in);
228 	(void) TIFFClose(out);
229 	return (0);
230 }
231 
232 static int
processCompressOptions(char * opt)233 processCompressOptions(char* opt)
234 {
235 	if (streq(opt, "none"))
236 		compression = COMPRESSION_NONE;
237 	else if (streq(opt, "packbits"))
238 		compression = COMPRESSION_PACKBITS;
239 	else if (strneq(opt, "jpeg", 4)) {
240 		char* cp = strchr(opt, ':');
241 
242                 compression = COMPRESSION_JPEG;
243                 while( cp )
244                 {
245                     if (isdigit((int)cp[1]))
246 			quality = atoi(cp+1);
247                     else if (cp[1] == 'r' )
248 			jpegcolormode = JPEGCOLORMODE_RAW;
249                     else
250                         usage();
251 
252                     cp = strchr(cp+1,':');
253                 }
254 	} else if (strneq(opt, "lzw", 3)) {
255 		char* cp = strchr(opt, ':');
256 		if (cp)
257 			predictor = atoi(cp+1);
258 		compression = COMPRESSION_LZW;
259 	} else if (strneq(opt, "zip", 3)) {
260 		char* cp = strchr(opt, ':');
261 		if (cp)
262 			predictor = atoi(cp+1);
263 		compression = COMPRESSION_DEFLATE;
264 	} else
265 		return (0);
266 	return (1);
267 }
268 
269 #define	CopyField(tag, v) \
270     if (TIFFGetField(in, tag, &v)) TIFFSetField(out, tag, v)
271 #define	CopyField2(tag, v1, v2) \
272     if (TIFFGetField(in, tag, &v1, &v2)) TIFFSetField(out, tag, v1, v2)
273 #define	CopyField3(tag, v1, v2, v3) \
274     if (TIFFGetField(in, tag, &v1, &v2, &v3)) TIFFSetField(out, tag, v1, v2, v3)
275 #define	CopyField4(tag, v1, v2, v3, v4) \
276     if (TIFFGetField(in, tag, &v1, &v2, &v3, &v4)) TIFFSetField(out, tag, v1, v2, v3, v4)
277 
278 static void
cpTag(TIFF * in,TIFF * out,uint16 tag,uint16 count,TIFFDataType type)279 cpTag(TIFF* in, TIFF* out, uint16 tag, uint16 count, TIFFDataType type)
280 {
281 	switch (type) {
282 	case TIFF_SHORT:
283 		if (count == 1) {
284 			uint16 shortv;
285 			CopyField(tag, shortv);
286 		} else if (count == 2) {
287 			uint16 shortv1, shortv2;
288 			CopyField2(tag, shortv1, shortv2);
289 		} else if (count == 4) {
290 			uint16 *tr, *tg, *tb, *ta;
291 			CopyField4(tag, tr, tg, tb, ta);
292 		} else if (count == (uint16) -1) {
293 			uint16 shortv1;
294 			uint16* shortav;
295 			CopyField2(tag, shortv1, shortav);
296 		}
297 		break;
298 	case TIFF_LONG:
299 		{ uint32 longv;
300 		  CopyField(tag, longv);
301 		}
302 		break;
303 	case TIFF_RATIONAL:
304 		if (count == 1) {
305 			float floatv;
306 			CopyField(tag, floatv);
307 		} else if (count == (uint16) -1) {
308 			float* floatav;
309 			CopyField(tag, floatav);
310 		}
311 		break;
312 	case TIFF_ASCII:
313 		{ char* stringv;
314 		  CopyField(tag, stringv);
315 		}
316 		break;
317 	case TIFF_DOUBLE:
318 		if (count == 1) {
319 			double doublev;
320 			CopyField(tag, doublev);
321 		} else if (count == (uint16) -1) {
322 			double* doubleav;
323 			CopyField(tag, doubleav);
324 		}
325 		break;
326           default:
327                 TIFFError(TIFFFileName(in),
328                           "Data type %d is not supported, tag %d skipped.",
329                           tag, type);
330 	}
331 }
332 
333 #undef CopyField4
334 #undef CopyField3
335 #undef CopyField2
336 #undef CopyField
337 
338 static struct cpTag {
339     uint16	tag;
340     uint16	count;
341     TIFFDataType type;
342 } tags[] = {
343     { TIFFTAG_IMAGEWIDTH,		1, TIFF_LONG },
344     { TIFFTAG_IMAGELENGTH,		1, TIFF_LONG },
345     { TIFFTAG_BITSPERSAMPLE,		1, TIFF_SHORT },
346     { TIFFTAG_COMPRESSION,		1, TIFF_SHORT },
347     { TIFFTAG_FILLORDER,		1, TIFF_SHORT },
348     { TIFFTAG_ROWSPERSTRIP,		1, TIFF_LONG },
349     { TIFFTAG_GROUP3OPTIONS,		1, TIFF_LONG },
350     { TIFFTAG_SUBFILETYPE,		1, TIFF_LONG },
351     { TIFFTAG_THRESHHOLDING,		1, TIFF_SHORT },
352     { TIFFTAG_DOCUMENTNAME,		1, TIFF_ASCII },
353     { TIFFTAG_IMAGEDESCRIPTION,		1, TIFF_ASCII },
354     { TIFFTAG_MAKE,			1, TIFF_ASCII },
355     { TIFFTAG_MODEL,			1, TIFF_ASCII },
356     { TIFFTAG_ORIENTATION,		1, TIFF_SHORT },
357     { TIFFTAG_MINSAMPLEVALUE,		1, TIFF_SHORT },
358     { TIFFTAG_MAXSAMPLEVALUE,		1, TIFF_SHORT },
359     { TIFFTAG_XRESOLUTION,		1, TIFF_RATIONAL },
360     { TIFFTAG_YRESOLUTION,		1, TIFF_RATIONAL },
361     { TIFFTAG_PAGENAME,			1, TIFF_ASCII },
362     { TIFFTAG_XPOSITION,		1, TIFF_RATIONAL },
363     { TIFFTAG_YPOSITION,		1, TIFF_RATIONAL },
364     { TIFFTAG_GROUP4OPTIONS,		1, TIFF_LONG },
365     { TIFFTAG_RESOLUTIONUNIT,		1, TIFF_SHORT },
366     { TIFFTAG_PAGENUMBER,		2, TIFF_SHORT },
367     { TIFFTAG_SOFTWARE,			1, TIFF_ASCII },
368     { TIFFTAG_DATETIME,			1, TIFF_ASCII },
369     { TIFFTAG_ARTIST,			1, TIFF_ASCII },
370     { TIFFTAG_HOSTCOMPUTER,		1, TIFF_ASCII },
371     { TIFFTAG_WHITEPOINT,		2, TIFF_RATIONAL },
372     { TIFFTAG_PRIMARYCHROMATICITIES,	(uint16) -1,TIFF_RATIONAL },
373     { TIFFTAG_HALFTONEHINTS,		2, TIFF_SHORT },
374     { TIFFTAG_BADFAXLINES,		1, TIFF_LONG },
375     { TIFFTAG_CLEANFAXDATA,		1, TIFF_SHORT },
376     { TIFFTAG_CONSECUTIVEBADFAXLINES,	1, TIFF_LONG },
377     { TIFFTAG_INKSET,			1, TIFF_SHORT },
378     /*{ TIFFTAG_INKNAMES,			1, TIFF_ASCII },*/ /* Needs much more complicated logic. See tiffcp */
379     { TIFFTAG_DOTRANGE,			2, TIFF_SHORT },
380     { TIFFTAG_TARGETPRINTER,		1, TIFF_ASCII },
381     { TIFFTAG_SAMPLEFORMAT,		1, TIFF_SHORT },
382     { TIFFTAG_YCBCRCOEFFICIENTS,	(uint16) -1,TIFF_RATIONAL },
383     { TIFFTAG_YCBCRSUBSAMPLING,		2, TIFF_SHORT },
384     { TIFFTAG_YCBCRPOSITIONING,		1, TIFF_SHORT },
385     { TIFFTAG_REFERENCEBLACKWHITE,	(uint16) -1,TIFF_RATIONAL },
386 };
387 #define	NTAGS	(sizeof (tags) / sizeof (tags[0]))
388 
389 static void
cpTags(TIFF * in,TIFF * out)390 cpTags(TIFF* in, TIFF* out)
391 {
392     struct cpTag *p;
393     for (p = tags; p < &tags[NTAGS]; p++)
394     {
395         if( p->tag == TIFFTAG_GROUP3OPTIONS )
396         {
397             uint16 compression;
398             if( !TIFFGetField(in, TIFFTAG_COMPRESSION, &compression) ||
399                     compression != COMPRESSION_CCITTFAX3 )
400                 continue;
401         }
402         if( p->tag == TIFFTAG_GROUP4OPTIONS )
403         {
404             uint16 compression;
405             if( !TIFFGetField(in, TIFFTAG_COMPRESSION, &compression) ||
406                     compression != COMPRESSION_CCITTFAX4 )
407                 continue;
408         }
409         cpTag(in, out, p->tag, p->count, p->type);
410     }
411 }
412 #undef NTAGS
413 
414 char* stuff[] = {
415 "usage: pal2rgb [options] input.tif output.tif",
416 "where options are:",
417 " -p contig	pack samples contiguously (e.g. RGBRGB...)",
418 " -p separate	store samples separately (e.g. RRR...GGG...BBB...)",
419 " -r #		make each strip have no more than # rows",
420 " -C 8		assume 8-bit colormap values (instead of 16-bit)",
421 " -C 16		assume 16-bit colormap values",
422 "",
423 " -c lzw[:opts]	compress output with Lempel-Ziv & Welch encoding",
424 " -c zip[:opts]	compress output with deflate encoding",
425 " -c packbits	compress output with packbits encoding",
426 " -c none	use no compression algorithm on output",
427 "",
428 "LZW and deflate options:",
429 " #		set predictor value",
430 "For example, -c lzw:2 to get LZW-encoded data with horizontal differencing",
431 NULL
432 };
433 
434 static void
usage(void)435 usage(void)
436 {
437 	char buf[BUFSIZ];
438 	int i;
439 
440 	setbuf(stderr, buf);
441         fprintf(stderr, "%s\n\n", TIFFGetVersion());
442 	for (i = 0; stuff[i] != NULL; i++)
443 		fprintf(stderr, "%s\n", stuff[i]);
444 	exit(-1);
445 }
446 
447 /* vim: set ts=8 sts=8 sw=8 noet: */
448 /*
449  * Local Variables:
450  * mode: c
451  * c-basic-offset: 8
452  * fill-column: 78
453  * End:
454  */
455