xref: /libtiff-4.0.7/tools/tiffdither.c (revision bfbc7176)
1 /* $Id: tiffdither.c,v 1.12 2010-03-10 18:56:50 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 <stdlib.h>
31 #include <string.h>
32 
33 #ifdef HAVE_UNISTD_H
34 # include <unistd.h>
35 #endif
36 
37 #ifdef NEED_LIBPORT
38 # include "libport.h"
39 #endif
40 
41 #include "tiffio.h"
42 
43 #define	streq(a,b)	(strcmp(a,b) == 0)
44 #define	strneq(a,b,n)	(strncmp(a,b,n) == 0)
45 
46 #define	CopyField(tag, v) \
47 	if (TIFFGetField(in, tag, &v)) TIFFSetField(out, tag, v)
48 
49 uint32	imagewidth;
50 uint32	imagelength;
51 int	threshold = 128;
52 
53 static	void usage(void);
54 
55 /*
56  * Floyd-Steinberg error propragation with threshold.
57  * This code is stolen from tiffmedian.
58  */
59 static void
60 fsdither(TIFF* in, TIFF* out)
61 {
62 	unsigned char *outline, *inputline, *inptr;
63 	short *thisline, *nextline, *tmpptr;
64 	register unsigned char	*outptr;
65 	register short *thisptr, *nextptr;
66 	register uint32 i, j;
67 	uint32 imax, jmax;
68 	int lastline, lastpixel;
69 	int bit;
70 	tsize_t outlinesize;
71 
72 	imax = imagelength - 1;
73 	jmax = imagewidth - 1;
74 	inputline = (unsigned char *)_TIFFmalloc(TIFFScanlineSize(in));
75 	thisline = (short *)_TIFFmalloc(imagewidth * sizeof (short));
76 	nextline = (short *)_TIFFmalloc(imagewidth * sizeof (short));
77 	outlinesize = TIFFScanlineSize(out);
78 	outline = (unsigned char *) _TIFFmalloc(outlinesize);
79 
80 	/*
81 	 * Get first line
82 	 */
83 	if (TIFFReadScanline(in, inputline, 0, 0) <= 0)
84             goto skip_on_error;
85 
86 	inptr = inputline;
87 	nextptr = nextline;
88 	for (j = 0; j < imagewidth; ++j)
89 		*nextptr++ = *inptr++;
90 	for (i = 1; i < imagelength; ++i) {
91 		tmpptr = thisline;
92 		thisline = nextline;
93 		nextline = tmpptr;
94 		lastline = (i == imax);
95 		if (TIFFReadScanline(in, inputline, i, 0) <= 0)
96 			break;
97 		inptr = inputline;
98 		nextptr = nextline;
99 		for (j = 0; j < imagewidth; ++j)
100 			*nextptr++ = *inptr++;
101 		thisptr = thisline;
102 		nextptr = nextline;
103 		_TIFFmemset(outptr = outline, 0, outlinesize);
104 		bit = 0x80;
105 		for (j = 0; j < imagewidth; ++j) {
106 			register int v;
107 
108 			lastpixel = (j == jmax);
109 			v = *thisptr++;
110 			if (v < 0)
111 				v = 0;
112 			else if (v > 255)
113 				v = 255;
114 			if (v > threshold) {
115 				*outptr |= bit;
116 				v -= 255;
117 			}
118 			bit >>= 1;
119 			if (bit == 0) {
120 				outptr++;
121 				bit = 0x80;
122 			}
123 			if (!lastpixel)
124 				thisptr[0] += v * 7 / 16;
125 			if (!lastline) {
126 				if (j != 0)
127 					nextptr[-1] += v * 3 / 16;
128 				*nextptr++ += v * 5 / 16;
129 				if (!lastpixel)
130 					nextptr[0] += v / 16;
131 			}
132 		}
133 		if (TIFFWriteScanline(out, outline, i-1, 0) < 0)
134 			break;
135 	}
136   skip_on_error:
137 	_TIFFfree(inputline);
138 	_TIFFfree(thisline);
139 	_TIFFfree(nextline);
140 	_TIFFfree(outline);
141 }
142 
143 static	uint16 compression = COMPRESSION_PACKBITS;
144 static	uint16 predictor = 0;
145 static	uint32 group3options = 0;
146 
147 static void
148 processG3Options(char* cp)
149 {
150 	if ((cp = strchr(cp, ':'))) {
151 		do {
152 			cp++;
153 			if (strneq(cp, "1d", 2))
154 				group3options &= ~GROUP3OPT_2DENCODING;
155 			else if (strneq(cp, "2d", 2))
156 				group3options |= GROUP3OPT_2DENCODING;
157 			else if (strneq(cp, "fill", 4))
158 				group3options |= GROUP3OPT_FILLBITS;
159 			else
160 				usage();
161 		} while ((cp = strchr(cp, ':')));
162 	}
163 }
164 
165 static int
166 processCompressOptions(char* opt)
167 {
168 	if (streq(opt, "none"))
169 		compression = COMPRESSION_NONE;
170 	else if (streq(opt, "packbits"))
171 		compression = COMPRESSION_PACKBITS;
172 	else if (strneq(opt, "g3", 2)) {
173 		processG3Options(opt);
174 		compression = COMPRESSION_CCITTFAX3;
175 	} else if (streq(opt, "g4"))
176 		compression = COMPRESSION_CCITTFAX4;
177 	else if (strneq(opt, "lzw", 3)) {
178 		char* cp = strchr(opt, ':');
179 		if (cp)
180 			predictor = atoi(cp+1);
181 		compression = COMPRESSION_LZW;
182 	} else if (strneq(opt, "zip", 3)) {
183 		char* cp = strchr(opt, ':');
184 		if (cp)
185 			predictor = atoi(cp+1);
186 		compression = COMPRESSION_DEFLATE;
187 	} else
188 		return (0);
189 	return (1);
190 }
191 
192 int
193 main(int argc, char* argv[])
194 {
195 	TIFF *in, *out;
196 	uint16 samplesperpixel, bitspersample = 1, shortv;
197 	float floatv;
198 	char thing[1024];
199 	uint32 rowsperstrip = (uint32) -1;
200 	uint16 fillorder = 0;
201 	int c;
202 	extern int optind;
203 	extern char *optarg;
204 
205 	while ((c = getopt(argc, argv, "c:f:r:t:")) != -1)
206 		switch (c) {
207 		case 'c':		/* compression scheme */
208 			if (!processCompressOptions(optarg))
209 				usage();
210 			break;
211 		case 'f':		/* fill order */
212 			if (streq(optarg, "lsb2msb"))
213 				fillorder = FILLORDER_LSB2MSB;
214 			else if (streq(optarg, "msb2lsb"))
215 				fillorder = FILLORDER_MSB2LSB;
216 			else
217 				usage();
218 			break;
219 		case 'r':		/* rows/strip */
220 			rowsperstrip = atoi(optarg);
221 			break;
222 		case 't':
223 			threshold = atoi(optarg);
224 			if (threshold < 0)
225 				threshold = 0;
226 			else if (threshold > 255)
227 				threshold = 255;
228 			break;
229 		case '?':
230 			usage();
231 			/*NOTREACHED*/
232 		}
233 	if (argc - optind < 2)
234 		usage();
235 	in = TIFFOpen(argv[optind], "r");
236 	if (in == NULL)
237 		return (-1);
238 	TIFFGetField(in, TIFFTAG_SAMPLESPERPIXEL, &samplesperpixel);
239 	if (samplesperpixel != 1) {
240 		fprintf(stderr, "%s: Not a b&w image.\n", argv[0]);
241 		return (-1);
242 	}
243 	TIFFGetField(in, TIFFTAG_BITSPERSAMPLE, &bitspersample);
244 	if (bitspersample != 8) {
245 		fprintf(stderr,
246 		    " %s: Sorry, only handle 8-bit samples.\n", argv[0]);
247 		return (-1);
248 	}
249 	out = TIFFOpen(argv[optind+1], "w");
250 	if (out == NULL)
251 		return (-1);
252 	CopyField(TIFFTAG_IMAGEWIDTH, imagewidth);
253 	TIFFGetField(in, TIFFTAG_IMAGELENGTH, &imagelength);
254 	TIFFSetField(out, TIFFTAG_IMAGELENGTH, imagelength-1);
255 	TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, 1);
256 	TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, 1);
257 	TIFFSetField(out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
258 	TIFFSetField(out, TIFFTAG_COMPRESSION, compression);
259 	if (fillorder)
260 		TIFFSetField(out, TIFFTAG_FILLORDER, fillorder);
261 	else
262 		CopyField(TIFFTAG_FILLORDER, shortv);
263 	sprintf(thing, "Dithered B&W version of %s", argv[optind]);
264 	TIFFSetField(out, TIFFTAG_IMAGEDESCRIPTION, thing);
265 	CopyField(TIFFTAG_PHOTOMETRIC, shortv);
266 	CopyField(TIFFTAG_ORIENTATION, shortv);
267 	CopyField(TIFFTAG_XRESOLUTION, floatv);
268 	CopyField(TIFFTAG_YRESOLUTION, floatv);
269 	CopyField(TIFFTAG_RESOLUTIONUNIT, shortv);
270         rowsperstrip = TIFFDefaultStripSize(out, rowsperstrip);
271 	TIFFSetField(out, TIFFTAG_ROWSPERSTRIP, rowsperstrip);
272 	switch (compression) {
273 	case COMPRESSION_CCITTFAX3:
274 		TIFFSetField(out, TIFFTAG_GROUP3OPTIONS, group3options);
275 		break;
276 	case COMPRESSION_LZW:
277 	case COMPRESSION_DEFLATE:
278 		if (predictor)
279 			TIFFSetField(out, TIFFTAG_PREDICTOR, predictor);
280 		break;
281 	}
282 	fsdither(in, out);
283 	TIFFClose(in);
284 	TIFFClose(out);
285 	return (0);
286 }
287 
288 char* stuff[] = {
289 "usage: tiffdither [options] input.tif output.tif",
290 "where options are:",
291 " -r #		make each strip have no more than # rows",
292 " -f lsb2msb	force lsb-to-msb FillOrder for output",
293 " -f msb2lsb	force msb-to-lsb FillOrder for output",
294 " -c lzw[:opts]	compress output with Lempel-Ziv & Welch encoding",
295 " -c zip[:opts]	compress output with deflate encoding",
296 " -c packbits	compress output with packbits encoding",
297 " -c g3[:opts]	compress output with CCITT Group 3 encoding",
298 " -c g4		compress output with CCITT Group 4 encoding",
299 " -c none	use no compression algorithm on output",
300 "",
301 "Group 3 options:",
302 " 1d		use default CCITT Group 3 1D-encoding",
303 " 2d		use optional CCITT Group 3 2D-encoding",
304 " fill		byte-align EOL codes",
305 "For example, -c g3:2d:fill to get G3-2D-encoded data with byte-aligned EOLs",
306 "",
307 "LZW and deflate options:",
308 " #		set predictor value",
309 "For example, -c lzw:2 to get LZW-encoded data with horizontal differencing",
310 NULL
311 };
312 
313 static void
314 usage(void)
315 {
316 	char buf[BUFSIZ];
317 	int i;
318 
319 	setbuf(stderr, buf);
320         fprintf(stderr, "%s\n\n", TIFFGetVersion());
321 	for (i = 0; stuff[i] != NULL; i++)
322 		fprintf(stderr, "%s\n", stuff[i]);
323 	exit(-1);
324 }
325 
326 /* vim: set ts=8 sts=8 sw=8 noet: */
327 /*
328  * Local Variables:
329  * mode: c
330  * c-basic-offset: 8
331  * fill-column: 78
332  * End:
333  */
334