xref: /libtiff-4.0.7/tools/ppm2tiff.c (revision d21d2b30)
1 /* $Id: ppm2tiff.c,v 1.19 2015-06-21 01:09:10 bfriesen Exp $ */
2 
3 /*
4  * Copyright (c) 1991-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 <stdlib.h>
31 #include <string.h>
32 #include <ctype.h>
33 
34 #ifdef HAVE_UNISTD_H
35 # include <unistd.h>
36 #endif
37 
38 #ifdef HAVE_FCNTL_H
39 # include <fcntl.h>
40 #endif
41 
42 #ifdef HAVE_IO_H
43 # include <io.h>
44 #endif
45 
46 #ifdef NEED_LIBPORT
47 # include "libport.h"
48 #endif
49 
50 #include "tiffio.h"
51 
52 #ifndef HAVE_GETOPT
53 extern int getopt(int, char**, char*);
54 #endif
55 
56 #define	streq(a,b)	(strcmp(a,b) == 0)
57 #define	strneq(a,b,n)	(strncmp(a,b,n) == 0)
58 
59 static	uint16 compression = COMPRESSION_PACKBITS;
60 static	uint16 predictor = 0;
61 static	int quality = 75;	/* JPEG quality */
62 static	int jpegcolormode = JPEGCOLORMODE_RGB;
63 static  uint32 g3opts;
64 
65 static	void usage(void);
66 static	int processCompressOptions(char*);
67 
68 static void
BadPPM(char * file)69 BadPPM(char* file)
70 {
71 	fprintf(stderr, "%s: Not a PPM file.\n", file);
72 	exit(-2);
73 }
74 
75 static tmsize_t
multiply_ms(tmsize_t m1,tmsize_t m2)76 multiply_ms(tmsize_t m1, tmsize_t m2)
77 {
78 	tmsize_t bytes = m1 * m2;
79 
80 	if (m1 && bytes / m1 != m2)
81 		bytes = 0;
82 
83 	return bytes;
84 }
85 
86 int
main(int argc,char * argv[])87 main(int argc, char* argv[])
88 {
89 	uint16 photometric = 0;
90 	uint32 rowsperstrip = (uint32) -1;
91 	double resolution = -1;
92 	unsigned char *buf = NULL;
93 	tmsize_t linebytes = 0;
94 	uint16 spp = 1;
95 	uint16 bpp = 8;
96 	TIFF *out;
97 	FILE *in;
98 	unsigned int w, h, prec, row;
99 	char *infile;
100 	int c;
101 #if !HAVE_DECL_OPTARG
102 	extern int optind;
103 	extern char* optarg;
104 #endif
105 	tmsize_t scanline_size;
106 
107 	if (argc < 2) {
108 	    fprintf(stderr, "%s: Too few arguments\n", argv[0]);
109 	    usage();
110 	}
111 	while ((c = getopt(argc, argv, "c:r:R:")) != -1)
112 		switch (c) {
113 		case 'c':		/* compression scheme */
114 			if (!processCompressOptions(optarg))
115 				usage();
116 			break;
117 		case 'r':		/* rows/strip */
118 			rowsperstrip = atoi(optarg);
119 			break;
120 		case 'R':		/* resolution */
121 			resolution = atof(optarg);
122 			break;
123 		case '?':
124 			usage();
125 			/*NOTREACHED*/
126 		}
127 
128 	if (optind + 2 < argc) {
129 	    fprintf(stderr, "%s: Too many arguments\n", argv[0]);
130 	    usage();
131 	}
132 
133 	/*
134 	 * If only one file is specified, read input from
135 	 * stdin; otherwise usage is: ppm2tiff input output.
136 	 */
137 	if (argc - optind > 1) {
138 		infile = argv[optind++];
139 		in = fopen(infile, "rb");
140 		if (in == NULL) {
141 			fprintf(stderr, "%s: Can not open.\n", infile);
142 			return (-1);
143 		}
144 	} else {
145 		infile = "<stdin>";
146 		in = stdin;
147 #if defined(HAVE_SETMODE) && defined(O_BINARY)
148 		setmode(fileno(stdin), O_BINARY);
149 #endif
150 	}
151 
152 	if (fgetc(in) != 'P')
153 		BadPPM(infile);
154 	switch (fgetc(in)) {
155 		case '4':			/* it's a PBM file */
156 			bpp = 1;
157 			spp = 1;
158 			photometric = PHOTOMETRIC_MINISWHITE;
159 			break;
160 		case '5':			/* it's a PGM file */
161 			bpp = 8;
162 			spp = 1;
163 			photometric = PHOTOMETRIC_MINISBLACK;
164 			break;
165 		case '6':			/* it's a PPM file */
166 			bpp = 8;
167 			spp = 3;
168 			photometric = PHOTOMETRIC_RGB;
169 			if (compression == COMPRESSION_JPEG &&
170 			    jpegcolormode == JPEGCOLORMODE_RGB)
171 				photometric = PHOTOMETRIC_YCBCR;
172 			break;
173 		default:
174 			BadPPM(infile);
175 	}
176 
177 	/* Parse header */
178 	while(1) {
179 		if (feof(in))
180 			BadPPM(infile);
181 		c = fgetc(in);
182 		/* Skip whitespaces (blanks, TABs, CRs, LFs) */
183 		if (strchr(" \t\r\n", c))
184 			continue;
185 
186 		/* Check for comment line */
187 		if (c == '#') {
188 			do {
189 			    c = fgetc(in);
190 			} while(!(strchr("\r\n", c) || feof(in)));
191 			continue;
192 		}
193 
194 		ungetc(c, in);
195 		break;
196 	}
197 	switch (bpp) {
198 	case 1:
199 		if (fscanf(in, " %u %u", &w, &h) != 2)
200 			BadPPM(infile);
201 		if (fgetc(in) != '\n')
202 			BadPPM(infile);
203 		break;
204 	case 8:
205 		if (fscanf(in, " %u %u %u", &w, &h, &prec) != 3)
206 			BadPPM(infile);
207 		if (fgetc(in) != '\n' || prec != 255)
208 			BadPPM(infile);
209 		break;
210 	}
211 	out = TIFFOpen(argv[optind], "w");
212 	if (out == NULL)
213 		return (-4);
214 	TIFFSetField(out, TIFFTAG_IMAGEWIDTH, (uint32) w);
215 	TIFFSetField(out, TIFFTAG_IMAGELENGTH, (uint32) h);
216 	TIFFSetField(out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
217 	TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, spp);
218 	TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, bpp);
219 	TIFFSetField(out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
220 	TIFFSetField(out, TIFFTAG_PHOTOMETRIC, photometric);
221 	TIFFSetField(out, TIFFTAG_COMPRESSION, compression);
222 	switch (compression) {
223 	case COMPRESSION_JPEG:
224 		TIFFSetField(out, TIFFTAG_JPEGQUALITY, quality);
225 		TIFFSetField(out, TIFFTAG_JPEGCOLORMODE, jpegcolormode);
226 		break;
227 	case COMPRESSION_LZW:
228 	case COMPRESSION_DEFLATE:
229 		if (predictor != 0)
230 			TIFFSetField(out, TIFFTAG_PREDICTOR, predictor);
231 		break;
232         case COMPRESSION_CCITTFAX3:
233 		TIFFSetField(out, TIFFTAG_GROUP3OPTIONS, g3opts);
234 		break;
235 	}
236 	switch (bpp) {
237 		case 1:
238 			/* if round-up overflows, result will be zero, OK */
239 			linebytes = (multiply_ms(spp, w) + (8 - 1)) / 8;
240 			if (rowsperstrip == (uint32) -1) {
241 				TIFFSetField(out, TIFFTAG_ROWSPERSTRIP, h);
242 			} else {
243 				TIFFSetField(out, TIFFTAG_ROWSPERSTRIP,
244 				    TIFFDefaultStripSize(out, rowsperstrip));
245 			}
246 			break;
247 		case 8:
248 			linebytes = multiply_ms(spp, w);
249 			TIFFSetField(out, TIFFTAG_ROWSPERSTRIP,
250 			    TIFFDefaultStripSize(out, rowsperstrip));
251 			break;
252 	}
253 	if (linebytes == 0) {
254 		fprintf(stderr, "%s: scanline size overflow\n", infile);
255 		(void) TIFFClose(out);
256 		exit(-2);
257 	}
258 	scanline_size = TIFFScanlineSize(out);
259 	if (scanline_size == 0) {
260 		/* overflow - TIFFScanlineSize already printed a message */
261 		(void) TIFFClose(out);
262 		exit(-2);
263 	}
264 	if (scanline_size < linebytes)
265 		buf = (unsigned char *)_TIFFmalloc(linebytes);
266 	else
267 		buf = (unsigned char *)_TIFFmalloc(scanline_size);
268 	if (buf == NULL) {
269 		fprintf(stderr, "%s: Not enough memory\n", infile);
270 		(void) TIFFClose(out);
271 		exit(-2);
272 	}
273 	if (resolution > 0) {
274 		TIFFSetField(out, TIFFTAG_XRESOLUTION, resolution);
275 		TIFFSetField(out, TIFFTAG_YRESOLUTION, resolution);
276 		TIFFSetField(out, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
277 	}
278 	for (row = 0; row < h; row++) {
279 		if (fread(buf, linebytes, 1, in) != 1) {
280 			fprintf(stderr, "%s: scanline %lu: Read error.\n",
281 			    infile, (unsigned long) row);
282 			break;
283 		}
284 		if (TIFFWriteScanline(out, buf, row, 0) < 0)
285 			break;
286 	}
287 	(void) TIFFClose(out);
288 	if (buf)
289 		_TIFFfree(buf);
290 	return (0);
291 }
292 
293 static void
processG3Options(char * cp)294 processG3Options(char* cp)
295 {
296 	g3opts = 0;
297         if( (cp = strchr(cp, ':')) ) {
298                 do {
299                         cp++;
300                         if (strneq(cp, "1d", 2))
301                                 g3opts &= ~GROUP3OPT_2DENCODING;
302                         else if (strneq(cp, "2d", 2))
303                                 g3opts |= GROUP3OPT_2DENCODING;
304                         else if (strneq(cp, "fill", 4))
305                                 g3opts |= GROUP3OPT_FILLBITS;
306                         else
307                                 usage();
308                 } while( (cp = strchr(cp, ':')) );
309         }
310 }
311 
312 static int
processCompressOptions(char * opt)313 processCompressOptions(char* opt)
314 {
315 	if (streq(opt, "none"))
316 		compression = COMPRESSION_NONE;
317 	else if (streq(opt, "packbits"))
318 		compression = COMPRESSION_PACKBITS;
319 	else if (strneq(opt, "jpeg", 4)) {
320 		char* cp = strchr(opt, ':');
321 
322                 compression = COMPRESSION_JPEG;
323                 while (cp)
324                 {
325                     if (isdigit((int)cp[1]))
326 			quality = atoi(cp+1);
327                     else if (cp[1] == 'r' )
328 			jpegcolormode = JPEGCOLORMODE_RAW;
329                     else
330                         usage();
331 
332                     cp = strchr(cp+1,':');
333                 }
334 	} else if (strneq(opt, "g3", 2)) {
335 		processG3Options(opt);
336 		compression = COMPRESSION_CCITTFAX3;
337 	} else if (streq(opt, "g4")) {
338 		compression = COMPRESSION_CCITTFAX4;
339 	} else if (strneq(opt, "lzw", 3)) {
340 		char* cp = strchr(opt, ':');
341 		if (cp)
342 			predictor = atoi(cp+1);
343 		compression = COMPRESSION_LZW;
344 	} else if (strneq(opt, "zip", 3)) {
345 		char* cp = strchr(opt, ':');
346 		if (cp)
347 			predictor = atoi(cp+1);
348 		compression = COMPRESSION_DEFLATE;
349 	} else
350 		return (0);
351 	return (1);
352 }
353 
354 char* stuff[] = {
355 "usage: ppm2tiff [options] input.ppm output.tif",
356 "where options are:",
357 " -r #		make each strip have no more than # rows",
358 " -R #		set x&y resolution (dpi)",
359 "",
360 " -c jpeg[:opts]  compress output with JPEG encoding",
361 " -c lzw[:opts]	compress output with Lempel-Ziv & Welch encoding",
362 " -c zip[:opts]	compress output with deflate encoding",
363 " -c packbits	compress output with packbits encoding (the default)",
364 " -c g3[:opts]  compress output with CCITT Group 3 encoding",
365 " -c g4         compress output with CCITT Group 4 encoding",
366 " -c none	use no compression algorithm on output",
367 "",
368 "JPEG options:",
369 " #		set compression quality level (0-100, default 75)",
370 " r		output color image as RGB rather than YCbCr",
371 "LZW and deflate options:",
372 " #		set predictor value",
373 "For example, -c lzw:2 to get LZW-encoded data with horizontal differencing",
374 NULL
375 };
376 
377 static void
usage(void)378 usage(void)
379 {
380 	char buf[BUFSIZ];
381 	int i;
382 
383 	setbuf(stderr, buf);
384         fprintf(stderr, "%s\n\n", TIFFGetVersion());
385 	for (i = 0; stuff[i] != NULL; i++)
386 		fprintf(stderr, "%s\n", stuff[i]);
387 	exit(-1);
388 }
389 
390 /* vim: set ts=8 sts=8 sw=8 noet: */
391 /*
392  * Local Variables:
393  * mode: c
394  * c-basic-offset: 8
395  * fill-column: 78
396  * End:
397  */
398