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