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