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