1/* $Id: tiff2rgba.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
49uint16	compression = COMPRESSION_PACKBITS;
50uint32	rowsperstrip = (uint32) -1;
51int	process_by_block = 0; /* default is whole image at once */
52int     no_alpha = 0;
53
54
55static	int tiffcvt(TIFF* in, TIFF* out);
56static	void usage(int code);
57
58int
59main(int argc, char* argv[])
60{
61    TIFF *in, *out;
62    int c;
63    extern int optind;
64    extern char *optarg;
65
66    while ((c = getopt(argc, argv, "c:r:t:bn")) != -1)
67        switch (c) {
68          case 'b':
69            process_by_block = 1;
70            break;
71
72          case 'c':
73            if (streq(optarg, "none"))
74                compression = COMPRESSION_NONE;
75            else if (streq(optarg, "packbits"))
76                compression = COMPRESSION_PACKBITS;
77            else if (streq(optarg, "lzw"))
78                compression = COMPRESSION_LZW;
79            else if (streq(optarg, "jpeg"))
80                compression = COMPRESSION_JPEG;
81            else if (streq(optarg, "zip"))
82                compression = COMPRESSION_DEFLATE;
83            else
84                usage(-1);
85            break;
86
87          case 'r':
88            rowsperstrip = atoi(optarg);
89            break;
90
91          case 't':
92            rowsperstrip = atoi(optarg);
93            break;
94
95          case 'n':
96            no_alpha = 1;
97            break;
98
99          case '?':
100            usage(0);
101            /*NOTREACHED*/
102        }
103
104    if (argc - optind < 2)
105        usage(-1);
106
107    out = TIFFOpen(argv[argc-1], "w");
108    if (out == NULL)
109        return (-2);
110
111    for (; optind < argc-1; optind++) {
112        in = TIFFOpen(argv[optind], "r");
113        if (in != NULL) {
114            do {
115                if (!tiffcvt(in, out) ||
116                    !TIFFWriteDirectory(out)) {
117                    (void) TIFFClose(out);
118                    return (1);
119                }
120            } while (TIFFReadDirectory(in));
121            (void) TIFFClose(in);
122        }
123    }
124    (void) TIFFClose(out);
125    return (0);
126}
127
128#define multiply(a,b) TIFFSafeMultiply(tsize_t,a,b)
129
130static int
131cvt_by_tile( TIFF *in, TIFF *out )
132
133{
134    uint32* raster;			/* retrieve RGBA image */
135    uint32  width, height;		/* image width & height */
136    uint32  tile_width, tile_height;
137    uint32  row, col;
138    uint32  *wrk_line;
139    tsize_t raster_size;
140    int	    ok = 1;
141
142    TIFFGetField(in, TIFFTAG_IMAGEWIDTH, &width);
143    TIFFGetField(in, TIFFTAG_IMAGELENGTH, &height);
144
145    if( !TIFFGetField(in, TIFFTAG_TILEWIDTH, &tile_width)
146        || !TIFFGetField(in, TIFFTAG_TILELENGTH, &tile_height) ) {
147        TIFFError(TIFFFileName(in), "Source image not tiled");
148        return (0);
149    }
150
151    TIFFSetField(out, TIFFTAG_TILEWIDTH, tile_width );
152    TIFFSetField(out, TIFFTAG_TILELENGTH, tile_height );
153
154    /*
155     * Allocate tile buffer
156     */
157    raster_size = multiply(multiply(tile_width, tile_height), sizeof (uint32));
158    if (!raster_size) {
159	TIFFError(TIFFFileName(in),
160		  "Can't allocate buffer for raster of size %lux%lu",
161		  (unsigned long) tile_width, (unsigned long) tile_height);
162	return (0);
163    }
164    raster = (uint32*)_TIFFmalloc(raster_size);
165    if (raster == 0) {
166        TIFFError(TIFFFileName(in), "No space for raster buffer");
167        return (0);
168    }
169
170    /*
171     * Allocate a scanline buffer for swapping during the vertical
172     * mirroring pass.  (Request can't overflow given prior checks.)
173     */
174    wrk_line = (uint32*)_TIFFmalloc(tile_width * sizeof (uint32));
175    if (!wrk_line) {
176        TIFFError(TIFFFileName(in), "No space for raster scanline buffer");
177        ok = 0;
178    }
179
180    /*
181     * Loop over the tiles.
182     */
183    for( row = 0; ok && row < height; row += tile_height )
184    {
185        for( col = 0; ok && col < width; col += tile_width )
186        {
187            uint32 i_row;
188
189            /* Read the tile into an RGBA array */
190            if (!TIFFReadRGBATile(in, col, row, raster)) {
191                ok = 0;
192                break;
193            }
194
195
196	    /*
197	     * XXX: raster array has 4-byte unsigned integer type, that is why
198	     * we should rearrange it here.
199	     */
200#if HOST_BIGENDIAN
201	    TIFFSwabArrayOfLong(raster, tile_width * tile_height);
202#endif
203
204            /*
205             * For some reason the TIFFReadRGBATile() function chooses the
206             * lower left corner as the origin.  Vertically mirror scanlines.
207             */
208            for( i_row = 0; i_row < tile_height / 2; i_row++ )
209            {
210                uint32	*top_line, *bottom_line;
211
212                top_line = raster + tile_width * i_row;
213                bottom_line = raster + tile_width * (tile_height-i_row-1);
214
215                _TIFFmemcpy(wrk_line, top_line, 4*tile_width);
216                _TIFFmemcpy(top_line, bottom_line, 4*tile_width);
217                _TIFFmemcpy(bottom_line, wrk_line, 4*tile_width);
218            }
219
220            /*
221             * Write out the result in a tile.
222             */
223
224            if( TIFFWriteEncodedTile( out,
225                                      TIFFComputeTile( out, col, row, 0, 0),
226                                      raster,
227                                      4 * tile_width * tile_height ) == -1 )
228            {
229                ok = 0;
230                break;
231            }
232        }
233    }
234
235    _TIFFfree( raster );
236    _TIFFfree( wrk_line );
237
238    return ok;
239}
240
241static int
242cvt_by_strip( TIFF *in, TIFF *out )
243
244{
245    uint32* raster;			/* retrieve RGBA image */
246    uint32  width, height;		/* image width & height */
247    uint32  row;
248    uint32  *wrk_line;
249    tsize_t raster_size;
250    int	    ok = 1;
251
252    TIFFGetField(in, TIFFTAG_IMAGEWIDTH, &width);
253    TIFFGetField(in, TIFFTAG_IMAGELENGTH, &height);
254
255    if( !TIFFGetField(in, TIFFTAG_ROWSPERSTRIP, &rowsperstrip) ) {
256        TIFFError(TIFFFileName(in), "Source image not in strips");
257        return (0);
258    }
259
260    TIFFSetField(out, TIFFTAG_ROWSPERSTRIP, rowsperstrip);
261
262    /*
263     * Allocate strip buffer
264     */
265    raster_size = multiply(multiply(width, rowsperstrip), sizeof (uint32));
266    if (!raster_size) {
267	TIFFError(TIFFFileName(in),
268		  "Can't allocate buffer for raster of size %lux%lu",
269		  (unsigned long) width, (unsigned long) rowsperstrip);
270	return (0);
271    }
272    raster = (uint32*)_TIFFmalloc(raster_size);
273    if (raster == 0) {
274        TIFFError(TIFFFileName(in), "No space for raster buffer");
275        return (0);
276    }
277
278    /*
279     * Allocate a scanline buffer for swapping during the vertical
280     * mirroring pass.  (Request can't overflow given prior checks.)
281     */
282    wrk_line = (uint32*)_TIFFmalloc(width * sizeof (uint32));
283    if (!wrk_line) {
284        TIFFError(TIFFFileName(in), "No space for raster scanline buffer");
285        ok = 0;
286    }
287
288    /*
289     * Loop over the strips.
290     */
291    for( row = 0; ok && row < height; row += rowsperstrip )
292    {
293        int	rows_to_write, i_row;
294
295        /* Read the strip into an RGBA array */
296        if (!TIFFReadRGBAStrip(in, row, raster)) {
297            ok = 0;
298            break;
299        }
300
301	/*
302	 * XXX: raster array has 4-byte unsigned integer type, that is why
303	 * we should rearrange it here.
304	 */
305#if HOST_BIGENDIAN
306	TIFFSwabArrayOfLong(raster, width * rowsperstrip);
307#endif
308
309        /*
310         * Figure out the number of scanlines actually in this strip.
311         */
312        if( row + rowsperstrip > height )
313            rows_to_write = height - row;
314        else
315            rows_to_write = rowsperstrip;
316
317        /*
318         * For some reason the TIFFReadRGBAStrip() function chooses the
319         * lower left corner as the origin.  Vertically mirror scanlines.
320         */
321
322        for( i_row = 0; i_row < rows_to_write / 2; i_row++ )
323        {
324            uint32	*top_line, *bottom_line;
325
326            top_line = raster + width * i_row;
327            bottom_line = raster + width * (rows_to_write-i_row-1);
328
329            _TIFFmemcpy(wrk_line, top_line, 4*width);
330            _TIFFmemcpy(top_line, bottom_line, 4*width);
331            _TIFFmemcpy(bottom_line, wrk_line, 4*width);
332        }
333
334        /*
335         * Write out the result in a strip
336         */
337
338        if( TIFFWriteEncodedStrip( out, row / rowsperstrip, raster,
339                                   4 * rows_to_write * width ) == -1 )
340        {
341            ok = 0;
342            break;
343        }
344    }
345
346    _TIFFfree( raster );
347    _TIFFfree( wrk_line );
348
349    return ok;
350}
351
352/*
353 * cvt_whole_image()
354 *
355 * read the whole image into one big RGBA buffer and then write out
356 * strips from that.  This is using the traditional TIFFReadRGBAImage()
357 * API that we trust.
358 */
359
360static int
361cvt_whole_image( TIFF *in, TIFF *out )
362
363{
364    uint32* raster;			/* retrieve RGBA image */
365    uint32  width, height;		/* image width & height */
366    uint32  row;
367    size_t pixel_count;
368
369    TIFFGetField(in, TIFFTAG_IMAGEWIDTH, &width);
370    TIFFGetField(in, TIFFTAG_IMAGELENGTH, &height);
371    pixel_count = width * height;
372
373    /* XXX: Check the integer overflow. */
374    if (!width || !height || pixel_count / width != height) {
375        TIFFError(TIFFFileName(in),
376		  "Malformed input file; can't allocate buffer for raster of %lux%lu size",
377		  (unsigned long)width, (unsigned long)height);
378        return 0;
379    }
380
381    rowsperstrip = TIFFDefaultStripSize(out, rowsperstrip);
382    TIFFSetField(out, TIFFTAG_ROWSPERSTRIP, rowsperstrip);
383
384    raster = (uint32*)_TIFFCheckMalloc(in, pixel_count, sizeof(uint32), "raster buffer");
385    if (raster == 0) {
386        TIFFError(TIFFFileName(in), "Requested buffer size is %lu elements %lu each",
387		  (unsigned long)pixel_count, (unsigned long)sizeof(uint32));
388        return (0);
389    }
390
391    /* Read the image in one chunk into an RGBA array */
392    if (!TIFFReadRGBAImageOriented(in, width, height, raster,
393                                   ORIENTATION_TOPLEFT, 0)) {
394        _TIFFfree(raster);
395        return (0);
396    }
397
398    /*
399     * XXX: raster array has 4-byte unsigned integer type, that is why
400     * we should rearrange it here.
401     */
402#if HOST_BIGENDIAN
403    TIFFSwabArrayOfLong(raster, width * height);
404#endif
405
406    /*
407     * Do we want to strip away alpha components?
408     */
409    if (no_alpha)
410    {
411        size_t count = pixel_count;
412        unsigned char *src, *dst;
413
414	src = dst = (unsigned char *) raster;
415        while (count > 0)
416        {
417	    *(dst++) = *(src++);
418	    *(dst++) = *(src++);
419	    *(dst++) = *(src++);
420	    src++;
421	    count--;
422        }
423    }
424
425    /*
426     * Write out the result in strips
427     */
428    for (row = 0; row < height; row += rowsperstrip)
429    {
430        unsigned char * raster_strip;
431        int	rows_to_write;
432        int	bytes_per_pixel;
433
434        if (no_alpha)
435        {
436            raster_strip = ((unsigned char *) raster) + 3 * row * width;
437            bytes_per_pixel = 3;
438        }
439        else
440        {
441            raster_strip = (unsigned char *) (raster + row * width);
442            bytes_per_pixel = 4;
443        }
444
445        if( row + rowsperstrip > height )
446            rows_to_write = height - row;
447        else
448            rows_to_write = rowsperstrip;
449
450        if( TIFFWriteEncodedStrip( out, row / rowsperstrip, raster_strip,
451                             bytes_per_pixel * rows_to_write * width ) == -1 )
452        {
453            _TIFFfree( raster );
454            return 0;
455        }
456    }
457
458    _TIFFfree( raster );
459
460    return 1;
461}
462
463
464static int
465tiffcvt(TIFF* in, TIFF* out)
466{
467	uint32 width, height;		/* image width & height */
468	uint16 shortv;
469	float floatv;
470	char *stringv;
471	uint32 longv;
472        uint16 v[1];
473
474	TIFFGetField(in, TIFFTAG_IMAGEWIDTH, &width);
475	TIFFGetField(in, TIFFTAG_IMAGELENGTH, &height);
476
477	CopyField(TIFFTAG_SUBFILETYPE, longv);
478	TIFFSetField(out, TIFFTAG_IMAGEWIDTH, width);
479	TIFFSetField(out, TIFFTAG_IMAGELENGTH, height);
480	TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, 8);
481	TIFFSetField(out, TIFFTAG_COMPRESSION, compression);
482	TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
483
484	CopyField(TIFFTAG_FILLORDER, shortv);
485	TIFFSetField(out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
486
487        if( no_alpha )
488            TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, 3);
489        else
490            TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, 4);
491
492        if( !no_alpha )
493        {
494            v[0] = EXTRASAMPLE_ASSOCALPHA;
495            TIFFSetField(out, TIFFTAG_EXTRASAMPLES, 1, v);
496        }
497
498	CopyField(TIFFTAG_XRESOLUTION, floatv);
499	CopyField(TIFFTAG_YRESOLUTION, floatv);
500	CopyField(TIFFTAG_RESOLUTIONUNIT, shortv);
501	TIFFSetField(out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
502	TIFFSetField(out, TIFFTAG_SOFTWARE, TIFFGetVersion());
503	CopyField(TIFFTAG_DOCUMENTNAME, stringv);
504
505        if( process_by_block && TIFFIsTiled( in ) )
506            return( cvt_by_tile( in, out ) );
507        else if( process_by_block )
508            return( cvt_by_strip( in, out ) );
509        else
510            return( cvt_whole_image( in, out ) );
511}
512
513static char* stuff[] = {
514    "usage: tiff2rgba [-c comp] [-r rows] [-b] input... output",
515    "where comp is one of the following compression algorithms:",
516    " jpeg\t\tJPEG encoding",
517    " zip\t\tLempel-Ziv & Welch encoding",
518    " lzw\t\tLempel-Ziv & Welch encoding",
519    " packbits\tPackBits encoding",
520    " none\t\tno compression",
521    "and the other options are:",
522    " -r\trows/strip",
523    " -b (progress by block rather than as a whole image)",
524    " -n don't emit alpha component.",
525    NULL
526};
527
528static void
529usage(int code)
530{
531	char buf[BUFSIZ];
532	int i;
533
534	setbuf(stderr, buf);
535        fprintf(stderr, "%s\n\n", TIFFGetVersion());
536	for (i = 0; stuff[i] != NULL; i++)
537		fprintf(stderr, "%s\n", stuff[i]);
538	exit(code);
539}
540
541/* vim: set ts=8 sts=8 sw=8 noet: */
542/*
543 * Local Variables:
544 * mode: c
545 * c-basic-offset: 8
546 * fill-column: 78
547 * End:
548 */
549