1 /* $Id: tiffdither.c,v 1.12 2010-03-10 18:56:50 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 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 */ 334