1/* $Id: tiffdither.c 276 2010-06-30 12:18:30Z nijtmans $ */ 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 45uint32 imagewidth; 46uint32 imagelength; 47int threshold = 128; 48 49static void usage(void); 50 51/* 52 * Floyd-Steinberg error propragation with threshold. 53 * This code is stolen from tiffmedian. 54 */ 55static void 56fsdither(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 137static uint16 compression = COMPRESSION_PACKBITS; 138static uint16 predictor = 0; 139static uint32 group3options = 0; 140 141static void 142processG3Options(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 159static int 160processCompressOptions(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 186int 187main(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 287char* 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", 309NULL 310}; 311 312static void 313usage(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: */ 326/* 327 * Local Variables: 328 * mode: c 329 * c-basic-offset: 8 330 * fill-column: 78 331 * End: 332 */ 333