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