1 /* This file is an image processing operation for GEGL 2 * 3 * GEGL is free software; you can redistribute it and/or 4 * modify it under the terms of the GNU Lesser General Public 5 * License as published by the Free Software Foundation; either 6 * version 3 of the License, or (at your option) any later version. 7 * 8 * GEGL is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 * Lesser General Public License for more details. 12 * 13 * You should have received a copy of the GNU Lesser General Public 14 * License along with GEGL; if not, see <https://www.gnu.org/licenses/>. 15 * 16 * Copyright 2015 Martin Blanchard <tchaik@gmx.com> 17 */ 18 19 #include "config.h" 20 #ifdef HAVE_STRPTIME 21 #define _XOPEN_SOURCE 22 #include <time.h> 23 #endif 24 #include <glib/gi18n-lib.h> 25 #include <gegl-metadata.h> 26 27 #ifdef GEGL_PROPERTIES 28 29 property_file_path (path, _("File"), "") 30 description (_("Path of file to load")) 31 property_uri (uri, _("URI"), "") 32 description (_("URI for file to load")) 33 34 property_int(directory, _("Directory"), 1) 35 description (_("Image file directory (subfile)")) 36 value_range (1, G_MAXINT) 37 ui_range (1, 16) 38 39 property_object(metadata, _("Metadata"), GEGL_TYPE_METADATA) 40 description (_("Object to receive image metadata")) 41 42 #else 43 44 #define GEGL_OP_SOURCE 45 #define GEGL_OP_NAME tiff_load 46 #define GEGL_OP_C_SOURCE tiff-load.c 47 48 49 #include <gegl-op.h> 50 #include <gegl-gio-private.h> 51 #include <glib/gprintf.h> 52 #include <tiffio.h> 53 54 typedef enum { 55 TIFF_LOADING_RGBA, 56 TIFF_LOADING_CONTIGUOUS, 57 TIFF_LOADING_SEPARATED 58 } LoadingMode; 59 60 typedef struct 61 { 62 GFile *file; 63 GInputStream *stream; 64 gboolean can_seek; 65 66 gchar *buffer; 67 gsize allocated; 68 gsize position; 69 gsize loaded; 70 71 TIFF *tiff; 72 73 gint directory; 74 75 const Babl *format; 76 LoadingMode mode; 77 78 gint width; 79 gint height; 80 } Priv; 81 82 #ifdef HAVE_STRPTIME 83 /* Parse the TIFF timestamp format - requires strptime() */ 84 static void 85 tiff_parse_timestamp (const GValue *src_value, GValue *dest_value) 86 { 87 GDateTime *datetime; 88 struct tm tm; 89 GTimeZone *tz; 90 const gchar *datestr; 91 gchar *ret; 92 93 g_return_if_fail (G_VALUE_HOLDS_STRING (src_value)); 94 g_return_if_fail (G_TYPE_CHECK_VALUE_TYPE (dest_value, G_TYPE_DATE_TIME)); 95 96 datestr = g_value_get_string (src_value); 97 g_return_if_fail (datestr != NULL); 98 99 ret = strptime (datestr, "%Y:%m:%d %T", &tm); 100 g_return_if_fail (ret != NULL); 101 102 tz = g_time_zone_new_local (); 103 datetime = g_date_time_new (tz, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 104 tm.tm_hour, tm.tm_min, tm.tm_sec); 105 g_time_zone_unref (tz); 106 107 g_return_if_fail (datetime != NULL); 108 g_value_take_boxed (dest_value, datetime); 109 } 110 #endif 111 112 static const GeglMetadataMap tiff_load_metadata[] = 113 { 114 { "Artist", "artist", NULL }, 115 { "Copyright", "copyright", NULL }, 116 #ifdef HAVE_STRPTIME 117 { "DateTime", "timestamp", tiff_parse_timestamp }, 118 #endif 119 { "ImageDescription", "description", NULL }, 120 { "PageName", "title", NULL }, 121 { "Software", "software", NULL }, 122 }; 123 124 static void 125 cleanup(GeglOperation *operation) 126 { 127 GeglProperties *o = GEGL_PROPERTIES(operation); 128 Priv *p = (Priv*) o->user_data; 129 130 if (p != NULL) 131 { 132 if (p->tiff != NULL) 133 TIFFClose(p->tiff); 134 else if (p->stream != NULL) 135 g_input_stream_close(G_INPUT_STREAM(p->stream), NULL, NULL); 136 137 g_clear_object (&p->stream); 138 p->tiff = NULL; 139 140 g_clear_object (&p->file); 141 142 p->width = p->height = 0; 143 p->directory = 0; 144 } 145 } 146 147 static GSeekType 148 lseek_to_seek_type(int whence) 149 { 150 switch (whence) 151 { 152 default: 153 case SEEK_SET: 154 return G_SEEK_SET; 155 156 case SEEK_CUR: 157 return G_SEEK_CUR; 158 159 case SEEK_END: 160 return G_SEEK_END; 161 } 162 } 163 164 static void 165 error_handler(const char *module, 166 const char *format, 167 va_list arguments) 168 { 169 gchar *message; 170 171 g_vasprintf(&message, format, arguments); 172 g_warning("%s", message); 173 174 g_free(message); 175 } 176 177 static void 178 warning_handler(const char *module, 179 const char *format, 180 va_list arguments) 181 { 182 gchar *message; 183 184 g_vasprintf(&message, format, arguments); 185 g_message("%s", message); 186 187 g_free(message); 188 } 189 190 static tsize_t 191 read_from_stream(thandle_t handle, 192 tdata_t buffer, 193 tsize_t size) 194 { 195 Priv *p = (Priv*) handle; 196 GError *error = NULL; 197 gchar *new_buffer; 198 gsize new_size = 1; 199 gsize missing, needed; 200 gssize read = -1; 201 202 g_assert(p->stream); 203 204 if (p->can_seek) 205 { 206 read = g_input_stream_read(G_INPUT_STREAM(p->stream), 207 (void *) buffer, (gsize) size, 208 NULL, &error); 209 if (read < 0 && error) 210 { 211 g_warning("%s", error->message); 212 g_error_free(error); 213 } 214 } 215 else 216 { 217 if (p->position + size > p->loaded) 218 { 219 missing = p->position + size - p->loaded; 220 if (p->loaded + missing > p->allocated) 221 { 222 needed = p->loaded + missing - p->allocated; 223 while (new_size < p->allocated + needed) 224 new_size *= 2; 225 226 new_buffer = g_try_realloc(p->buffer, new_size); 227 if (!new_buffer) 228 return -1; 229 230 p->allocated = new_size; 231 p->buffer = new_buffer; 232 } 233 234 while (missing > 0) 235 { 236 read = g_input_stream_read(G_INPUT_STREAM(p->stream), 237 (void *) (p->buffer + p->loaded), 238 missing, 239 NULL, &error); 240 if (read < 0) 241 { 242 if (error) 243 { 244 g_warning("%s", error->message); 245 g_error_free(error); 246 } 247 break; 248 } 249 250 p->loaded += read; 251 missing -= read; 252 } 253 } 254 255 g_assert(p->position + size <= p->loaded); 256 257 memcpy(buffer, p->buffer + p->position, size); 258 p->position += size; 259 read = size; 260 } 261 262 return (tsize_t) read; 263 } 264 265 static tsize_t 266 write_to_stream(thandle_t handle, 267 tdata_t buffer, 268 tsize_t size) 269 { 270 Priv *p = (Priv*) handle; 271 272 g_assert(p->stream && FALSE); 273 274 return -1; 275 } 276 277 static toff_t 278 seek_in_stream(thandle_t handle, 279 toff_t offset, 280 int whence) 281 { 282 Priv *p = (Priv*) handle; 283 GError *error = NULL; 284 gboolean sought = FALSE; 285 goffset position = -1; 286 287 g_assert(p->stream); 288 289 if (p->can_seek) 290 { 291 sought = g_seekable_seek(G_SEEKABLE(p->stream), 292 (goffset) offset, lseek_to_seek_type(whence), 293 NULL, &error); 294 if (sought) 295 position = g_seekable_tell(G_SEEKABLE(p->stream)); 296 else if (error) 297 { 298 g_warning("%s", error->message); 299 g_error_free(error); 300 } 301 } 302 else 303 { 304 switch (whence) 305 { 306 default: 307 case SEEK_SET: 308 if (offset <= p->loaded) 309 position = p->position = offset; 310 break; 311 312 case SEEK_CUR: 313 if (p->position + offset <= p->loaded) 314 position = p->position += offset; 315 break; 316 317 case G_SEEK_END: 318 if (p->loaded + offset <= p->loaded) 319 position = p->position = p->loaded + offset; 320 break; 321 } 322 } 323 324 return (toff_t) position; 325 } 326 327 static int 328 close_stream(thandle_t handle) 329 { 330 Priv *p = (Priv*) handle; 331 GError *error = NULL; 332 gboolean closed = FALSE; 333 334 g_assert(p->stream); 335 336 closed = g_input_stream_close(G_INPUT_STREAM(p->stream), 337 NULL, &error); 338 if (!closed && error) 339 { 340 g_warning("%s", error->message); 341 g_error_free(error); 342 } 343 344 g_clear_object(&p->stream); 345 346 p->loaded = 0; 347 p->position = 0; 348 349 g_clear_pointer(&p->buffer, g_free); 350 351 p->allocated = 0; 352 353 return (closed) ? 0 : -1; 354 } 355 356 static toff_t 357 get_file_size(thandle_t handle) 358 { 359 Priv *p = (Priv*) handle; 360 GError *error = NULL; 361 GFileInfo *info; 362 goffset size; 363 364 g_assert(p->stream); 365 366 size = p->loaded; 367 368 if (p->file != NULL) 369 { 370 info = g_file_query_info(p->file, 371 G_FILE_ATTRIBUTE_STANDARD_SIZE, 372 G_FILE_QUERY_INFO_NONE, 373 NULL, &error); 374 if (info == NULL) 375 { 376 if (error) 377 { 378 g_warning("%s", error->message); 379 g_error_free(error); 380 } 381 } 382 else 383 { 384 if (g_file_info_has_attribute(info, G_FILE_ATTRIBUTE_STANDARD_SIZE)) 385 size = g_file_info_get_size(info); 386 g_object_unref(info); 387 } 388 } 389 390 return (toff_t) size; 391 } 392 393 static void 394 set_meta_string (GObject *metadata, const gchar *name, const gchar *value) 395 { 396 GValue gvalue = G_VALUE_INIT; 397 GeglMetadataIter iter; 398 399 g_value_init (&gvalue, G_TYPE_STRING); 400 g_value_set_string (&gvalue, value); 401 if (gegl_metadata_iter_lookup (GEGL_METADATA (metadata), &iter, name)) 402 gegl_metadata_iter_set_value (GEGL_METADATA (metadata), &iter, &gvalue); 403 g_value_unset (&gvalue); 404 } 405 406 static gint 407 query_tiff(GeglOperation *operation) 408 { 409 GeglProperties *o = GEGL_PROPERTIES(operation); 410 Priv *p = (Priv*) o->user_data; 411 gushort color_space, compression; 412 gushort bits_per_sample, samples_per_pixel; 413 gushort sample_format; 414 gboolean has_alpha = FALSE; 415 gboolean alpha_is_premultiplied = FALSE; 416 gushort *extra_types = NULL; 417 gushort nb_extras, planar_config; 418 gboolean fallback_mode = FALSE; 419 gchar format_string[32]; 420 const Babl *space = NULL; 421 guint width, height; 422 423 g_return_val_if_fail(p->tiff != NULL, -1); 424 425 if (!TIFFGetField(p->tiff, TIFFTAG_IMAGEWIDTH, &width)) 426 { 427 g_warning("could not get TIFF image width"); 428 return -1; 429 } 430 else if (!TIFFGetField(p->tiff, TIFFTAG_IMAGELENGTH, &height)) 431 { 432 g_warning("could not get TIFF image height"); 433 return -1; 434 } 435 436 TIFFGetFieldDefaulted(p->tiff, TIFFTAG_COMPRESSION, &compression); 437 if (!TIFFGetField(p->tiff, TIFFTAG_PHOTOMETRIC, &color_space)) 438 { 439 g_warning("could not get photometric from TIFF image"); 440 if (compression == COMPRESSION_CCITTFAX3 || 441 compression == COMPRESSION_CCITTFAX4 || 442 compression == COMPRESSION_CCITTRLE || 443 compression == COMPRESSION_CCITTRLEW) 444 { 445 g_message("assuming min-is-white (CCITT compressed)"); 446 color_space = PHOTOMETRIC_MINISWHITE; 447 } 448 else 449 { 450 g_message("assuming min-is-black"); 451 color_space = PHOTOMETRIC_MINISBLACK; 452 } 453 } 454 455 TIFFGetFieldDefaulted(p->tiff, TIFFTAG_SAMPLESPERPIXEL, &samples_per_pixel); 456 if (!TIFFGetField(p->tiff, TIFFTAG_EXTRASAMPLES, &nb_extras, &extra_types)) 457 nb_extras = 0; 458 459 if (nb_extras > 0) 460 { 461 if (extra_types[0] == EXTRASAMPLE_ASSOCALPHA) 462 { 463 has_alpha = TRUE; 464 alpha_is_premultiplied = TRUE; 465 nb_extras--; 466 } 467 else if (extra_types[0] == EXTRASAMPLE_UNASSALPHA) 468 { 469 has_alpha = TRUE; 470 alpha_is_premultiplied = FALSE; 471 nb_extras--; 472 } 473 else if (extra_types[0] == EXTRASAMPLE_UNSPECIFIED) 474 { 475 has_alpha = TRUE; 476 alpha_is_premultiplied = FALSE; 477 nb_extras--; 478 } 479 } 480 481 switch(color_space) 482 { 483 case PHOTOMETRIC_MINISBLACK: 484 case PHOTOMETRIC_MINISWHITE: 485 if (samples_per_pixel > 1 + nb_extras) 486 { 487 nb_extras = samples_per_pixel - 2; 488 has_alpha = TRUE; 489 } 490 491 if (has_alpha) 492 { 493 if(alpha_is_premultiplied) 494 g_strlcpy(format_string, "Y'aA ", 32); 495 else 496 g_strlcpy(format_string, "Y'A ", 32); 497 } 498 else 499 g_strlcpy(format_string, "Y' ", 32); 500 break; 501 502 case PHOTOMETRIC_RGB: 503 if (samples_per_pixel > 3 + nb_extras) 504 { 505 nb_extras = samples_per_pixel - 4; 506 has_alpha = TRUE; 507 } 508 509 if (has_alpha) 510 { 511 if (alpha_is_premultiplied) 512 g_strlcpy(format_string, "R'aG'aB'aA ", 32); 513 else 514 g_strlcpy(format_string, "R'G'B'A ", 32); 515 } 516 else 517 g_strlcpy(format_string, "R'G'B' ", 32); 518 break; 519 case PHOTOMETRIC_SEPARATED: 520 if (samples_per_pixel > 4 + nb_extras) 521 { 522 nb_extras = samples_per_pixel - 5; 523 has_alpha = TRUE; 524 } 525 #if 0 526 if (has_alpha) 527 { 528 if (alpha_is_premultiplied) 529 g_strlcpy(format_string, "camayakaA ", 32); 530 else 531 g_strlcpy(format_string, "cmykA ", 32); 532 } 533 else 534 g_strlcpy(format_string, "cmyk ", 32); 535 #else 536 if (has_alpha) 537 { 538 if (alpha_is_premultiplied) 539 g_strlcpy(format_string, "CaMaYaKaA ", 32); 540 else 541 g_strlcpy(format_string, "CMYKA ", 32); 542 } 543 else 544 g_strlcpy(format_string, "CMYK ", 32); 545 #endif 546 547 break; 548 default: 549 fallback_mode = TRUE; 550 break; 551 } 552 553 TIFFGetFieldDefaulted(p->tiff, TIFFTAG_SAMPLEFORMAT, &sample_format); 554 TIFFGetFieldDefaulted(p->tiff, TIFFTAG_BITSPERSAMPLE, &bits_per_sample); 555 556 switch(bits_per_sample) 557 { 558 case 8: 559 g_strlcat(format_string, "u8", 32); 560 break; 561 562 case 16: 563 if (sample_format == SAMPLEFORMAT_IEEEFP) 564 g_strlcat(format_string, "half", 32); 565 else 566 g_strlcat(format_string, "u16", 32); 567 break; 568 569 case 32: 570 if (sample_format == SAMPLEFORMAT_IEEEFP) 571 g_strlcat(format_string, "float", 32); 572 else 573 g_strlcat(format_string, "u32", 32); 574 break; 575 576 case 64: 577 g_strlcat(format_string, "double", 32); 578 break; 579 580 default: 581 fallback_mode = TRUE; 582 break; 583 } 584 585 if (fallback_mode == TRUE) 586 g_strlcpy(format_string, "R'aG'aB'aA u8", 32); 587 588 TIFFGetFieldDefaulted(p->tiff, TIFFTAG_PLANARCONFIG, &planar_config); 589 590 { 591 uint32 profile_size; 592 guchar *icc_profile; 593 594 /* set the ICC profile - if found in the TIFF */ 595 if (TIFFGetField (p->tiff, TIFFTAG_ICCPROFILE, &profile_size, &icc_profile)) 596 { 597 const char *error = NULL; 598 space = babl_space_from_icc ((char*)icc_profile, (gint)profile_size, 599 BABL_ICC_INTENT_RELATIVE_COLORIMETRIC, &error); 600 if (error) 601 g_warning ("error creating space from icc: %s\n", error); 602 } 603 604 } 605 606 p->format = babl_format_with_space (format_string, space); 607 if (fallback_mode) 608 p->mode = TIFF_LOADING_RGBA; 609 else if (planar_config == PLANARCONFIG_CONTIG) 610 p->mode = TIFF_LOADING_CONTIGUOUS; 611 else 612 p->mode = TIFF_LOADING_SEPARATED; 613 614 p->height = (gint) height; 615 p->width = (gint) width; 616 617 if (o->metadata != NULL) 618 { 619 gfloat resx = 300.0f, resy = 300.0f; 620 gboolean have_x, have_y; 621 guint16 unit; 622 gchar *str; 623 GeglResolutionUnit resunit; 624 625 gegl_metadata_register_map (GEGL_METADATA (o->metadata), 626 "gegl:tiff-load", 627 GEGL_MAP_EXCLUDE_UNMAPPED, 628 tiff_load_metadata, 629 G_N_ELEMENTS (tiff_load_metadata)); 630 631 TIFFGetFieldDefaulted (p->tiff, TIFFTAG_RESOLUTIONUNIT, &unit); 632 have_x = TIFFGetField (p->tiff, TIFFTAG_XRESOLUTION, &resx); 633 have_y = TIFFGetField (p->tiff, TIFFTAG_YRESOLUTION, &resy); 634 if (!have_x && have_y) 635 resx = resy; 636 else if (have_x && !have_y) 637 resy = resx; 638 639 switch (unit) 640 { 641 case RESUNIT_INCH: 642 resunit = GEGL_RESOLUTION_UNIT_DPI; 643 break; 644 case RESUNIT_CENTIMETER: 645 resunit = GEGL_RESOLUTION_UNIT_DPM; 646 resx *= 100.0f; 647 resy *= 100.0f; 648 break; 649 default: 650 resunit = GEGL_RESOLUTION_UNIT_NONE; 651 break; 652 } 653 gegl_metadata_set_resolution (GEGL_METADATA (o->metadata), resunit, resx, resy); 654 655 //XXX make and model for scanner 656 657 if (TIFFGetField (p->tiff, TIFFTAG_ARTIST, &str)) 658 set_meta_string (o->metadata, "Artist", str); 659 if (TIFFGetField (p->tiff, TIFFTAG_COPYRIGHT, &str)) 660 set_meta_string (o->metadata, "Copyright", str); 661 if (TIFFGetField (p->tiff, TIFFTAG_PAGENAME, &str)) 662 set_meta_string (o->metadata, "PageName", str); 663 if (TIFFGetField (p->tiff, TIFFTAG_SOFTWARE, &str)) 664 set_meta_string (o->metadata, "Software", str); 665 if (TIFFGetField (p->tiff, TIFFTAG_IMAGEDESCRIPTION, &str)) 666 set_meta_string (o->metadata, "ImageDescription", str); 667 if (TIFFGetField (p->tiff, TIFFTAG_DATETIME, &str)) 668 set_meta_string (o->metadata, "DateTime", str); 669 670 gegl_metadata_unregister_map (GEGL_METADATA (o->metadata)); 671 } 672 673 return 0; 674 } 675 676 static gint 677 load_RGBA(GeglOperation *operation, 678 GeglBuffer *output) 679 { 680 GeglProperties *o = GEGL_PROPERTIES(operation); 681 Priv *p = (Priv*) o->user_data; 682 guint32 *buffer; 683 gint row; 684 685 g_return_val_if_fail(p->tiff != NULL, -1); 686 687 buffer = g_try_new(guint32, p->width * p->height * sizeof(guint32)); 688 689 g_assert(buffer != NULL); 690 691 if (!TIFFReadRGBAImage(p->tiff, p->width, p->height, buffer, 0)) 692 { 693 g_message("unsupported layout, RGBA loader failed"); 694 g_free(buffer); 695 return -1; 696 } 697 698 for (row = 0; row < p->height; row++) 699 { 700 GeglRectangle line = { 0, p->height - row - 1, p->width, 1 }; 701 #if G_BYTE_ORDER != G_LITTLE_ENDIAN 702 guint row_start = row * p->width; 703 guint row_end = row * p->width + p->width; 704 guint i; 705 706 for (i = row_start; i < row_end; i++) 707 buffer[i] = GUINT32_TO_LE(buffer[i]); 708 #endif 709 710 gegl_buffer_set(output, &line, 0, p->format, 711 ((guchar *) buffer) + (row * p->width * 4), 712 GEGL_AUTO_ROWSTRIDE); 713 } 714 715 g_free(buffer); 716 return 0; 717 } 718 719 static gint 720 load_contiguous(GeglOperation *operation, 721 GeglBuffer *output) 722 { 723 GeglProperties *o = GEGL_PROPERTIES(operation); 724 Priv *p = (Priv*) o->user_data; 725 guint32 tile_width = (guint32) p->width; 726 guint32 tile_height = 1; 727 guchar *buffer; 728 gint x, y; 729 730 g_return_val_if_fail(p->tiff != NULL, -1); 731 732 if (!TIFFIsTiled(p->tiff)) 733 buffer = g_try_new(guchar, TIFFScanlineSize(p->tiff)); 734 else 735 { 736 TIFFGetField(p->tiff, TIFFTAG_TILEWIDTH, &tile_width); 737 TIFFGetField(p->tiff, TIFFTAG_TILELENGTH, &tile_height); 738 739 buffer = g_try_new(guchar, TIFFTileSize(p->tiff)); 740 } 741 742 g_assert(buffer != NULL); 743 744 for (y = 0; y < p->height; y += tile_height) 745 { 746 for (x = 0; x < p->width; x += tile_width) 747 { 748 GeglRectangle tile = { x, y, tile_width, tile_height }; 749 750 if (TIFFIsTiled(p->tiff)) 751 TIFFReadTile(p->tiff, buffer, x, y, 0, 0); 752 else 753 TIFFReadScanline(p->tiff, buffer, y, 0); 754 755 gegl_buffer_set(output, &tile, 0, p->format, 756 (guchar *) buffer, 757 GEGL_AUTO_ROWSTRIDE); 758 } 759 } 760 761 g_free(buffer); 762 return 0; 763 } 764 765 static gint 766 load_separated(GeglOperation *operation, 767 GeglBuffer *output) 768 { 769 GeglProperties *o = GEGL_PROPERTIES(operation); 770 Priv *p = (Priv*) o->user_data; 771 guint32 tile_width = (guint32) p->width; 772 guint32 tile_height = 1; 773 gint output_bytes_per_pixel; 774 gint nb_components, offset = 0; 775 guchar *buffer; 776 gint i; 777 778 g_return_val_if_fail(p->tiff != NULL, -1); 779 780 if (!TIFFIsTiled(p->tiff)) 781 buffer = g_try_new(guchar, TIFFScanlineSize(p->tiff)); 782 else 783 { 784 TIFFGetField(p->tiff, TIFFTAG_TILEWIDTH, &tile_width); 785 TIFFGetField(p->tiff, TIFFTAG_TILELENGTH, &tile_height); 786 787 buffer = g_try_new(guchar, TIFFTileSize(p->tiff)); 788 } 789 790 g_assert(buffer != NULL); 791 792 nb_components = babl_format_get_n_components(p->format); 793 output_bytes_per_pixel = babl_format_get_bytes_per_pixel(p->format); 794 795 for (i = 0; i < nb_components; i++) 796 { 797 const Babl *plane_format; 798 const Babl *component_type; 799 gint plane_bytes_per_pixel; 800 gint x, y; 801 802 component_type = babl_format_get_type(p->format, i); 803 804 plane_format = babl_format_n(component_type, 1); 805 806 plane_bytes_per_pixel = babl_format_get_bytes_per_pixel(plane_format); 807 808 for (y = 0; y < p->height; y += tile_height) 809 { 810 for (x = 0; x < p->width; x += tile_width) 811 { 812 GeglRectangle output_tile = { x, y, tile_width, tile_height }; 813 GeglRectangle plane_tile = { 0, 0, tile_width, tile_height }; 814 GeglBufferIterator *iterator; 815 GeglBuffer *linear; 816 817 if (TIFFIsTiled(p->tiff)) 818 TIFFReadTile(p->tiff, buffer, x, y, 0, i); 819 else 820 TIFFReadScanline(p->tiff, buffer, y, i); 821 822 linear = gegl_buffer_linear_new_from_data(buffer, plane_format, 823 &plane_tile, 824 GEGL_AUTO_ROWSTRIDE, 825 NULL, NULL); 826 827 iterator = gegl_buffer_iterator_new(linear, &plane_tile, 828 0, NULL, 829 GEGL_ACCESS_READ, 830 GEGL_ABYSS_NONE, 2); 831 832 gegl_buffer_iterator_add(iterator, output, &output_tile, 833 0, p->format, 834 GEGL_ACCESS_READWRITE, 835 GEGL_ABYSS_NONE); 836 837 while (gegl_buffer_iterator_next(iterator)) 838 { 839 guchar *plane_buffer = iterator->items[0].data; 840 guchar *output_buffer = iterator->items[1].data; 841 gint nb_pixels = iterator->length; 842 843 output_buffer += offset; 844 845 while (nb_pixels--) 846 { 847 memcpy(output_buffer, plane_buffer, plane_bytes_per_pixel); 848 849 output_buffer += output_bytes_per_pixel; 850 plane_buffer += plane_bytes_per_pixel; 851 } 852 } 853 854 g_object_unref(linear); 855 } 856 } 857 858 offset += plane_bytes_per_pixel; 859 } 860 861 g_free(buffer); 862 return 0; 863 } 864 865 static void 866 prepare(GeglOperation *operation) 867 { 868 GeglProperties *o = GEGL_PROPERTIES(operation); 869 Priv *p = (o->user_data) ? o->user_data : g_new0(Priv, 1); 870 GError *error = NULL; 871 GFile *file = NULL; 872 gint directories; 873 874 g_assert(p != NULL); 875 876 if (p->file != NULL && (o->uri || o->path)) 877 { 878 if (o->uri && strlen(o->uri) > 0) 879 file = g_file_new_for_uri(o->uri); 880 else if (o->path && strlen(o->path) > 0) 881 file = g_file_new_for_path(o->path); 882 if (file != NULL) 883 { 884 if (!g_file_equal(p->file, file)) 885 cleanup(operation); 886 g_object_unref(file); 887 } 888 } 889 890 o->user_data = (void*) p; 891 892 if (p->stream == NULL) 893 { 894 p->stream = gegl_gio_open_input_stream(o->uri, o->path, &p->file, &error); 895 if (p->stream != NULL && p->file != NULL) 896 p->can_seek = g_seekable_can_seek(G_SEEKABLE(p->stream)); 897 if (p->stream == NULL) 898 { 899 if (error) 900 { 901 g_warning("%s", error->message); 902 g_error_free(error); 903 } 904 cleanup(operation); 905 return; 906 } 907 908 TIFFSetErrorHandler(error_handler); 909 TIFFSetWarningHandler(warning_handler); 910 911 p->tiff = TIFFClientOpen("GEGL-tiff-load", "r", (thandle_t) p, 912 read_from_stream, write_to_stream, 913 seek_in_stream, close_stream, 914 get_file_size, NULL, NULL); 915 if (p->tiff == NULL) 916 { 917 if (o->uri != NULL && strlen(o->uri) > 0) 918 g_warning("failed to open TIFF from %s", o->uri); 919 else 920 g_warning("failed to open TIFF from %s", o->path); 921 cleanup(operation); 922 return; 923 } 924 } 925 926 if (o->directory != p->directory) 927 { 928 directories = TIFFNumberOfDirectories(p->tiff); 929 if (o->directory > 1 && o->directory <= directories) 930 TIFFSetDirectory(p->tiff, o->directory - 1); 931 932 if (query_tiff(operation)) 933 { 934 g_warning("could not query TIFF file"); 935 cleanup(operation); 936 return; 937 } 938 939 p->directory = o->directory; 940 } 941 942 gegl_operation_set_format(operation, "output", p->format); 943 } 944 945 static GeglRectangle 946 get_bounding_box(GeglOperation *operation) 947 { 948 GeglProperties *o = GEGL_PROPERTIES(operation); 949 GeglRectangle result = { 0, 0, 0, 0 }; 950 Priv *p = (Priv*) o->user_data; 951 952 if (p->tiff != NULL) 953 { 954 result.width = p->width; 955 result.height = p->height; 956 } 957 958 return result; 959 } 960 961 static gboolean 962 process(GeglOperation *operation, 963 GeglBuffer *output, 964 const GeglRectangle *result, 965 gint level) 966 { 967 GeglProperties *o = GEGL_PROPERTIES(operation); 968 Priv *p = (Priv*) o->user_data; 969 970 if (p->tiff != NULL) 971 { 972 switch (p->mode) 973 { 974 case TIFF_LOADING_RGBA: 975 if (!load_RGBA(operation, output)) 976 return TRUE; 977 break; 978 979 case TIFF_LOADING_CONTIGUOUS: 980 if (!load_contiguous(operation, output)) 981 return TRUE; 982 break; 983 984 case TIFF_LOADING_SEPARATED: 985 if (!load_separated(operation, output)) 986 return TRUE; 987 break; 988 989 default: 990 break; 991 } 992 } 993 994 return FALSE; 995 } 996 997 static GeglRectangle 998 get_cached_region(GeglOperation *operation, 999 const GeglRectangle *roi) 1000 { 1001 return get_bounding_box(operation); 1002 } 1003 1004 static void 1005 finalize(GObject *object) 1006 { 1007 GeglProperties *o = GEGL_PROPERTIES(object); 1008 1009 if (o->user_data != NULL) 1010 { 1011 cleanup(GEGL_OPERATION(object)); 1012 g_clear_pointer(&o->user_data, g_free); 1013 } 1014 1015 G_OBJECT_CLASS(gegl_op_parent_class)->finalize(object); 1016 } 1017 1018 static void 1019 gegl_op_class_init(GeglOpClass *klass) 1020 { 1021 GeglOperationClass *operation_class; 1022 GeglOperationSourceClass *source_class; 1023 1024 G_OBJECT_CLASS(klass)->finalize = finalize; 1025 1026 operation_class = GEGL_OPERATION_CLASS(klass); 1027 source_class = GEGL_OPERATION_SOURCE_CLASS(klass); 1028 1029 source_class->process = process; 1030 operation_class->prepare = prepare; 1031 operation_class->get_bounding_box = get_bounding_box; 1032 operation_class->get_cached_region = get_cached_region; 1033 1034 gegl_operation_class_set_keys(operation_class, 1035 "name", "gegl:tiff-load", 1036 "title", _("TIFF File Loader"), 1037 "categories", "hidden", 1038 "description", _("TIFF image loader using libtiff"), 1039 NULL); 1040 1041 gegl_operation_handlers_register_loader( 1042 "image/tiff", "gegl:tiff-load"); 1043 gegl_operation_handlers_register_loader( 1044 "image/x-tiff-multipage", "gegl:tiff-load"); 1045 gegl_operation_handlers_register_loader( 1046 ".tiff", "gegl:tiff-load"); 1047 gegl_operation_handlers_register_loader( 1048 ".tif", "gegl:tiff-load"); 1049 } 1050 1051 #endif 1052