]>
Commit | Line | Data |
---|---|---|
8414a40c VZ |
1 | |
2 | /* | |
3 | * Copyright (c) 1988-1997 Sam Leffler | |
4 | * Copyright (c) 1991-1997 Silicon Graphics, Inc. | |
5 | * | |
6 | * Permission to use, copy, modify, distribute, and sell this software and | |
7 | * its documentation for any purpose is hereby granted without fee, provided | |
8 | * that (i) the above copyright notices and this permission notice appear in | |
9 | * all copies of the software and related documentation, and (ii) the names of | |
10 | * Sam Leffler and Silicon Graphics may not be used in any advertising or | |
11 | * publicity relating to the software without the specific, prior written | |
12 | * permission of Sam Leffler and Silicon Graphics. | |
13 | * | |
14 | * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, | |
15 | * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY | |
16 | * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. | |
17 | * | |
18 | * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR | |
19 | * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, | |
20 | * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, | |
21 | * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF | |
22 | * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE | |
23 | * OF THIS SOFTWARE. | |
24 | */ | |
25 | ||
26 | #include "tif_config.h" | |
27 | ||
28 | #include <stdio.h> | |
29 | #include <stdlib.h> | |
30 | #include <string.h> | |
31 | ||
32 | #ifdef HAVE_UNISTD_H | |
33 | # include <unistd.h> | |
34 | #endif | |
35 | ||
80ed523f VZ |
36 | #ifdef NEED_LIBPORT |
37 | # include "libport.h" | |
38 | #endif | |
39 | ||
8414a40c VZ |
40 | #include "tiffio.h" |
41 | ||
42 | #define streq(a,b) (strcmp(a,b) == 0) | |
43 | #define strneq(a,b,n) (strncmp(a,b,n) == 0) | |
44 | ||
45 | #define CopyField(tag, v) \ | |
46 | if (TIFFGetField(in, tag, &v)) TIFFSetField(out, tag, v) | |
47 | ||
48 | uint32 imagewidth; | |
49 | uint32 imagelength; | |
50 | int threshold = 128; | |
51 | ||
52 | static void usage(void); | |
53 | ||
54 | /* | |
55 | * Floyd-Steinberg error propragation with threshold. | |
56 | * This code is stolen from tiffmedian. | |
57 | */ | |
58 | static void | |
59 | fsdither(TIFF* in, TIFF* out) | |
60 | { | |
61 | unsigned char *outline, *inputline, *inptr; | |
62 | short *thisline, *nextline, *tmpptr; | |
63 | register unsigned char *outptr; | |
64 | register short *thisptr, *nextptr; | |
65 | register uint32 i, j; | |
66 | uint32 imax, jmax; | |
67 | int lastline, lastpixel; | |
68 | int bit; | |
69 | tsize_t outlinesize; | |
70 | ||
71 | imax = imagelength - 1; | |
72 | jmax = imagewidth - 1; | |
73 | inputline = (unsigned char *)_TIFFmalloc(TIFFScanlineSize(in)); | |
74 | thisline = (short *)_TIFFmalloc(imagewidth * sizeof (short)); | |
75 | nextline = (short *)_TIFFmalloc(imagewidth * sizeof (short)); | |
76 | outlinesize = TIFFScanlineSize(out); | |
77 | outline = (unsigned char *) _TIFFmalloc(outlinesize); | |
78 | ||
79 | /* | |
80 | * Get first line | |
81 | */ | |
82 | if (TIFFReadScanline(in, inputline, 0, 0) <= 0) | |
80ed523f VZ |
83 | goto skip_on_error; |
84 | ||
8414a40c VZ |
85 | inptr = inputline; |
86 | nextptr = nextline; | |
87 | for (j = 0; j < imagewidth; ++j) | |
88 | *nextptr++ = *inptr++; | |
89 | for (i = 1; i < imagelength; ++i) { | |
90 | tmpptr = thisline; | |
91 | thisline = nextline; | |
92 | nextline = tmpptr; | |
93 | lastline = (i == imax); | |
94 | if (TIFFReadScanline(in, inputline, i, 0) <= 0) | |
95 | break; | |
96 | inptr = inputline; | |
97 | nextptr = nextline; | |
98 | for (j = 0; j < imagewidth; ++j) | |
99 | *nextptr++ = *inptr++; | |
100 | thisptr = thisline; | |
101 | nextptr = nextline; | |
102 | _TIFFmemset(outptr = outline, 0, outlinesize); | |
103 | bit = 0x80; | |
104 | for (j = 0; j < imagewidth; ++j) { | |
105 | register int v; | |
106 | ||
107 | lastpixel = (j == jmax); | |
108 | v = *thisptr++; | |
109 | if (v < 0) | |
110 | v = 0; | |
111 | else if (v > 255) | |
112 | v = 255; | |
113 | if (v > threshold) { | |
114 | *outptr |= bit; | |
115 | v -= 255; | |
116 | } | |
117 | bit >>= 1; | |
118 | if (bit == 0) { | |
119 | outptr++; | |
120 | bit = 0x80; | |
121 | } | |
122 | if (!lastpixel) | |
123 | thisptr[0] += v * 7 / 16; | |
124 | if (!lastline) { | |
125 | if (j != 0) | |
126 | nextptr[-1] += v * 3 / 16; | |
127 | *nextptr++ += v * 5 / 16; | |
128 | if (!lastpixel) | |
129 | nextptr[0] += v / 16; | |
130 | } | |
131 | } | |
132 | if (TIFFWriteScanline(out, outline, i-1, 0) < 0) | |
133 | break; | |
134 | } | |
80ed523f | 135 | skip_on_error: |
8414a40c VZ |
136 | _TIFFfree(inputline); |
137 | _TIFFfree(thisline); | |
138 | _TIFFfree(nextline); | |
139 | _TIFFfree(outline); | |
140 | } | |
141 | ||
142 | static uint16 compression = COMPRESSION_PACKBITS; | |
143 | static uint16 predictor = 0; | |
144 | static uint32 group3options = 0; | |
145 | ||
146 | static void | |
147 | processG3Options(char* cp) | |
148 | { | |
149 | if ((cp = strchr(cp, ':'))) { | |
150 | do { | |
151 | cp++; | |
152 | if (strneq(cp, "1d", 2)) | |
153 | group3options &= ~GROUP3OPT_2DENCODING; | |
154 | else if (strneq(cp, "2d", 2)) | |
155 | group3options |= GROUP3OPT_2DENCODING; | |
156 | else if (strneq(cp, "fill", 4)) | |
157 | group3options |= GROUP3OPT_FILLBITS; | |
158 | else | |
159 | usage(); | |
160 | } while ((cp = strchr(cp, ':'))); | |
161 | } | |
162 | } | |
163 | ||
164 | static int | |
165 | processCompressOptions(char* opt) | |
166 | { | |
167 | if (streq(opt, "none")) | |
168 | compression = COMPRESSION_NONE; | |
169 | else if (streq(opt, "packbits")) | |
170 | compression = COMPRESSION_PACKBITS; | |
171 | else if (strneq(opt, "g3", 2)) { | |
172 | processG3Options(opt); | |
173 | compression = COMPRESSION_CCITTFAX3; | |
174 | } else if (streq(opt, "g4")) | |
175 | compression = COMPRESSION_CCITTFAX4; | |
176 | else if (strneq(opt, "lzw", 3)) { | |
177 | char* cp = strchr(opt, ':'); | |
178 | if (cp) | |
179 | predictor = atoi(cp+1); | |
180 | compression = COMPRESSION_LZW; | |
181 | } else if (strneq(opt, "zip", 3)) { | |
182 | char* cp = strchr(opt, ':'); | |
183 | if (cp) | |
184 | predictor = atoi(cp+1); | |
185 | compression = COMPRESSION_DEFLATE; | |
186 | } else | |
187 | return (0); | |
188 | return (1); | |
189 | } | |
190 | ||
191 | int | |
192 | main(int argc, char* argv[]) | |
193 | { | |
194 | TIFF *in, *out; | |
195 | uint16 samplesperpixel, bitspersample = 1, shortv; | |
196 | float floatv; | |
197 | char thing[1024]; | |
198 | uint32 rowsperstrip = (uint32) -1; | |
8414a40c VZ |
199 | uint16 fillorder = 0; |
200 | int c; | |
201 | extern int optind; | |
202 | extern char *optarg; | |
203 | ||
204 | while ((c = getopt(argc, argv, "c:f:r:t:")) != -1) | |
205 | switch (c) { | |
206 | case 'c': /* compression scheme */ | |
207 | if (!processCompressOptions(optarg)) | |
208 | usage(); | |
209 | break; | |
210 | case 'f': /* fill order */ | |
211 | if (streq(optarg, "lsb2msb")) | |
212 | fillorder = FILLORDER_LSB2MSB; | |
213 | else if (streq(optarg, "msb2lsb")) | |
214 | fillorder = FILLORDER_MSB2LSB; | |
215 | else | |
216 | usage(); | |
217 | break; | |
218 | case 'r': /* rows/strip */ | |
219 | rowsperstrip = atoi(optarg); | |
8414a40c VZ |
220 | break; |
221 | case 't': | |
222 | threshold = atoi(optarg); | |
223 | if (threshold < 0) | |
224 | threshold = 0; | |
225 | else if (threshold > 255) | |
226 | threshold = 255; | |
227 | break; | |
228 | case '?': | |
229 | usage(); | |
230 | /*NOTREACHED*/ | |
231 | } | |
232 | if (argc - optind < 2) | |
233 | usage(); | |
234 | in = TIFFOpen(argv[optind], "r"); | |
235 | if (in == NULL) | |
236 | return (-1); | |
237 | TIFFGetField(in, TIFFTAG_SAMPLESPERPIXEL, &samplesperpixel); | |
238 | if (samplesperpixel != 1) { | |
239 | fprintf(stderr, "%s: Not a b&w image.\n", argv[0]); | |
240 | return (-1); | |
241 | } | |
242 | TIFFGetField(in, TIFFTAG_BITSPERSAMPLE, &bitspersample); | |
243 | if (bitspersample != 8) { | |
244 | fprintf(stderr, | |
245 | " %s: Sorry, only handle 8-bit samples.\n", argv[0]); | |
246 | return (-1); | |
247 | } | |
248 | out = TIFFOpen(argv[optind+1], "w"); | |
249 | if (out == NULL) | |
250 | return (-1); | |
251 | CopyField(TIFFTAG_IMAGEWIDTH, imagewidth); | |
252 | TIFFGetField(in, TIFFTAG_IMAGELENGTH, &imagelength); | |
253 | TIFFSetField(out, TIFFTAG_IMAGELENGTH, imagelength-1); | |
254 | TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, 1); | |
255 | TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, 1); | |
256 | TIFFSetField(out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); | |
257 | TIFFSetField(out, TIFFTAG_COMPRESSION, compression); | |
258 | if (fillorder) | |
259 | TIFFSetField(out, TIFFTAG_FILLORDER, fillorder); | |
260 | else | |
261 | CopyField(TIFFTAG_FILLORDER, shortv); | |
262 | sprintf(thing, "Dithered B&W version of %s", argv[optind]); | |
263 | TIFFSetField(out, TIFFTAG_IMAGEDESCRIPTION, thing); | |
264 | CopyField(TIFFTAG_PHOTOMETRIC, shortv); | |
265 | CopyField(TIFFTAG_ORIENTATION, shortv); | |
266 | CopyField(TIFFTAG_XRESOLUTION, floatv); | |
267 | CopyField(TIFFTAG_YRESOLUTION, floatv); | |
268 | CopyField(TIFFTAG_RESOLUTIONUNIT, shortv); | |
80ed523f | 269 | rowsperstrip = TIFFDefaultStripSize(out, rowsperstrip); |
8414a40c VZ |
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: */ | |
80ed523f VZ |
326 | /* |
327 | * Local Variables: | |
328 | * mode: c | |
329 | * c-basic-offset: 8 | |
330 | * fill-column: 78 | |
331 | * End: | |
332 | */ |