]>
Commit | Line | Data |
---|---|---|
8414a40c VZ |
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 | ||
80ed523f VZ |
36 | #ifdef NEED_LIBPORT |
37 | # include "libport.h" | |
38 | #endif | |
39 | ||
40 | #include "tiffiop.h" | |
8414a40c VZ |
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 | |
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* | |
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 | |
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 | |
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 | |
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 | |
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 | |
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); | |
80ed523f | 261 | // FIXME unchecked malloc |
8414a40c VZ |
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 | |
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; | |
80ed523f VZ |
286 | int result; |
287 | size_t pixel_count; | |
8414a40c VZ |
288 | |
289 | TIFFGetField(in, TIFFTAG_IMAGEWIDTH, &width); | |
290 | TIFFGetField(in, TIFFTAG_IMAGELENGTH, &height); | |
80ed523f VZ |
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 | ||
8414a40c VZ |
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 | ||
80ed523f VZ |
348 | result = cvtRaster(out, raster, width, height); |
349 | _TIFFfree(raster); | |
350 | return result; | |
8414a40c VZ |
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 | |
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: */ | |
80ed523f VZ |
383 | /* |
384 | * Local Variables: | |
385 | * mode: c | |
386 | * c-basic-offset: 8 | |
387 | * fill-column: 78 | |
388 | * End: | |
389 | */ |