1 /*
2  * Copyright (c) 1991-1997 Sam Leffler
3  * Copyright (c) 1991-1997 Silicon Graphics, Inc.
4  *
5  * Permission to use, copy, modify, distribute, and sell this software and
6  * its documentation for any purpose is hereby granted without fee, provided
7  * that (i) the above copyright notices and this permission notice appear in
8  * all copies of the software and related documentation, and (ii) the names of
9  * Sam Leffler and Silicon Graphics may not be used in any advertising or
10  * publicity relating to the software without the specific, prior written
11  * permission of Sam Leffler and Silicon Graphics.
12  *
13  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
14  * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
15  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
16  *
17  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
18  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
19  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
20  * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
21  * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
22  * OF THIS SOFTWARE.
23  */
24 
25 #include "tif_config.h"
26 #include "libport.h"
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <ctype.h>
32 
33 #ifdef HAVE_UNISTD_H
34 # include <unistd.h>
35 #endif
36 
37 #ifdef HAVE_FCNTL_H
38 # include <fcntl.h>
39 #endif
40 
41 #ifdef HAVE_IO_H
42 # include <io.h>
43 #endif
44 
45 #include "tiffio.h"
46 
47 #ifndef EXIT_SUCCESS
48 #define EXIT_SUCCESS 0
49 #endif
50 #ifndef EXIT_FAILURE
51 #define EXIT_FAILURE 1
52 #endif
53 
54 #define	streq(a,b)	(strcmp(a,b) == 0)
55 #define	strneq(a,b,n)	(strncmp(a,b,n) == 0)
56 
57 static	uint16_t compression = COMPRESSION_PACKBITS;
58 static	uint16_t predictor = 0;
59 static	int quality = 75;	/* JPEG quality */
60 static	int jpegcolormode = JPEGCOLORMODE_RGB;
61 static  uint32_t g3opts;
62 
63 static	void usage(int code);
64 static	int processCompressOptions(char*);
65 
66 static void
pack_none(unsigned char * buf,unsigned int smpls,uint16_t bps)67 pack_none (unsigned char *buf, unsigned int smpls, uint16_t bps)
68 {
69 	(void)buf;
70 	(void)smpls;
71 	(void)bps;
72 	return;
73 }
74 
75 static void
pack_swab(unsigned char * buf,unsigned int smpls,uint16_t bps)76 pack_swab (unsigned char *buf, unsigned int smpls, uint16_t bps)
77 {
78 	unsigned int s;
79 	unsigned char h;
80 	unsigned char l;
81 	(void)bps;
82 
83 	for (s = 0; smpls > s; s++) {
84 
85 		h = buf [s * 2 + 0];
86 		l = buf [s * 2 + 1];
87 
88 		buf [s * 2 + 0] = l;
89 		buf [s * 2 + 1] = h;
90 	}
91 	return;
92 }
93 
94 static void
pack_bytes(unsigned char * buf,unsigned int smpls,uint16_t bps)95 pack_bytes (unsigned char *buf, unsigned int smpls, uint16_t bps)
96 {
97 	unsigned int s;
98 	unsigned int in;
99 	unsigned int out;
100 	int bits;
101 	uint16_t t;
102 
103 	in   = 0;
104 	out  = 0;
105 	bits = 0;
106 	t    = 0;
107 
108 	for (s = 0; smpls > s; s++) {
109 
110 		t <<= bps;
111 		t |= (uint16_t) buf [in++];
112 
113 		bits += bps;
114 
115 		if (8 <= bits) {
116 			bits -= 8;
117 			buf [out++] = (t >> bits) & 0xFF;
118 		}
119 	}
120 	if (0 != bits)
121 		buf [out] = (t << (8 - bits)) & 0xFF;
122 }
123 
124 static void
pack_words(unsigned char * buf,unsigned int smpls,uint16_t bps)125 pack_words (unsigned char *buf, unsigned int smpls, uint16_t bps)
126 {
127 	unsigned int s;
128 	unsigned int in;
129 	unsigned int out;
130 	int bits;
131 	uint32_t t;
132 
133 	in   = 0;
134 	out  = 0;
135 	bits = 0;
136 	t    = 0;
137 
138 	for (s = 0; smpls > s; s++) {
139 
140 		t <<= bps;
141 		t |= (uint32_t) buf [in++] << 8;
142 		t |= (uint32_t) buf [in++] << 0;
143 
144 		bits += bps;
145 
146 		if (16 <= bits) {
147 
148 			bits -= 16;
149 			buf [out++] = (t >> (bits + 8));
150 			buf [out++] = (t >> (bits + 0));
151 		}
152 	}
153 	if (0 != bits) {
154 		t <<= 16 - bits;
155 
156 		buf [out++] = (t >> (16 + 8));
157 		buf [out++] = (t >> (16 + 0));
158 	}
159 }
160 
161 static void
BadPPM(char * file)162 BadPPM(char* file)
163 {
164 	fprintf(stderr, "%s: Not a PPM file.\n", file);
165 	exit(EXIT_FAILURE);
166 }
167 
168 
169 static tmsize_t
multiply_ms(tmsize_t m1,tmsize_t m2)170 multiply_ms(tmsize_t m1, tmsize_t m2)
171 {
172         if( m1 == 0 || m2 > TIFF_TMSIZE_T_MAX / m1 )
173             return 0;
174         return m1 * m2;
175 }
176 
177 int
main(int argc,char * argv[])178 main(int argc, char* argv[])
179 {
180 	uint16_t photometric = 0;
181 	uint32_t rowsperstrip = (uint32_t) -1;
182 	double resolution = -1;
183 	unsigned char *buf = NULL;
184 	tmsize_t linebytes = 0;
185 	int pbm;
186 	uint16_t spp = 1;
187 	uint16_t bpp = 8;
188 	void (*pack_func) (unsigned char *buf, unsigned int smpls, uint16_t bps);
189 	TIFF *out;
190 	FILE *in;
191 	unsigned int w, h, prec, row;
192 	char *infile;
193 	int c;
194 #if !HAVE_DECL_OPTARG
195 	extern int optind;
196 	extern char* optarg;
197 #endif
198 	tmsize_t scanline_size;
199 
200 	if (argc < 2) {
201 	    fprintf(stderr, "%s: Too few arguments\n", argv[0]);
202 	    usage(EXIT_FAILURE);
203 	}
204 	while ((c = getopt(argc, argv, "c:r:R:h")) != -1)
205 		switch (c) {
206 		case 'c':		/* compression scheme */
207 			if (!processCompressOptions(optarg))
208 				usage(EXIT_FAILURE);
209 			break;
210 		case 'r':		/* rows/strip */
211 			rowsperstrip = atoi(optarg);
212 			break;
213 		case 'R':		/* resolution */
214 			resolution = atof(optarg);
215 			break;
216 		case 'h':
217 			usage(EXIT_SUCCESS);
218 			break;
219 		case '?':
220 			usage(EXIT_FAILURE);
221 			/*NOTREACHED*/
222 		}
223 
224 	if (optind + 2 < argc) {
225 	    fprintf(stderr, "%s: Too many arguments\n", argv[0]);
226 	    usage(EXIT_FAILURE);
227 	}
228 
229 	/*
230 	 * If only one file is specified, read input from
231 	 * stdin; otherwise usage is: ppm2tiff input output.
232 	 */
233 	if (argc - optind > 1) {
234 		infile = argv[optind++];
235 		in = fopen(infile, "rb");
236 		if (in == NULL) {
237 			fprintf(stderr, "%s: Can not open.\n", infile);
238 			return (EXIT_FAILURE);
239 		}
240 	} else {
241 		infile = "<stdin>";
242 		in = stdin;
243 #if defined(HAVE_SETMODE) && defined(O_BINARY)
244 		setmode(fileno(stdin), O_BINARY);
245 #endif
246 	}
247 
248 	if (fgetc(in) != 'P')
249 		BadPPM(infile);
250 	switch (fgetc(in)) {
251 		case '4':			/* it's a PBM file */
252 			pbm = !0;
253 			spp = 1;
254 			photometric = PHOTOMETRIC_MINISWHITE;
255 			break;
256 		case '5':			/* it's a PGM file */
257 			pbm = 0;
258 			spp = 1;
259 			photometric = PHOTOMETRIC_MINISBLACK;
260 			break;
261 		case '6':			/* it's a PPM file */
262 			pbm = 0;
263 			spp = 3;
264 			photometric = PHOTOMETRIC_RGB;
265 			if (compression == COMPRESSION_JPEG &&
266 			    jpegcolormode == JPEGCOLORMODE_RGB)
267 				photometric = PHOTOMETRIC_YCBCR;
268 			break;
269 		default:
270 			BadPPM(infile);
271 	}
272 
273 	/* Parse header */
274 	while(1) {
275 		if (feof(in))
276 			BadPPM(infile);
277 		c = fgetc(in);
278 		/* Skip whitespaces (blanks, TABs, CRs, LFs) */
279 		if (strchr(" \t\r\n", c))
280 			continue;
281 
282 		/* Check for comment line */
283 		if (c == '#') {
284 			do {
285 			    c = fgetc(in);
286 			} while(!(strchr("\r\n", c) || feof(in)));
287 			continue;
288 		}
289 
290 		ungetc(c, in);
291 		break;
292 	}
293 	if (pbm) {
294 		if (fscanf(in, " %u %u", &w, &h) != 2)
295 			BadPPM(infile);
296 		if (fgetc(in) != '\n')
297 			BadPPM(infile);
298 		bpp = 1;
299 		pack_func = pack_none;
300 	} else {
301 		if (fscanf(in, " %u %u %u", &w, &h, &prec) != 3)
302 			BadPPM(infile);
303 		if (fgetc(in) != '\n' || 0 == prec || 65535 < prec)
304 			BadPPM(infile);
305 
306 		if (0 != (prec & (prec + 1))) {
307 			fprintf(stderr, "%s: unsupported maxval %u.\n",
308 				infile, prec);
309 			exit(EXIT_FAILURE);
310 		}
311 		bpp = 0;
312 		if ((prec + 1) & 0xAAAAAAAA) bpp |=  1;
313 		if ((prec + 1) & 0xCCCCCCCC) bpp |=  2;
314 		if ((prec + 1) & 0xF0F0F0F0) bpp |=  4;
315 		if ((prec + 1) & 0xFF00FF00) bpp |=  8;
316 		if ((prec + 1) & 0xFFFF0000) bpp |= 16;
317 
318 		switch (bpp) {
319 		case 8:
320 			pack_func = pack_none;
321 			break;
322 		case 16:
323 			{
324 				const unsigned short i = 0x0100;
325 
326 				if (0 == *(unsigned char*) &i)
327 					pack_func = pack_swab;
328 				else
329 					pack_func = pack_none;
330 			}
331 			break;
332 		default:
333 			if (8 >= bpp)
334 				pack_func = pack_bytes;
335 			else
336 				pack_func = pack_words;
337 			break;
338 		}
339 	}
340 	out = TIFFOpen(argv[optind], "w");
341 	if (out == NULL)
342 		return (EXIT_FAILURE);
343 	TIFFSetField(out, TIFFTAG_IMAGEWIDTH, (uint32_t) w);
344 	TIFFSetField(out, TIFFTAG_IMAGELENGTH, (uint32_t) h);
345 	TIFFSetField(out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
346 	TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, spp);
347 	TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, bpp);
348 	TIFFSetField(out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
349 	TIFFSetField(out, TIFFTAG_PHOTOMETRIC, photometric);
350 	TIFFSetField(out, TIFFTAG_COMPRESSION, compression);
351 	switch (compression) {
352 	case COMPRESSION_JPEG:
353 		TIFFSetField(out, TIFFTAG_JPEGQUALITY, quality);
354 		TIFFSetField(out, TIFFTAG_JPEGCOLORMODE, jpegcolormode);
355 		break;
356 	case COMPRESSION_LZW:
357 	case COMPRESSION_DEFLATE:
358 		if (predictor != 0)
359 			TIFFSetField(out, TIFFTAG_PREDICTOR, predictor);
360 		break;
361         case COMPRESSION_CCITTFAX3:
362 		TIFFSetField(out, TIFFTAG_GROUP3OPTIONS, g3opts);
363 		break;
364 	}
365 	if (pbm) {
366 		/* if round-up overflows, result will be zero, OK */
367 		linebytes = (multiply_ms(spp, w) + (8 - 1)) / 8;
368 	} else if (bpp <= 8) {
369 		linebytes = multiply_ms(spp, w);
370 	} else {
371 		linebytes = multiply_ms(2 * spp, w);
372 	}
373 	if (rowsperstrip == (uint32_t) -1) {
374 		TIFFSetField(out, TIFFTAG_ROWSPERSTRIP, h);
375 	} else {
376 		TIFFSetField(out, TIFFTAG_ROWSPERSTRIP,
377 		    TIFFDefaultStripSize(out, rowsperstrip));
378 	}
379 	if (linebytes == 0) {
380 		fprintf(stderr, "%s: scanline size overflow\n", infile);
381 		(void) TIFFClose(out);
382 		exit(EXIT_FAILURE);
383 	}
384 	scanline_size = TIFFScanlineSize(out);
385 	if (scanline_size == 0) {
386 		/* overflow - TIFFScanlineSize already printed a message */
387 		(void) TIFFClose(out);
388 		exit(EXIT_FAILURE);
389 	}
390 	if (scanline_size < linebytes)
391 		buf = (unsigned char *)_TIFFmalloc(linebytes);
392 	else
393 		buf = (unsigned char *)_TIFFmalloc(scanline_size);
394 	if (buf == NULL) {
395 		fprintf(stderr, "%s: Not enough memory\n", infile);
396 		(void) TIFFClose(out);
397 		exit(EXIT_FAILURE);
398 	}
399 	if (resolution > 0) {
400 		TIFFSetField(out, TIFFTAG_XRESOLUTION, resolution);
401 		TIFFSetField(out, TIFFTAG_YRESOLUTION, resolution);
402 		TIFFSetField(out, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
403 	}
404 	for (row = 0; row < h; row++) {
405 		if (fread(buf, linebytes, 1, in) != 1) {
406 			fprintf(stderr, "%s: scanline %u: Read error.\n",
407 			    infile, row);
408 			break;
409 		}
410 		pack_func (buf, w * spp, bpp);
411 		if (TIFFWriteScanline(out, buf, row, 0) < 0)
412 			break;
413 	}
414 	if (in != stdin)
415 		fclose(in);
416 	(void) TIFFClose(out);
417 	if (buf)
418 		_TIFFfree(buf);
419 	return (EXIT_SUCCESS);
420 }
421 
422 static void
processG3Options(char * cp)423 processG3Options(char* cp)
424 {
425 	g3opts = 0;
426         if( (cp = strchr(cp, ':')) ) {
427                 do {
428                         cp++;
429                         if (strneq(cp, "1d", 2))
430                                 g3opts &= ~GROUP3OPT_2DENCODING;
431                         else if (strneq(cp, "2d", 2))
432                                 g3opts |= GROUP3OPT_2DENCODING;
433                         else if (strneq(cp, "fill", 4))
434                                 g3opts |= GROUP3OPT_FILLBITS;
435                         else
436                                 usage(EXIT_FAILURE);
437                 } while( (cp = strchr(cp, ':')) );
438         }
439 }
440 
441 static int
processCompressOptions(char * opt)442 processCompressOptions(char* opt)
443 {
444 	if (streq(opt, "none"))
445 		compression = COMPRESSION_NONE;
446 	else if (streq(opt, "packbits"))
447 		compression = COMPRESSION_PACKBITS;
448 	else if (strneq(opt, "jpeg", 4)) {
449 		char* cp = strchr(opt, ':');
450 
451                 compression = COMPRESSION_JPEG;
452                 while (cp)
453                 {
454                     if (isdigit((int)cp[1]))
455 			quality = atoi(cp+1);
456                     else if (cp[1] == 'r' )
457 			jpegcolormode = JPEGCOLORMODE_RAW;
458                     else
459                         usage(EXIT_FAILURE);
460 
461                     cp = strchr(cp+1,':');
462                 }
463 	} else if (strneq(opt, "g3", 2)) {
464 		processG3Options(opt);
465 		compression = COMPRESSION_CCITTFAX3;
466 	} else if (streq(opt, "g4")) {
467 		compression = COMPRESSION_CCITTFAX4;
468 	} else if (strneq(opt, "lzw", 3)) {
469 		char* cp = strchr(opt, ':');
470 		if (cp)
471 			predictor = atoi(cp+1);
472 		compression = COMPRESSION_LZW;
473 	} else if (strneq(opt, "zip", 3)) {
474 		char* cp = strchr(opt, ':');
475 		if (cp)
476 			predictor = atoi(cp+1);
477 		compression = COMPRESSION_DEFLATE;
478 	} else
479 		return (0);
480 	return (1);
481 }
482 
483 static const char usage_info[] =
484 "Create a TIFF file from PPM, PGM and PBM image files\n\n"
485 "usage: ppm2tiff [options] input.ppm output.tif\n"
486 "where options are:\n"
487 " -r #		make each strip have no more than # rows\n"
488 " -R #		set x&y resolution (dpi)\n"
489 "\n"
490 #ifdef JPEG_SUPPORT
491 " -c jpeg[:opts] compress output with JPEG encoding\n"
492 /*     "JPEG options:\n" */
493 "    #  set compression quality level (0-100, default 75)\n"
494 "    r  output color image as RGB rather than YCbCr\n"
495 #endif
496 #ifdef LZW_SUPPORT
497 " -c lzw[:opts]  compress output with Lempel-Ziv & Welch encoding\n"
498 /* "    LZW options:\n" */
499 "    #  set predictor value\n"
500 "    For example, -c lzw:2 for LZW-encoded data with horizontal differencing\n"
501 #endif
502 #ifdef ZIP_SUPPORT
503 " -c zip[:opts]  compress output with deflate encoding\n"
504 /* "    Deflate (ZIP) options:\n" */
505 "    #  set predictor value\n"
506 #endif
507 #ifdef PACKBITS_SUPPORT
508 " -c packbits    compress output with packbits encoding (the default)\n"
509 #endif
510 #ifdef CCITT_SUPPORT
511 " -c g3[:opts]   compress output with CCITT Group 3 encoding\n"
512 " -c g4          compress output with CCITT Group 4 encoding\n"
513 #endif
514 #if defined(JPEG_SUPPORT) || defined(LZW_SUPPORT) || defined(ZIP_SUPPORT) || defined(PACKBITS_SUPPORT) || defined(CCITT_SUPPORT)
515 " -c none        use no compression algorithm on output\n"
516 #endif
517 ;
518 
519 static void
usage(int code)520 usage(int code)
521 {
522 	FILE * out = (code == EXIT_SUCCESS) ? stdout : stderr;
523 
524         fprintf(out, "%s\n\n", TIFFGetVersion());
525         fprintf(out, "%s", usage_info);
526 	exit(code);
527 }
528 
529 /* vim: set ts=8 sts=8 sw=8 noet: */
530 /*
531  * Local Variables:
532  * mode: c
533  * c-basic-offset: 8
534  * fill-column: 78
535  * End:
536  */
537