]>
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 | #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 | */ |