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