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