1
2 /*
3 * Copyright (c) 1991-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 <string.h>
30 #include <stdlib.h>
31
32 #ifdef HAVE_UNISTD_H
33 # include <unistd.h>
34 #endif
35
36 #ifdef NEED_LIBPORT
37 # include "libport.h"
38 #endif
39
40 #include "tiffiop.h"
41 #include "tiffio.h"
42
43 #define streq(a,b) (strcmp(a,b) == 0)
44 #define CopyField(tag, v) \
45 if (TIFFGetField(in, tag, &v)) TIFFSetField(out, tag, v)
46
47 #ifndef howmany
48 #define howmany(x, y) (((x)+((y)-1))/(y))
49 #endif
50 #define roundup(x, y) (howmany(x,y)*((uint32)(y)))
51
52 #define LumaRed ycbcrCoeffs[0]
53 #define LumaGreen ycbcrCoeffs[1]
54 #define LumaBlue ycbcrCoeffs[2]
55
56 uint16 compression = COMPRESSION_PACKBITS;
57 uint32 rowsperstrip = (uint32) -1;
58
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 };
64
65 static int tiffcvt(TIFF* in, TIFF* out);
66 static void usage(int code);
67 static void setupLumaTables(void);
68
69 int
main(int argc,char * argv[])70 main(int argc, char* argv[])
71 {
72 TIFF *in, *out;
73 int c;
74 extern int optind;
75 extern char *optarg;
76
77 while ((c = getopt(argc, argv, "c:h:r:v:z")) != -1)
78 switch (c) {
79 case 'c':
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;
90 else
91 usage(-1);
92 break;
93 case 'h':
94 horizSubSampling = atoi(optarg);
95 break;
96 case 'v':
97 vertSubSampling = atoi(optarg);
98 break;
99 case 'r':
100 rowsperstrip = atoi(optarg);
101 break;
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.;
109 break;
110 case '?':
111 usage(0);
112 /*NOTREACHED*/
113 }
114 if (argc - optind < 2)
115 usage(-1);
116 out = TIFFOpen(argv[argc-1], "w");
117 if (out == NULL)
118 return (-2);
119 setupLumaTables();
120 for (; optind < argc-1; optind++) {
121 in = TIFFOpen(argv[optind], "r");
122 if (in != NULL) {
123 do {
124 if (!tiffcvt(in, out) ||
125 !TIFFWriteDirectory(out)) {
126 (void) TIFFClose(out);
127 return (1);
128 }
129 } while (TIFFReadDirectory(in));
130 (void) TIFFClose(in);
131 }
132 }
133 (void) TIFFClose(out);
134 return (0);
135 }
136
137 float *lumaRed;
138 float *lumaGreen;
139 float *lumaBlue;
140 float D1, D2;
141 int Yzero;
142
143 static float*
setupLuma(float c)144 setupLuma(float c)
145 {
146 float *v = (float *)_TIFFmalloc(256 * sizeof (float));
147 int i;
148 for (i = 0; i < 256; i++)
149 v[i] = c * i;
150 return (v);
151 }
152
153 static unsigned
V2Code(float f,float RB,float RW,int CR)154 V2Code(float f, float RB, float RW, int CR)
155 {
156 unsigned int c = (unsigned int)((((f)*(RW-RB)/CR)+RB)+.5);
157 return (c > 255 ? 255 : c);
158 }
159
160 static void
setupLumaTables(void)161 setupLumaTables(void)
162 {
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);
169 }
170
171 static void
cvtClump(unsigned char * op,uint32 * raster,uint32 ch,uint32 cw,uint32 w)172 cvtClump(unsigned char* op, uint32* raster, uint32 ch, uint32 cw, uint32 w)
173 {
174 float Y, Cb = 0, Cr = 0;
175 uint32 j, k;
176 /*
177 * Convert ch-by-cw block of RGB
178 * to YCbCr and sample accordingly.
179 */
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;
189 /* emit luminence */
190 *op++ = V2Code(Y,
191 refBlackWhite[0], refBlackWhite[1], 255);
192 }
193 for (; j < horizSubSampling; j++)
194 *op++ = Yzero;
195 }
196 for (; k < vertSubSampling; k++) {
197 for (j = 0; j < horizSubSampling; j++)
198 *op++ = Yzero;
199 }
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);
203 }
204 #undef LumaRed
205 #undef LumaGreen
206 #undef LumaBlue
207 #undef V2Code
208
209 /*
210 * Convert a strip of RGB data to YCbCr and
211 * sample to generate the output data.
212 */
213 static void
cvtStrip(unsigned char * op,uint32 * raster,uint32 nrows,uint32 width)214 cvtStrip(unsigned char* op, uint32* raster, uint32 nrows, uint32 width)
215 {
216 uint32 x;
217 int clumpSize = vertSubSampling * horizSubSampling + 2;
218 uint32 *tp;
219
220 for (; nrows >= vertSubSampling; nrows -= vertSubSampling) {
221 tp = raster;
222 for (x = width; x >= horizSubSampling; x -= horizSubSampling) {
223 cvtClump(op, tp,
224 vertSubSampling, horizSubSampling, width);
225 op += clumpSize;
226 tp += horizSubSampling;
227 }
228 if (x > 0) {
229 cvtClump(op, tp, vertSubSampling, x, width);
230 op += clumpSize;
231 }
232 raster -= vertSubSampling*width;
233 }
234 if (nrows > 0) {
235 tp = raster;
236 for (x = width; x >= horizSubSampling; x -= horizSubSampling) {
237 cvtClump(op, tp, nrows, horizSubSampling, width);
238 op += clumpSize;
239 tp += horizSubSampling;
240 }
241 if (x > 0)
242 cvtClump(op, tp, nrows, x, width);
243 }
244 }
245
246 static int
cvtRaster(TIFF * tif,uint32 * raster,uint32 width,uint32 height)247 cvtRaster(TIFF* tif, uint32* raster, uint32 width, uint32 height)
248 {
249 uint32 y;
250 tstrip_t strip = 0;
251 tsize_t cc, acc;
252 unsigned char* buf;
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);
257
258 cc = rnrows*rwidth +
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);
266 acc = nr*rwidth +
267 2*((nr*rwidth)/(horizSubSampling*vertSubSampling));
268 if (!TIFFWriteEncodedStrip(tif, strip++, buf, acc)) {
269 _TIFFfree(buf);
270 return (0);
271 }
272 }
273 _TIFFfree(buf);
274 return (1);
275 }
276
277 static int
tiffcvt(TIFF * in,TIFF * out)278 tiffcvt(TIFF* in, TIFF* out)
279 {
280 uint32 width, height; /* image width & height */
281 uint32* raster; /* retrieve RGBA image */
282 uint16 shortv;
283 float floatv;
284 char *stringv;
285 uint32 longv;
286 int result;
287 size_t pixel_count;
288
289 TIFFGetField(in, TIFFTAG_IMAGEWIDTH, &width);
290 TIFFGetField(in, TIFFTAG_IMAGELENGTH, &height);
291 pixel_count = width * height;
292
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);
299 return 0;
300 }
301
302 raster = (uint32*)_TIFFCheckMalloc(in, pixel_count, sizeof(uint32),
303 "raster buffer");
304 if (raster == 0) {
305 TIFFError(TIFFFileName(in),
306 "Failed to allocate buffer (%lu elements of %lu each)",
307 (unsigned long)pixel_count,
308 (unsigned long)sizeof(uint32));
309 return (0);
310 }
311
312 if (!TIFFReadRGBAImage(in, width, height, raster, 0)) {
313 _TIFFfree(raster);
314 return (0);
315 }
316
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);
332 { char buf[2048];
333 char *cp = strrchr(TIFFFileName(in), '/');
334 sprintf(buf, "YCbCr conversion of %s", cp ? cp+1 : TIFFFileName(in));
335 TIFFSetField(out, TIFFTAG_IMAGEDESCRIPTION, buf);
336 }
337 TIFFSetField(out, TIFFTAG_SOFTWARE, TIFFGetVersion());
338 CopyField(TIFFTAG_DOCUMENTNAME, stringv);
339
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);
347
348 result = cvtRaster(out, raster, width, height);
349 _TIFFfree(raster);
350 return result;
351 }
352
353 char* stuff[] = {
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",
362 " -r\trows/strip\n",
363 " -h\thorizontal sampling factor (1,2,4)\n",
364 " -v\tvertical sampling factor (1,2,4)\n",
365 NULL
366 };
367
368 static void
usage(int code)369 usage(int code)
370 {
371 char buf[BUFSIZ];
372 int i;
373
374 setbuf(stderr, buf);
375
376 fprintf(stderr, "%s\n\n", TIFFGetVersion());
377 for (i = 0; stuff[i] != NULL; i++)
378 fprintf(stderr, "%s\n", stuff[i]);
379 exit(code);
380 }
381
382 /* vim: set ts=8 sts=8 sw=8 noet: */
383 /*
384 * Local Variables:
385 * mode: c
386 * c-basic-offset: 8
387 * fill-column: 78
388 * End:
389 */
390