1/* $Id: rgb2ycbcr.c 276 2010-06-30 12:18:30Z nijtmans $ */ 2 3/* 4 * Copyright (c) 1991-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 <string.h> 31#include <stdlib.h> 32 33#ifdef HAVE_UNISTD_H 34# include <unistd.h> 35#endif 36 37#include "tiffiop.h" 38#include "tiffio.h" 39 40#define streq(a,b) (strcmp(a,b) == 0) 41#define CopyField(tag, v) \ 42 if (TIFFGetField(in, tag, &v)) TIFFSetField(out, tag, v) 43 44#ifndef howmany 45#define howmany(x, y) (((x)+((y)-1))/(y)) 46#endif 47#define roundup(x, y) (howmany(x,y)*((uint32)(y))) 48 49#define LumaRed ycbcrCoeffs[0] 50#define LumaGreen ycbcrCoeffs[1] 51#define LumaBlue ycbcrCoeffs[2] 52 53uint16 compression = COMPRESSION_PACKBITS; 54uint32 rowsperstrip = (uint32) -1; 55 56uint16 horizSubSampling = 2; /* YCbCr horizontal subsampling */ 57uint16 vertSubSampling = 2; /* YCbCr vertical subsampling */ 58float ycbcrCoeffs[3] = { .299F, .587F, .114F }; 59/* default coding range is CCIR Rec 601-1 with no headroom/footroom */ 60float refBlackWhite[6] = { 0.F, 255.F, 128.F, 255.F, 128.F, 255.F }; 61 62static int tiffcvt(TIFF* in, TIFF* out); 63static void usage(int code); 64static void setupLumaTables(void); 65 66int 67main(int argc, char* argv[]) 68{ 69 TIFF *in, *out; 70 int c; 71 extern int optind; 72 extern char *optarg; 73 74 while ((c = getopt(argc, argv, "c:h:r:v:z")) != -1) 75 switch (c) { 76 case 'c': 77 if (streq(optarg, "none")) 78 compression = COMPRESSION_NONE; 79 else if (streq(optarg, "packbits")) 80 compression = COMPRESSION_PACKBITS; 81 else if (streq(optarg, "lzw")) 82 compression = COMPRESSION_LZW; 83 else if (streq(optarg, "jpeg")) 84 compression = COMPRESSION_JPEG; 85 else if (streq(optarg, "zip")) 86 compression = COMPRESSION_ADOBE_DEFLATE; 87 else 88 usage(-1); 89 break; 90 case 'h': 91 horizSubSampling = atoi(optarg); 92 break; 93 case 'v': 94 vertSubSampling = atoi(optarg); 95 break; 96 case 'r': 97 rowsperstrip = atoi(optarg); 98 break; 99 case 'z': /* CCIR Rec 601-1 w/ headroom/footroom */ 100 refBlackWhite[0] = 16.; 101 refBlackWhite[1] = 235.; 102 refBlackWhite[2] = 128.; 103 refBlackWhite[3] = 240.; 104 refBlackWhite[4] = 128.; 105 refBlackWhite[5] = 240.; 106 break; 107 case '?': 108 usage(0); 109 /*NOTREACHED*/ 110 } 111 if (argc - optind < 2) 112 usage(-1); 113 out = TIFFOpen(argv[argc-1], "w"); 114 if (out == NULL) 115 return (-2); 116 setupLumaTables(); 117 for (; optind < argc-1; optind++) { 118 in = TIFFOpen(argv[optind], "r"); 119 if (in != NULL) { 120 do { 121 if (!tiffcvt(in, out) || 122 !TIFFWriteDirectory(out)) { 123 (void) TIFFClose(out); 124 return (1); 125 } 126 } while (TIFFReadDirectory(in)); 127 (void) TIFFClose(in); 128 } 129 } 130 (void) TIFFClose(out); 131 return (0); 132} 133 134float *lumaRed; 135float *lumaGreen; 136float *lumaBlue; 137float D1, D2; 138int Yzero; 139 140static float* 141setupLuma(float c) 142{ 143 float *v = (float *)_TIFFmalloc(256 * sizeof (float)); 144 int i; 145 for (i = 0; i < 256; i++) 146 v[i] = c * i; 147 return (v); 148} 149 150static unsigned 151V2Code(float f, float RB, float RW, int CR) 152{ 153 unsigned int c = (unsigned int)((((f)*(RW-RB)/CR)+RB)+.5); 154 return (c > 255 ? 255 : c); 155} 156 157static void 158setupLumaTables(void) 159{ 160 lumaRed = setupLuma(LumaRed); 161 lumaGreen = setupLuma(LumaGreen); 162 lumaBlue = setupLuma(LumaBlue); 163 D1 = 1.F/(2.F - 2.F*LumaBlue); 164 D2 = 1.F/(2.F - 2.F*LumaRed); 165 Yzero = V2Code(0, refBlackWhite[0], refBlackWhite[1], 255); 166} 167 168static void 169cvtClump(unsigned char* op, uint32* raster, uint32 ch, uint32 cw, uint32 w) 170{ 171 float Y, Cb = 0, Cr = 0; 172 uint32 j, k; 173 /* 174 * Convert ch-by-cw block of RGB 175 * to YCbCr and sample accordingly. 176 */ 177 for (k = 0; k < ch; k++) { 178 for (j = 0; j < cw; j++) { 179 uint32 RGB = (raster - k*w)[j]; 180 Y = lumaRed[TIFFGetR(RGB)] + 181 lumaGreen[TIFFGetG(RGB)] + 182 lumaBlue[TIFFGetB(RGB)]; 183 /* accumulate chrominance */ 184 Cb += (TIFFGetB(RGB) - Y) * D1; 185 Cr += (TIFFGetR(RGB) - Y) * D2; 186 /* emit luminence */ 187 *op++ = V2Code(Y, 188 refBlackWhite[0], refBlackWhite[1], 255); 189 } 190 for (; j < horizSubSampling; j++) 191 *op++ = Yzero; 192 } 193 for (; k < vertSubSampling; k++) { 194 for (j = 0; j < horizSubSampling; j++) 195 *op++ = Yzero; 196 } 197 /* emit sampled chrominance values */ 198 *op++ = V2Code(Cb / (ch*cw), refBlackWhite[2], refBlackWhite[3], 127); 199 *op++ = V2Code(Cr / (ch*cw), refBlackWhite[4], refBlackWhite[5], 127); 200} 201#undef LumaRed 202#undef LumaGreen 203#undef LumaBlue 204#undef V2Code 205 206/* 207 * Convert a strip of RGB data to YCbCr and 208 * sample to generate the output data. 209 */ 210static void 211cvtStrip(unsigned char* op, uint32* raster, uint32 nrows, uint32 width) 212{ 213 uint32 x; 214 int clumpSize = vertSubSampling * horizSubSampling + 2; 215 uint32 *tp; 216 217 for (; nrows >= vertSubSampling; nrows -= vertSubSampling) { 218 tp = raster; 219 for (x = width; x >= horizSubSampling; x -= horizSubSampling) { 220 cvtClump(op, tp, 221 vertSubSampling, horizSubSampling, width); 222 op += clumpSize; 223 tp += horizSubSampling; 224 } 225 if (x > 0) { 226 cvtClump(op, tp, vertSubSampling, x, width); 227 op += clumpSize; 228 } 229 raster -= vertSubSampling*width; 230 } 231 if (nrows > 0) { 232 tp = raster; 233 for (x = width; x >= horizSubSampling; x -= horizSubSampling) { 234 cvtClump(op, tp, nrows, horizSubSampling, width); 235 op += clumpSize; 236 tp += horizSubSampling; 237 } 238 if (x > 0) 239 cvtClump(op, tp, nrows, x, width); 240 } 241} 242 243static int 244cvtRaster(TIFF* tif, uint32* raster, uint32 width, uint32 height) 245{ 246 uint32 y; 247 tstrip_t strip = 0; 248 tsize_t cc, acc; 249 unsigned char* buf; 250 uint32 rwidth = roundup(width, horizSubSampling); 251 uint32 rheight = roundup(height, vertSubSampling); 252 uint32 nrows = (rowsperstrip > rheight ? rheight : rowsperstrip); 253 uint32 rnrows = roundup(nrows,vertSubSampling); 254 255 cc = rnrows*rwidth + 256 2*((rnrows*rwidth) / (horizSubSampling*vertSubSampling)); 257 buf = (unsigned char*)_TIFFmalloc(cc); 258 for (y = height; (int32) y > 0; y -= nrows) { 259 uint32 nr = (y > nrows ? nrows : y); 260 cvtStrip(buf, raster + (y-1)*width, nr, width); 261 nr = roundup(nr, vertSubSampling); 262 acc = nr*rwidth + 263 2*((nr*rwidth)/(horizSubSampling*vertSubSampling)); 264 if (!TIFFWriteEncodedStrip(tif, strip++, buf, acc)) { 265 _TIFFfree(buf); 266 return (0); 267 } 268 } 269 _TIFFfree(buf); 270 return (1); 271} 272 273static int 274tiffcvt(TIFF* in, TIFF* out) 275{ 276 uint32 width, height; /* image width & height */ 277 uint32* raster; /* retrieve RGBA image */ 278 uint16 shortv; 279 float floatv; 280 char *stringv; 281 uint32 longv; 282 283 size_t pixel_count; 284 TIFFGetField(in, TIFFTAG_IMAGEWIDTH, &width); 285 TIFFGetField(in, TIFFTAG_IMAGELENGTH, &height); 286 pixel_count = width * height; 287 288 /* XXX: Check the integer overflow. */ 289 if (!width || !height || pixel_count / width != height) { 290 TIFFError(TIFFFileName(in), 291 "Malformed input file; " 292 "can't allocate buffer for raster of %lux%lu size", 293 (unsigned long)width, (unsigned long)height); 294 return 0; 295 } 296 297 raster = (uint32*)_TIFFCheckMalloc(in, pixel_count, sizeof(uint32), 298 "raster buffer"); 299 if (raster == 0) { 300 TIFFError(TIFFFileName(in), 301 "Requested buffer size is %lu elements %lu each", 302 (unsigned long)pixel_count, 303 (unsigned long)sizeof(uint32)); 304 return (0); 305 } 306 307 if (!TIFFReadRGBAImage(in, width, height, raster, 0)) { 308 _TIFFfree(raster); 309 return (0); 310 } 311 312 CopyField(TIFFTAG_SUBFILETYPE, longv); 313 TIFFSetField(out, TIFFTAG_IMAGEWIDTH, width); 314 TIFFSetField(out, TIFFTAG_IMAGELENGTH, height); 315 TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, 8); 316 TIFFSetField(out, TIFFTAG_COMPRESSION, compression); 317 TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_YCBCR); 318 if (compression == COMPRESSION_JPEG) 319 TIFFSetField(out, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RAW); 320 CopyField(TIFFTAG_FILLORDER, shortv); 321 TIFFSetField(out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); 322 TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, 3); 323 CopyField(TIFFTAG_XRESOLUTION, floatv); 324 CopyField(TIFFTAG_YRESOLUTION, floatv); 325 CopyField(TIFFTAG_RESOLUTIONUNIT, shortv); 326 TIFFSetField(out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); 327 { char buf[2048]; 328 char *cp = strrchr(TIFFFileName(in), '/'); 329 sprintf(buf, "YCbCr conversion of %s", cp ? cp+1 : TIFFFileName(in)); 330 TIFFSetField(out, TIFFTAG_IMAGEDESCRIPTION, buf); 331 } 332 TIFFSetField(out, TIFFTAG_SOFTWARE, TIFFGetVersion()); 333 CopyField(TIFFTAG_DOCUMENTNAME, stringv); 334 335 TIFFSetField(out, TIFFTAG_REFERENCEBLACKWHITE, refBlackWhite); 336 TIFFSetField(out, TIFFTAG_YCBCRSUBSAMPLING, 337 horizSubSampling, vertSubSampling); 338 TIFFSetField(out, TIFFTAG_YCBCRPOSITIONING, YCBCRPOSITION_CENTERED); 339 TIFFSetField(out, TIFFTAG_YCBCRCOEFFICIENTS, ycbcrCoeffs); 340 rowsperstrip = TIFFDefaultStripSize(out, rowsperstrip); 341 TIFFSetField(out, TIFFTAG_ROWSPERSTRIP, rowsperstrip); 342 343 return (cvtRaster(out, raster, width, height)); 344} 345 346char* stuff[] = { 347 "usage: rgb2ycbcr [-c comp] [-r rows] [-h N] [-v N] input... output\n", 348 "where comp is one of the following compression algorithms:\n", 349 " jpeg\t\tJPEG encoding\n", 350 " lzw\t\tLempel-Ziv & Welch encoding\n", 351 " zip\t\tdeflate encoding\n", 352 " packbits\tPackBits encoding (default)\n", 353 " none\t\tno compression\n", 354 "and the other options are:\n", 355 " -r\trows/strip\n", 356 " -h\thorizontal sampling factor (1,2,4)\n", 357 " -v\tvertical sampling factor (1,2,4)\n", 358 NULL 359}; 360 361static void 362usage(int code) 363{ 364 char buf[BUFSIZ]; 365 int i; 366 367 setbuf(stderr, buf); 368 369 fprintf(stderr, "%s\n\n", TIFFGetVersion()); 370 for (i = 0; stuff[i] != NULL; i++) 371 fprintf(stderr, "%s\n", stuff[i]); 372 exit(code); 373} 374 375/* vim: set ts=8 sts=8 sw=8 noet: */ 376/* 377 * Local Variables: 378 * mode: c 379 * c-basic-offset: 8 380 * fill-column: 78 381 * End: 382 */ 383