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