3 * Copyright (c) 1991-1997 Sam Leffler
4 * Copyright (c) 1991-1997 Silicon Graphics, Inc.
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.
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.
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
26 #include "tif_config.h"
43 #define streq(a,b) (strcmp(a,b) == 0)
44 #define CopyField(tag, v) \
45 if (TIFFGetField(in, tag, &v)) TIFFSetField(out, tag, v)
48 #define howmany(x, y) (((x)+((y)-1))/(y))
50 #define roundup(x, y) (howmany(x,y)*((uint32)(y)))
52 #define LumaRed ycbcrCoeffs[0]
53 #define LumaGreen ycbcrCoeffs[1]
54 #define LumaBlue ycbcrCoeffs[2]
56 uint16 compression
= COMPRESSION_PACKBITS
;
57 uint32 rowsperstrip
= (uint32
) -1;
59 uint16 horizSubSampling
= 2; /* YCbCr horizontal subsampling */
60 uint16 vertSubSampling
= 2; /* YCbCr vertical subsampling */
61 float ycbcrCoeffs
[3] = { .299F
, .587F
, .114F
};
62 /* default coding range is CCIR Rec 601-1 with no headroom/footroom */
63 float refBlackWhite
[6] = { 0.F
, 255.F
, 128.F
, 255.F
, 128.F
, 255.F
};
65 static int tiffcvt(TIFF
* in
, TIFF
* out
);
66 static void usage(int code
);
67 static void setupLumaTables(void);
70 main(int argc
, char* argv
[])
77 while ((c
= getopt(argc
, argv
, "c:h:r:v:z")) != -1)
80 if (streq(optarg
, "none"))
81 compression
= COMPRESSION_NONE
;
82 else if (streq(optarg
, "packbits"))
83 compression
= COMPRESSION_PACKBITS
;
84 else if (streq(optarg
, "lzw"))
85 compression
= COMPRESSION_LZW
;
86 else if (streq(optarg
, "jpeg"))
87 compression
= COMPRESSION_JPEG
;
88 else if (streq(optarg
, "zip"))
89 compression
= COMPRESSION_ADOBE_DEFLATE
;
94 horizSubSampling
= atoi(optarg
);
97 vertSubSampling
= atoi(optarg
);
100 rowsperstrip
= atoi(optarg
);
102 case 'z': /* CCIR Rec 601-1 w/ headroom/footroom */
103 refBlackWhite
[0] = 16.;
104 refBlackWhite
[1] = 235.;
105 refBlackWhite
[2] = 128.;
106 refBlackWhite
[3] = 240.;
107 refBlackWhite
[4] = 128.;
108 refBlackWhite
[5] = 240.;
114 if (argc
- optind
< 2)
116 out
= TIFFOpen(argv
[argc
-1], "w");
120 for (; optind
< argc
-1; optind
++) {
121 in
= TIFFOpen(argv
[optind
], "r");
124 if (!tiffcvt(in
, out
) ||
125 !TIFFWriteDirectory(out
)) {
126 (void) TIFFClose(out
);
129 } while (TIFFReadDirectory(in
));
130 (void) TIFFClose(in
);
133 (void) TIFFClose(out
);
146 float *v
= (float *)_TIFFmalloc(256 * sizeof (float));
148 for (i
= 0; i
< 256; i
++)
154 V2Code(float f
, float RB
, float RW
, int CR
)
156 unsigned int c
= (unsigned int)((((f
)*(RW
-RB
)/CR
)+RB
)+.5);
157 return (c
> 255 ? 255 : c
);
161 setupLumaTables(void)
163 lumaRed
= setupLuma(LumaRed
);
164 lumaGreen
= setupLuma(LumaGreen
);
165 lumaBlue
= setupLuma(LumaBlue
);
166 D1
= 1.F
/(2.F
- 2.F
*LumaBlue
);
167 D2
= 1.F
/(2.F
- 2.F
*LumaRed
);
168 Yzero
= V2Code(0, refBlackWhite
[0], refBlackWhite
[1], 255);
172 cvtClump(unsigned char* op
, uint32
* raster
, uint32 ch
, uint32 cw
, uint32 w
)
174 float Y
, Cb
= 0, Cr
= 0;
177 * Convert ch-by-cw block of RGB
178 * to YCbCr and sample accordingly.
180 for (k
= 0; k
< ch
; k
++) {
181 for (j
= 0; j
< cw
; j
++) {
182 uint32 RGB
= (raster
- k
*w
)[j
];
183 Y
= lumaRed
[TIFFGetR(RGB
)] +
184 lumaGreen
[TIFFGetG(RGB
)] +
185 lumaBlue
[TIFFGetB(RGB
)];
186 /* accumulate chrominance */
187 Cb
+= (TIFFGetB(RGB
) - Y
) * D1
;
188 Cr
+= (TIFFGetR(RGB
) - Y
) * D2
;
191 refBlackWhite
[0], refBlackWhite
[1], 255);
193 for (; j
< horizSubSampling
; j
++)
196 for (; k
< vertSubSampling
; k
++) {
197 for (j
= 0; j
< horizSubSampling
; j
++)
200 /* emit sampled chrominance values */
201 *op
++ = V2Code(Cb
/ (ch
*cw
), refBlackWhite
[2], refBlackWhite
[3], 127);
202 *op
++ = V2Code(Cr
/ (ch
*cw
), refBlackWhite
[4], refBlackWhite
[5], 127);
210 * Convert a strip of RGB data to YCbCr and
211 * sample to generate the output data.
214 cvtStrip(unsigned char* op
, uint32
* raster
, uint32 nrows
, uint32 width
)
217 int clumpSize
= vertSubSampling
* horizSubSampling
+ 2;
220 for (; nrows
>= vertSubSampling
; nrows
-= vertSubSampling
) {
222 for (x
= width
; x
>= horizSubSampling
; x
-= horizSubSampling
) {
224 vertSubSampling
, horizSubSampling
, width
);
226 tp
+= horizSubSampling
;
229 cvtClump(op
, tp
, vertSubSampling
, x
, width
);
232 raster
-= vertSubSampling
*width
;
236 for (x
= width
; x
>= horizSubSampling
; x
-= horizSubSampling
) {
237 cvtClump(op
, tp
, nrows
, horizSubSampling
, width
);
239 tp
+= horizSubSampling
;
242 cvtClump(op
, tp
, nrows
, x
, width
);
247 cvtRaster(TIFF
* tif
, uint32
* raster
, uint32 width
, uint32 height
)
253 uint32 rwidth
= roundup(width
, horizSubSampling
);
254 uint32 rheight
= roundup(height
, vertSubSampling
);
255 uint32 nrows
= (rowsperstrip
> rheight
? rheight
: rowsperstrip
);
256 uint32 rnrows
= roundup(nrows
,vertSubSampling
);
259 2*((rnrows
*rwidth
) / (horizSubSampling
*vertSubSampling
));
260 buf
= (unsigned char*)_TIFFmalloc(cc
);
261 // FIXME unchecked malloc
262 for (y
= height
; (int32
) y
> 0; y
-= nrows
) {
263 uint32 nr
= (y
> nrows
? nrows
: y
);
264 cvtStrip(buf
, raster
+ (y
-1)*width
, nr
, width
);
265 nr
= roundup(nr
, vertSubSampling
);
267 2*((nr
*rwidth
)/(horizSubSampling
*vertSubSampling
));
268 if (!TIFFWriteEncodedStrip(tif
, strip
++, buf
, acc
)) {
278 tiffcvt(TIFF
* in
, TIFF
* out
)
280 uint32 width
, height
; /* image width & height */
281 uint32
* raster
; /* retrieve RGBA image */
289 TIFFGetField(in
, TIFFTAG_IMAGEWIDTH
, &width
);
290 TIFFGetField(in
, TIFFTAG_IMAGELENGTH
, &height
);
291 pixel_count
= width
* height
;
293 /* XXX: Check the integer overflow. */
294 if (!width
|| !height
|| pixel_count
/ width
!= height
) {
295 TIFFError(TIFFFileName(in
),
296 "Malformed input file; "
297 "can't allocate buffer for raster of %lux%lu size",
298 (unsigned long)width
, (unsigned long)height
);
302 raster
= (uint32
*)_TIFFCheckMalloc(in
, pixel_count
, sizeof(uint32
),
305 TIFFError(TIFFFileName(in
),
306 "Failed to allocate buffer (%lu elements of %lu each)",
307 (unsigned long)pixel_count
,
308 (unsigned long)sizeof(uint32
));
312 if (!TIFFReadRGBAImage(in
, width
, height
, raster
, 0)) {
317 CopyField(TIFFTAG_SUBFILETYPE
, longv
);
318 TIFFSetField(out
, TIFFTAG_IMAGEWIDTH
, width
);
319 TIFFSetField(out
, TIFFTAG_IMAGELENGTH
, height
);
320 TIFFSetField(out
, TIFFTAG_BITSPERSAMPLE
, 8);
321 TIFFSetField(out
, TIFFTAG_COMPRESSION
, compression
);
322 TIFFSetField(out
, TIFFTAG_PHOTOMETRIC
, PHOTOMETRIC_YCBCR
);
323 if (compression
== COMPRESSION_JPEG
)
324 TIFFSetField(out
, TIFFTAG_JPEGCOLORMODE
, JPEGCOLORMODE_RAW
);
325 CopyField(TIFFTAG_FILLORDER
, shortv
);
326 TIFFSetField(out
, TIFFTAG_ORIENTATION
, ORIENTATION_TOPLEFT
);
327 TIFFSetField(out
, TIFFTAG_SAMPLESPERPIXEL
, 3);
328 CopyField(TIFFTAG_XRESOLUTION
, floatv
);
329 CopyField(TIFFTAG_YRESOLUTION
, floatv
);
330 CopyField(TIFFTAG_RESOLUTIONUNIT
, shortv
);
331 TIFFSetField(out
, TIFFTAG_PLANARCONFIG
, PLANARCONFIG_CONTIG
);
333 char *cp
= strrchr(TIFFFileName(in
), '/');
334 sprintf(buf
, "YCbCr conversion of %s", cp
? cp
+1 : TIFFFileName(in
));
335 TIFFSetField(out
, TIFFTAG_IMAGEDESCRIPTION
, buf
);
337 TIFFSetField(out
, TIFFTAG_SOFTWARE
, TIFFGetVersion());
338 CopyField(TIFFTAG_DOCUMENTNAME
, stringv
);
340 TIFFSetField(out
, TIFFTAG_REFERENCEBLACKWHITE
, refBlackWhite
);
341 TIFFSetField(out
, TIFFTAG_YCBCRSUBSAMPLING
,
342 horizSubSampling
, vertSubSampling
);
343 TIFFSetField(out
, TIFFTAG_YCBCRPOSITIONING
, YCBCRPOSITION_CENTERED
);
344 TIFFSetField(out
, TIFFTAG_YCBCRCOEFFICIENTS
, ycbcrCoeffs
);
345 rowsperstrip
= TIFFDefaultStripSize(out
, rowsperstrip
);
346 TIFFSetField(out
, TIFFTAG_ROWSPERSTRIP
, rowsperstrip
);
348 result
= cvtRaster(out
, raster
, width
, height
);
354 "usage: rgb2ycbcr [-c comp] [-r rows] [-h N] [-v N] input... output\n",
355 "where comp is one of the following compression algorithms:\n",
356 " jpeg\t\tJPEG encoding\n",
357 " lzw\t\tLempel-Ziv & Welch encoding\n",
358 " zip\t\tdeflate encoding\n",
359 " packbits\tPackBits encoding (default)\n",
360 " none\t\tno compression\n",
361 "and the other options are:\n",
363 " -h\thorizontal sampling factor (1,2,4)\n",
364 " -v\tvertical sampling factor (1,2,4)\n",
376 fprintf(stderr
, "%s\n\n", TIFFGetVersion());
377 for (i
= 0; stuff
[i
] != NULL
; i
++)
378 fprintf(stderr
, "%s\n", stuff
[i
]);
382 /* vim: set ts=8 sts=8 sw=8 noet: */